From 3c9433e21c91042d90622c37ae45fa55ed2e563a Mon Sep 17 00:00:00 2001 From: mattf Date: Fri, 20 Jul 2007 19:14:09 +0000 Subject: Merged revisions 2762 via svnmerge from https://origsvn.digium.com/svn/zaptel/branches/1.4 ................ r2762 | mattf | 2007-07-20 13:54:20 -0500 (Fri, 20 Jul 2007) | 9 lines Merged revisions 2761 via svnmerge from https://origsvn.digium.com/svn/zaptel/branches/1.2 ........ r2761 | mattf | 2007-07-20 11:19:47 -0500 (Fri, 20 Jul 2007) | 1 line New product support, new echo canceler and new boards ........ ................ git-svn-id: http://svn.digium.com/svn/zaptel/trunk@2763 5390a7c7-147a-4af0-8ec9-7488f05a26cb --- Makefile | 9 +- wctdm24xxp.c | 3593 ------------------------------------ wctdm24xxp/GpakApi.c | 1609 ++++++++++++++++ wctdm24xxp/GpakApi.h | 619 +++++++ wctdm24xxp/GpakCust.c | 388 ++++ wctdm24xxp/GpakCust.h | 161 ++ wctdm24xxp/GpakHpi.h | 63 + wctdm24xxp/Makefile | 22 + wctdm24xxp/Makefile.kernel26 | 12 + wctdm24xxp/VPMADT032.bin | Bin 0 -> 60980 bytes wctdm24xxp/base.c | 4201 ++++++++++++++++++++++++++++++++++++++++++ wctdm24xxp/gpakErrs.h | 139 ++ wctdm24xxp/gpakenum.h | 175 ++ wctdm24xxp/wctdm24xxp.h | 237 +++ 14 files changed, 7632 insertions(+), 3596 deletions(-) delete mode 100644 wctdm24xxp.c create mode 100644 wctdm24xxp/GpakApi.c create mode 100644 wctdm24xxp/GpakApi.h create mode 100644 wctdm24xxp/GpakCust.c create mode 100644 wctdm24xxp/GpakCust.h create mode 100644 wctdm24xxp/GpakHpi.h create mode 100644 wctdm24xxp/Makefile create mode 100644 wctdm24xxp/Makefile.kernel26 create mode 100644 wctdm24xxp/VPMADT032.bin create mode 100644 wctdm24xxp/base.c create mode 100644 wctdm24xxp/gpakErrs.h create mode 100644 wctdm24xxp/gpakenum.h create mode 100644 wctdm24xxp/wctdm24xxp.h diff --git a/Makefile b/Makefile index 4f9e43a..9ed154f 100644 --- a/Makefile +++ b/Makefile @@ -125,7 +125,7 @@ ifeq ($(strip $(foreach var,clean distclean dist-clean update,$(findstring $(var endif endif -MODULES:=pciradio tor2 torisa wcfxo wct1xxp wctdm wctdm24xxp wcte11xp wcusb zaptel ztd-eth ztd-loc ztdummy ztdynamic zttranscode +MODULES:=pciradio tor2 torisa wcfxo wct1xxp wctdm wcte11xp wcusb zaptel ztd-eth ztd-loc ztdummy ztdynamic zttranscode MODULES:=$(filter-out $(MENUSELECT_MODULES),$(MODULES)) MODULE_ALIASES=wcfxs wctdm8xxp wct2xxp @@ -160,6 +160,11 @@ obj-m+=xpp/ MODULES+=xpp endif +ifeq ($(findstring wctdm24xxp,$(MENUSELECT_MODULES)),) +obj-m+=wctdm24xxp/ +MODULES+=wctdm24xxp +endif + # Set this to override hotplug firmware loading and revert to classic header #HOTPLUG_FIRMWARE=no ifeq ($(HOTPLUG_FIRMWARE),yes) @@ -220,8 +225,6 @@ wcfxsusb.o: wcfxsusb.h wctdm.o: wctdm.h -wctdm24xxp.o: wctdm.h - pciradio.o: radfw.h ztdummy.o: ztdummy.h diff --git a/wctdm24xxp.c b/wctdm24xxp.c deleted file mode 100644 index a330bb5..0000000 --- a/wctdm24xxp.c +++ /dev/null @@ -1,3593 +0,0 @@ -/* - * Wilcard TDM2400P TDM FXS/FXO Interface Driver for Zapata Telephony interface - * - * Written by Mark Spencer - * Further modified to add support for the TDM800P by Matthew Fredrickson - * - * Copyright (C) 2005,2006, Digium, Inc. - * All rights reserved. - * - * Sections for QRV cards written by Jim Dixon - * Copyright (C) 2006, Jim Dixon and QRV Communications - * 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. - * - */ -/* For QRV DRI cards, gain is signed short, expressed in hundredths of -db (in reference to 1v Peak @ 1000Hz) , as follows: - -Rx Gain: -11.99 to 15.52 db -Tx Gain - No Pre-Emphasis: -35.99 to 12.00 db -Tx Gain - W/Pre-Emphasis: -23.99 to 0.00 db -*/ - -#include -#include -#include -#include -#include -#include -#include -#include "proslic.h" -#include "wctdm.h" - -/* Comment to disable VPM support */ -#define VPM_SUPPORT - -#ifdef VPM_SUPPORT - -/* Define to get more attention-grabbing but slightly more CPU using echocan status */ -#define FANCY_ECHOCAN - -#endif - -/* - Experimental max loop current limit for the proslic - Loop current limit is from 20 mA to 41 mA in steps of 3 - (according to datasheet) - So set the value below to: - 0x00 : 20mA (default) - 0x01 : 23mA - 0x02 : 26mA - 0x03 : 29mA - 0x04 : 32mA - 0x05 : 35mA - 0x06 : 37mA - 0x07 : 41mA -*/ -static int loopcurrent = 20; - -static alpha indirect_regs[] = -{ -{0,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", 0x4C00 /* 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}, -}; - -#ifdef FANCY_ECHOCAN -static char ectab[] = { -0, 0, 0, 1, 2, 3, 4, 6, 8, 9, 11, 13, 16, 18, 20, 22, 24, 25, 27, 28, 29, 30, 31, 31, 32, -32, 32, 32, 32, 32, 32, 32, 32, 32, 32 ,32 ,32, 32, -32, 32, 32, 32, 32, 32, 32, 32, 32, 32 ,32 ,32, 32, -32, 32, 32, 32, 32, 32, 32, 32, 32, 32 ,32 ,32, 32, -31, 31, 30, 29, 28, 27, 25, 23, 22, 20, 18, 16, 13, 11, 9, 8, 6, 4, 3, 2, 1, 0, 0, -}; -static int ectrans[4] = { 0, 1, 3, 2 }; -#define EC_SIZE (sizeof(ectab)) -#define EC_SIZE_Q (sizeof(ectab) / 4) -#endif - -/* Undefine to enable Power alarm / Transistor debug -- note: do not - enable for normal operation! */ -/* #define PAQ_DEBUG */ - -static struct fxo_mode { - char *name; - /* FXO */ - int ohs; - int ohs2; - int rz; - int rt; - int ilim; - int dcv; - int mini; - int acim; - int ring_osc; - int ring_x; -} fxo_modes[] = -{ - { "FCC", 0, 0, 0, 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, }, -}; - -#ifdef STANDALONE_ZAPATA -#include "zaptel.h" -#else -#include -#endif - -#ifdef LINUX26 -#include -#endif - -#define NUM_FXO_REGS 60 - -#define WC_MAX_IFACES 128 - -#define FLAG_EMPTY 0 -#define FLAG_WRITE 1 -#define FLAG_READ 2 - -#define RING_DEBOUNCE 128 /* Ringer Debounce (in ms) */ -#define DEFAULT_BATT_DEBOUNCE 64 /* Battery debounce (in ms) */ -#define POLARITY_DEBOUNCE 64 /* Polarity debounce (in ms) */ -#define DEFAULT_BATT_THRESH 3 /* Anything under this is "no battery" */ - -#define OHT_TIMER 6000 /* How long after RING to retain OHT */ - -#define FLAG_3215 (1 << 0) - -#define NUM_CARDS 24 -#define NUM_EC 4 -#define NUM_SLOTS 6 -#define MAX_TDM_CHAN 31 - -#define EFRAME_SIZE 108 -#define ERING_SIZE 16 /* Maximum ring size */ -#define EFRAME_GAP 20 -#define SFRAME_SIZE ((EFRAME_SIZE * ZT_CHUNKSIZE) + (EFRAME_GAP * (ZT_CHUNKSIZE - 1))) - -#define MAX_ALARMS 10 - -#define MOD_TYPE_NONE 0 -#define MOD_TYPE_FXS 1 -#define MOD_TYPE_FXO 2 -#define MOD_TYPE_FXSINIT 3 -#define MOD_TYPE_VPM 4 -#define MOD_TYPE_QRV 5 - -#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 SDI_CLK (0x00010000) -#define SDI_DOUT (0x00020000) -#define SDI_DREAD (0x00040000) -#define SDI_DIN (0x00080000) - -#define NUM_CAL_REGS 12 - -#define PCI_WINDOW_SIZE ((2 * 2 * 2 * SFRAME_SIZE) + (2 * ERING_SIZE * 4)) - -#define USER_COMMANDS 8 -#define ISR_COMMANDS 2 -#define QRV_DEBOUNCETIME 20 - -#define MAX_COMMANDS (USER_COMMANDS + ISR_COMMANDS) - -#define __CMD_RD (1 << 20) /* Read Operation */ -#define __CMD_WR (1 << 21) /* Write Operation */ -#define __CMD_FIN (1 << 22) /* Has finished receive */ -#define __CMD_TX (1 << 23) /* Has been transmitted */ - -#define CMD_WR(a,b) (((a) << 8) | (b) | __CMD_WR) -#define CMD_RD(a) (((a) << 8) | __CMD_RD) - -#define CMD_BYTE(card,bit,altcs) (((((card) & 0x3) * 3 + (bit)) * 7) \ - + ((card) >> 2) + (altcs) + ((altcs) ? -21 : 0)) - -struct calregs { - unsigned char vals[NUM_CAL_REGS]; -}; - -struct cmdq { - unsigned int cmds[MAX_COMMANDS]; - unsigned char isrshadow[ISR_COMMANDS]; -}; - -struct wctdm { - struct pci_dev *dev; - char *variety; - struct zt_span span; - unsigned char ios; - unsigned int sdi; - int usecount; - unsigned int intcount; - unsigned int rxints; - unsigned int txints; - unsigned int intmask; - unsigned char txident; - unsigned char rxident; - int dead; - int pos; - int flags[NUM_CARDS]; - int freeregion; - int alt; - int rdbl; - int tdbl; - int curcard; - unsigned char ctlreg; - int cards; - int cardflag; /* Bit-map of present cards */ - int altcs[NUM_CARDS + NUM_EC]; - char qrvhook[NUM_CARDS]; - unsigned short qrvdebtime[NUM_CARDS]; - int radmode[NUM_CARDS]; -#define RADMODE_INVERTCOR 1 -#define RADMODE_IGNORECOR 2 -#define RADMODE_EXTTONE 4 -#define RADMODE_EXTINVERT 8 -#define RADMODE_IGNORECT 16 -#define RADMODE_PREEMP 32 -#define RADMODE_DEEMP 64 - unsigned short debouncetime[NUM_CARDS]; - signed short rxgain[NUM_CARDS]; - signed short txgain[NUM_CARDS]; - spinlock_t reglock; - wait_queue_head_t regq; - /* FXO Stuff */ - union { - struct { - int wasringing; - int ringdebounce; - int offhook; - int battdebounce; - int nobatttimer; - int battery; - int lastpol; - int polarity; - int polaritydebounce; - } fxo; - struct { - int oldrxhook; - int debouncehook; - int lastrxhook; - int debounce; - int ohttimer; - int idletxhookstate; /* IDLE changing hook state */ - int lasttxhook; - int palarms; - struct calregs calregs; - } fxs; - } mods[NUM_CARDS]; - struct cmdq cmdq[NUM_CARDS + NUM_EC]; - /* Receive hook state and debouncing */ - int modtype[NUM_CARDS + NUM_EC]; - /* Set hook */ - int sethook[NUM_CARDS + NUM_EC]; - int dacssrc[NUM_CARDS]; - int type; - -#ifdef VPM_SUPPORT - int vpm; - unsigned int dtmfactive; - unsigned int dtmfmask; - unsigned int dtmfmutemask; - short dtmfenergy[NUM_CARDS]; - short dtmfdigit[NUM_CARDS]; -#ifdef FANCY_ECHOCAN - int echocanpos; - int blinktimer; -#endif -#endif - unsigned long iobase; - dma_addr_t readdma; - dma_addr_t writedma; - dma_addr_t descripdma; - volatile unsigned int *writechunk; /* Double-word aligned write memory */ - volatile unsigned int *readchunk; /* Double-word aligned read memory */ - volatile unsigned int *descripchunk; /* Descriptors */ - struct zt_chan chans[NUM_CARDS]; -}; - - -struct wctdm_desc { - char *name; - int flags; - int ports; -}; - -static struct wctdm_desc wctdm2400 = { "Wildcard TDM2400P", 0, 24 }; -static struct wctdm_desc wctdm800 = { "Wildcard TDM800P", 0, 8 }; -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 int battdebounce = DEFAULT_BATT_DEBOUNCE; -static int battthresh = DEFAULT_BATT_THRESH; -static int debug = 0; -static int robust = 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 fxo_addrs[4] = { 0x00, 0x08, 0x04, 0x0c }; -static int fxotxgain = 0; -static int fxorxgain = 0; -static int fxstxgain = 0; -static int fxsrxgain = 0; -static int nativebridge = 1; -#ifdef VPM_SUPPORT -static int vpmsupport = 1; -static int vpmdtmfsupport = 0; -#define VPM_DEFAULT_DTMFTHRESHOLD 1250 -static int dtmfthreshold = VPM_DEFAULT_DTMFTHRESHOLD; -#endif - -static int wctdm_init_proslic(struct wctdm *wc, int card, int fast , int manual, int sane); - -/* sleep in user space until woken up. Equivilant of tsleep() in BSD */ -static int schluffen(wait_queue_head_t *q) -{ - DECLARE_WAITQUEUE(wait, current); - add_wait_queue(q, &wait); - current->state = TASK_INTERRUPTIBLE; - if (!signal_pending(current)) schedule(); - current->state = TASK_RUNNING; - remove_wait_queue(q, &wait); - if (signal_pending(current)) return -ERESTARTSYS; - return(0); -} - -static inline int empty_slot(struct wctdm *wc, int card) -{ - int x; - for (x=0;xcmdq[card].cmds[x]) - return x; - } - return -1; -} - -static inline void cmd_dequeue(struct wctdm *wc, volatile unsigned char *writechunk, int card, int pos) -{ - unsigned long flags; - unsigned int curcmd=0; - int x; - int subaddr = card & 0x3; -#ifdef FANCY_ECHOCAN - int ecval; - ecval = wc->echocanpos; - ecval += EC_SIZE_Q * ectrans[(card & 0x3)]; - ecval = ecval % EC_SIZE; -#endif - - /* if a QRV card, map it to its first channel */ - if ((wc->modtype[card] == MOD_TYPE_QRV) && (card & 3)) - { - return; - } - if (wc->altcs[card]) - subaddr = 0; - - /* Skip audio */ - writechunk += 24; - spin_lock_irqsave(&wc->reglock, flags); - /* Search for something waiting to transmit */ - if (pos) { - for (x=0;xcmdq[card].cmds[x] & (__CMD_RD | __CMD_WR)) && - !(wc->cmdq[card].cmds[x] & (__CMD_TX | __CMD_FIN))) { - curcmd = wc->cmdq[card].cmds[x]; -#if 0 - printk("Transmitting command '%08x' in slot %d\n", wc->cmdq[card].cmds[x], wc->txident); -#endif - wc->cmdq[card].cmds[x] |= (wc->txident << 24) | __CMD_TX; - break; - } - } - } - if (!curcmd) { - /* If nothing else, use filler */ - if (wc->modtype[card] == MOD_TYPE_FXS) - curcmd = CMD_RD(64); - else if (wc->modtype[card] == MOD_TYPE_FXO) - curcmd = CMD_RD(12); - else if (wc->modtype[card] == MOD_TYPE_QRV) - curcmd = CMD_RD(3); - else if (wc->modtype[card] == MOD_TYPE_VPM) { -#ifdef FANCY_ECHOCAN - if (wc->blinktimer >= 0xf) { - curcmd = CMD_WR(0x1ab, 0x0f); - } else if (wc->blinktimer == (ectab[ecval] >> 1)) { - curcmd = CMD_WR(0x1ab, 0x00); - } else -#endif - curcmd = CMD_RD(0x1a0); - } - } - if (wc->modtype[card] == MOD_TYPE_FXS) { - writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = (1 << (subaddr)); - if (curcmd & __CMD_WR) - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0x7f; - else - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x80 | ((curcmd >> 8) & 0x7f); - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; - } else if (wc->modtype[card] == MOD_TYPE_FXO) { - if (curcmd & __CMD_WR) - writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x20 | fxo_addrs[subaddr]; - else - writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x60 | fxo_addrs[subaddr]; - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0xff; - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; - } else if (wc->modtype[card] == MOD_TYPE_FXSINIT) { - /* Special case, we initialize the FXS's into the three-byte command mode then - switch to the regular mode. To send it into thee byte mode, treat the path as - 6 two-byte commands and in the last one we initialize register 0 to 0x80. All modules - read this as the command to switch to daisy chain mode and we're done. */ - writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00; - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00; - if ((card & 0x1) == 0x1) - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x80; - else - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00; -#ifdef VPM_SUPPORT - } else if (wc->modtype[card] == MOD_TYPE_VPM) { - if (curcmd & __CMD_WR) - writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = ((card & 0x3) << 4) | 0xc | ((curcmd >> 16) & 0x1); - else - writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = ((card & 0x3) << 4) | 0xa | ((curcmd >> 16) & 0x1); - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0xff; - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; -#endif - } else if (wc->modtype[card] == MOD_TYPE_QRV) { - - writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00; - if (!curcmd) - { - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00; - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00; - } - else - { - if (curcmd & __CMD_WR) - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x40 | ((curcmd >> 8) & 0x3f); - else - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0xc0 | ((curcmd >> 8) & 0x3f); - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; - } - } else if (wc->modtype[card] == MOD_TYPE_NONE) { - writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00; - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00; - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00; - } -#if 0 - /* XXX */ - if (cmddesc < 40) - printk("Pass %d, card = %d (modtype=%d), pos = %d, CMD_BYTES = %d,%d,%d, (%02x,%02x,%02x) curcmd = %08x\n", cmddesc, card, wc->modtype[card], pos, CMD_BYTE(card, 0), CMD_BYTE(card, 1), CMD_BYTE(card, 2), writechunk[CMD_BYTE(card, 0)], writechunk[CMD_BYTE(card, 1)], writechunk[CMD_BYTE(card, 2)], curcmd); -#endif - spin_unlock_irqrestore(&wc->reglock, flags); -#if 0 - /* XXX */ - cmddesc++; -#endif -} - -static inline void cmd_decifer(struct wctdm *wc, volatile unsigned char *readchunk, int card) -{ - unsigned long flags; - unsigned char ident; - int x; - - /* if a QRV card, map it to its first channel */ - if ((wc->modtype[card] == MOD_TYPE_QRV) && (card & 3)) - { - return; - } - /* Skip audio */ - readchunk += 24; - spin_lock_irqsave(&wc->reglock, flags); - /* Search for any pending results */ - for (x=0;xcmdq[card].cmds[x] & (__CMD_RD | __CMD_WR)) && - (wc->cmdq[card].cmds[x] & (__CMD_TX)) && - !(wc->cmdq[card].cmds[x] & (__CMD_FIN))) { - ident = (wc->cmdq[card].cmds[x] >> 24) & 0xff; - if (ident == wc->rxident) { - /* Store result */ - wc->cmdq[card].cmds[x] |= readchunk[CMD_BYTE(card, 2, wc->altcs[card])]; - wc->cmdq[card].cmds[x] |= __CMD_FIN; - if (wc->cmdq[card].cmds[x] & __CMD_WR) { - /* Go ahead and clear out writes since they need no acknowledgement */ - wc->cmdq[card].cmds[x] = 0x00000000; - } else if (x >= USER_COMMANDS) { - /* Clear out ISR reads */ - wc->cmdq[card].isrshadow[x - USER_COMMANDS] = wc->cmdq[card].cmds[x] & 0xff; - wc->cmdq[card].cmds[x] = 0x00000000; - } - break; - } - } - } -#if 0 - /* XXX */ - if (!pos && (cmddesc < 256)) - printk("Card %d: Command '%08x' => %02x\n",card, wc->cmdq[card].lasttx[pos], wc->cmdq[card].lastrd[pos]); -#endif - spin_unlock_irqrestore(&wc->reglock, flags); -} - -static inline void cmd_checkisr(struct wctdm *wc, int card) -{ - if (!wc->cmdq[card].cmds[USER_COMMANDS + 0]) { - if (wc->sethook[card]) { - wc->cmdq[card].cmds[USER_COMMANDS + 0] = wc->sethook[card]; - wc->sethook[card] = 0; - } else if (wc->modtype[card] == MOD_TYPE_FXS) { - wc->cmdq[card].cmds[USER_COMMANDS + 0] = CMD_RD(68); /* Hook state */ - } else if (wc->modtype[card] == MOD_TYPE_FXO) { - wc->cmdq[card].cmds[USER_COMMANDS + 0] = CMD_RD(5); /* Hook/Ring state */ - } else if (wc->modtype[card] == MOD_TYPE_QRV) { - wc->cmdq[card & 0xfc].cmds[USER_COMMANDS + 0] = CMD_RD(3); /* COR/CTCSS state */ -#ifdef VPM_SUPPORT - } else if (wc->modtype[card] == MOD_TYPE_VPM) { - wc->cmdq[card].cmds[USER_COMMANDS + 0] = CMD_RD(0xb9); /* DTMF interrupt */ -#endif - } - } - if (!wc->cmdq[card].cmds[USER_COMMANDS + 1]) { - if (wc->modtype[card] == MOD_TYPE_FXS) { -#ifdef PAQ_DEBUG - wc->cmdq[card].cmds[USER_COMMANDS + 1] = CMD_RD(19); /* Transistor interrupts */ -#else - wc->cmdq[card].cmds[USER_COMMANDS + 1] = CMD_RD(64); /* Battery mode */ -#endif - } else if (wc->modtype[card] == MOD_TYPE_FXO) { - wc->cmdq[card].cmds[USER_COMMANDS + 1] = CMD_RD(29); /* Battery */ - } else if (wc->modtype[card] == MOD_TYPE_QRV) { - wc->cmdq[card & 0xfc].cmds[USER_COMMANDS + 1] = CMD_RD(3); /* Battery */ -#ifdef VPM_SUPPORT - } else if (wc->modtype[card] == MOD_TYPE_VPM) { - wc->cmdq[card].cmds[USER_COMMANDS + 1] = CMD_RD(0xbd); /* DTMF interrupt */ -#endif - } - } -} - -static inline void wctdm_transmitprep(struct wctdm *wc, int dbl) -{ - volatile unsigned char *writechunk; - int x,y; - - dbl = dbl % 2; - - writechunk = (volatile unsigned char *)(wc->writechunk); - if (dbl) - /* Write is at interrupt address. Start writing from normal offset */ - writechunk += SFRAME_SIZE; - - /* Calculate Transmission */ - zt_transmit(&wc->span); - - for (x=0;xcards;y++) { - if (!x) - cmd_checkisr(wc, y); - - if (y < wc->type) - writechunk[y] = wc->chans[y].writechunk[x]; - cmd_dequeue(wc, writechunk, y, x); - } -#ifdef VPM_SUPPORT - if (wc->vpm) { - if (!x) - wc->blinktimer++; - for (y=24;y<28;y++) { - if (!x) - cmd_checkisr(wc, y); - cmd_dequeue(wc, writechunk, y, x); - } -#ifdef FANCY_ECHOCAN - if (wc->blinktimer >= 0xf) { - wc->blinktimer = -1; - wc->echocanpos++; - } -#endif - } -#endif -#if 0 - if (cmddesc < 1024) - printk("TC Result: %02x\n", wc->txident); -#endif - if (x < ZT_CHUNKSIZE - 1) { - writechunk[EFRAME_SIZE] = wc->ctlreg; - writechunk[EFRAME_SIZE + 1] = wc->txident++; - } - writechunk += (EFRAME_SIZE + EFRAME_GAP); - } -} - -static inline void __wctdm_setctl(struct wctdm *wc, unsigned int addr, unsigned int val) -{ - outl(val, wc->iobase + addr); -} - -static inline unsigned int __wctdm_getctl(struct wctdm *wc, unsigned int addr) -{ - return inl(wc->iobase + addr); -} - -static inline void wctdm_setctl(struct wctdm *wc, unsigned int addr, unsigned int val) -{ - unsigned long flags; - spin_lock_irqsave(&wc->reglock, flags); - __wctdm_setctl(wc, addr, val); - spin_unlock_irqrestore(&wc->reglock, flags); -} - -static inline int wctdm_setreg_full(struct wctdm *wc, int card, int addr, int val, int inisr) -{ - unsigned long flags; - int hit=0; - int ret; - - /* if a QRV card, use only its first channel */ - if (wc->modtype[card] == MOD_TYPE_QRV) - { - if (card & 3) return(0); - } - do { - spin_lock_irqsave(&wc->reglock, flags); - hit = empty_slot(wc, card); - if (hit > -1) { - wc->cmdq[card].cmds[hit] = CMD_WR(addr, val); - } - spin_unlock_irqrestore(&wc->reglock, flags); - if (inisr) - break; - if (hit < 0) { - if ((ret = schluffen(&wc->regq))) - return ret; - } - } while (hit < 0); - return (hit > -1) ? 0 : -1; -} - -static inline int wctdm_setreg_intr(struct wctdm *wc, int card, int addr, int val) -{ - return wctdm_setreg_full(wc, card, addr, val, 1); -} -static inline int wctdm_setreg(struct wctdm *wc, int card, int addr, int val) -{ - return wctdm_setreg_full(wc, card, addr, val, 0); -} - -static inline int wctdm_getreg(struct wctdm *wc, int card, int addr) -{ - unsigned long flags; - int hit; - int ret=0; - - /* if a QRV card, use only its first channel */ - if (wc->modtype[card] == MOD_TYPE_QRV) - { - if (card & 3) return(0); - } - do { - spin_lock_irqsave(&wc->reglock, flags); - hit = empty_slot(wc, card); - if (hit > -1) { - wc->cmdq[card].cmds[hit] = CMD_RD(addr); - } - spin_unlock_irqrestore(&wc->reglock, flags); - if (hit < 0) { - if ((ret = schluffen(&wc->regq))) - return ret; - } - } while (hit < 0); - do { - spin_lock_irqsave(&wc->reglock, flags); - if (wc->cmdq[card].cmds[hit] & __CMD_FIN) { - ret = wc->cmdq[card].cmds[hit] & 0xff; - wc->cmdq[card].cmds[hit] = 0x00000000; - hit = -1; - } - spin_unlock_irqrestore(&wc->reglock, flags); - if (hit > -1) { - if ((ret = schluffen(&wc->regq))) - return ret; - } - } while (hit > -1); - return ret; -} - -static inline unsigned int wctdm_getctl(struct wctdm *wc, unsigned int addr) -{ - unsigned long flags; - unsigned int val; - spin_lock_irqsave(&wc->reglock, flags); - val = __wctdm_getctl(wc, addr); - spin_unlock_irqrestore(&wc->reglock, flags); - return val; -} - -static inline int __wctdm_sdi_clk(struct wctdm *wc) -{ - unsigned int ret; - wc->sdi &= ~SDI_CLK; - __wctdm_setctl(wc, 0x0048, wc->sdi); - ret = __wctdm_getctl(wc, 0x0048); - wc->sdi |= SDI_CLK; - __wctdm_setctl(wc, 0x0048, wc->sdi); - return ret & SDI_DIN; -} - -static inline void __wctdm_sdi_sendbits(struct wctdm *wc, unsigned int bits, int count) -{ - wc->sdi &= ~SDI_DREAD; - __wctdm_setctl(wc, 0x0048, wc->sdi); - while(count--) { - if (bits & (1 << count)) - wc->sdi |= SDI_DOUT; - else - wc->sdi &= ~SDI_DOUT; - __wctdm_sdi_clk(wc); - } -} - -static inline unsigned int __wctdm_sdi_recvbits(struct wctdm *wc, int count) -{ - unsigned int bits=0; - wc->sdi |= SDI_DREAD; - __wctdm_setctl(wc, 0x0048, wc->sdi); - while(count--) { - bits <<= 1; - if (__wctdm_sdi_clk(wc)) - bits |= 1; - else - bits &= ~1; - } - return bits; -} - -static inline void __wctdm_setsdi(struct wctdm *wc, unsigned char addr, unsigned short value) -{ - unsigned int bits; - /* Send preamble */ - bits = 0xffffffff; - __wctdm_sdi_sendbits(wc, bits, 32); - bits = (0x5 << 12) | (1 << 7) | (addr << 2) | 0x2; - __wctdm_sdi_sendbits(wc, bits, 16); - __wctdm_sdi_sendbits(wc, value, 16); - -} - -static inline unsigned short __wctdm_getsdi(struct wctdm *wc, unsigned char addr) -{ - unsigned int bits; - /* Send preamble */ - bits = 0xffffffff; - __wctdm_sdi_sendbits(wc, bits, 32); - bits = (0x6 << 10) | (1 << 5) | (addr); - __wctdm_sdi_sendbits(wc, bits, 14); - return __wctdm_sdi_recvbits(wc, 18); -} - -static inline void wctdm_setsdi(struct wctdm *wc, unsigned char addr, unsigned short value) -{ - unsigned long flags; - spin_lock_irqsave(&wc->reglock, flags); - __wctdm_setsdi(wc, addr, value); - spin_unlock_irqrestore(&wc->reglock, flags); -} - -static inline unsigned short wctdm_getsdi(struct wctdm *wc, unsigned char addr) -{ - unsigned long flags; - unsigned short val; - spin_lock_irqsave(&wc->reglock, flags); - val = __wctdm_getsdi(wc, addr); - spin_unlock_irqrestore(&wc->reglock, flags); - return val; -} -#ifdef VPM_SUPPORT -static inline unsigned char wctdm_vpm_in(struct wctdm *wc, int unit, const unsigned int addr) -{ - return wctdm_getreg(wc, unit + NUM_CARDS, addr); -} - -static inline void wctdm_vpm_out(struct wctdm *wc, int unit, const unsigned int addr, const unsigned char val) -{ - wctdm_setreg(wc, unit + NUM_CARDS, addr, val); -} -#endif - -static inline void cmd_retransmit(struct wctdm *wc) -{ - int x,y; - unsigned long flags; - /* Force retransmissions */ - spin_lock_irqsave(&wc->reglock, flags); - for (x=0;xcards;y++) { - if (!(wc->cmdq[y].cmds[x] & __CMD_FIN)) - wc->cmdq[y].cmds[x] &= ~(__CMD_TX | (0xff << 24)); - } - } - spin_unlock_irqrestore(&wc->reglock, flags); -} - -static inline void wctdm_receiveprep(struct wctdm *wc, int dbl) -{ - volatile unsigned char *readchunk; - int x,y; - unsigned char expected; - - dbl = dbl % 2; - - readchunk = (volatile unsigned char *)wc->readchunk; - if (dbl) - readchunk += SFRAME_SIZE; - for (x=0;xrxident+1; - wc->rxident = readchunk[EFRAME_SIZE + 1]; - if (wc->rxident != expected) { - wc->span.irqmisses++; - cmd_retransmit(wc); - } - } - for (y=0;y < wc->cards;y++) { - if (y < wc->type) - wc->chans[y].readchunk[x] = readchunk[y]; - cmd_decifer(wc, readchunk, y); - } -#ifdef VPM_SUPPORT - if (wc->vpm) { - for (y=NUM_CARDS;y < NUM_CARDS + NUM_EC; y++) - cmd_decifer(wc, readchunk, y); - } -#endif -#if 0 - if (cmddesc < 1024) { - printk("RC Result: %02x\n", readchunk[EFRAME_SIZE+1]); - } -#endif - readchunk += (EFRAME_SIZE + EFRAME_GAP); - } - /* XXX We're wasting 8 taps. We should get closer :( */ - for (x=0;xtype;x++) { - if (wc->cardflag & (1 << x)) - zt_ec_chunk(&wc->chans[x], wc->chans[x].readchunk, wc->chans[x].writechunk); - } - zt_receive(&wc->span); - /* Wake up anyone sleeping to read/write a new register */ - wake_up_interruptible(&wc->regq); -} - -static void wctdm_stop_dma(struct wctdm *wc); -static void wctdm_restart_dma(struct wctdm *wc); - -static int wait_access(struct wctdm *wc, int card) -{ - unsigned char data=0; - long origjiffies; - int count = 0; - - #define MAX 10 /* 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(" ##### Loop error (%02x) #####\n", data); - - return 0; -} - -static unsigned char translate_3215(unsigned char address) -{ - int x; - for (x=0;xflags[card] & FLAG_3215) { - address = translate_3215(address); - if (address == 255) - return 0; - } - 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; - }; - return res; -} - -static int wctdm_proslic_getreg_indirect(struct wctdm *wc, int card, unsigned char address) -{ - int res = -1; - char *p=NULL; - /* Translate 3215 addresses */ - if (wc->flags[card] & FLAG_3215) { - address = translate_3215(address); - if (address == 255) - return 0; - } - 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"; - if (p) - printk(p); - return res; -} - -static int wctdm_proslic_init_indirect_regs(struct wctdm *wc, int card) -{ - unsigned char i; - - for (i=0; iflags[card] & FLAG_3215) || (indirect_regs[i].altaddr != 255))) - { - printk("!!!!!!! %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("Init Indirect Registers completed successfully.\n"); - } else { - printk(" !!!!! Init Indirect Registers UNSUCCESSFULLY.\n"); - return -1; - } - return 0; -} - -static inline void wctdm_proslic_recheck_sanity(struct wctdm *wc, int card) -{ - int res; -#ifdef PAQ_DEBUG - res = wc->cmdq[card].isrshadow[1]; - res &= ~0x3; - if (res) { - wc->cmdq[card].isrshadow[1]=0; - wc->mods[card].fxs.palarms++; - if (wc->mods[card].fxs.palarms < MAX_ALARMS) { - printk("Power alarm (%02x) on module %d, resetting!\n", res, card + 1); - if (wc->mods[card].fxs.lasttxhook == 4) - wc->mods[card].fxs.lasttxhook = 1; - wc->sethook[card] = CMD_WR(19, res); -#if 0 - wc->sethook[card] = CMD_WR(64, wc->mods[card].fxs.lasttxhook); -#endif - - /* wctdm_setreg_intr(wc, card, 64, wc->mods[card].fxs.lasttxhook); */ - /* Update shadow register to avoid extra power alarms until next read */ - wc->cmdq[card].isrshadow[1] = 0; - } else { - if (wc->mods[card].fxs.palarms == MAX_ALARMS) - printk("Too many power alarms on card %d, NOT resetting!\n", card + 1); - } - } -#else - res = wc->cmdq[card].isrshadow[1]; - if (!res && (res != wc->mods[card].fxs.lasttxhook)) { - wc->mods[card].fxs.palarms++; - if (wc->mods[card].fxs.palarms < MAX_ALARMS) { - printk("Power alarm on module %d, resetting!\n", card + 1); - if (wc->mods[card].fxs.lasttxhook == 4) - wc->mods[card].fxs.lasttxhook = 1; - wc->sethook[card] = CMD_WR(64, wc->mods[card].fxs.lasttxhook); - - /* wctdm_setreg_intr(wc, card, 64, wc->mods[card].fxs.lasttxhook); */ - /* Update shadow register to avoid extra power alarms until next read */ - wc->cmdq[card].isrshadow[1] = wc->mods[card].fxs.lasttxhook; - } else { - if (wc->mods[card].fxs.palarms == MAX_ALARMS) - printk("Too many power alarms on card %d, NOT resetting!\n", card + 1); - } - } -#endif -} - -static inline void wctdm_qrvdri_check_hook(struct wctdm *wc, int card) -{ - signed char b,b1; - int qrvcard = card & 0xfc; - - - if (wc->qrvdebtime[card] >= 2) wc->qrvdebtime[card]--; - b = wc->cmdq[qrvcard].isrshadow[0]; /* Hook/Ring state */ - b &= 0xcc; /* use bits 3-4 and 6-7 only */ - - if (wc->radmode[qrvcard] & RADMODE_IGNORECOR) b &= ~4; - else if (!(wc->radmode[qrvcard] & RADMODE_INVERTCOR)) b ^= 4; - if (wc->radmode[qrvcard + 1] | RADMODE_IGNORECOR) b &= ~0x40; - else if (!(wc->radmode[qrvcard + 1] | RADMODE_INVERTCOR)) b ^= 0x40; - - if ((wc->radmode[qrvcard] & RADMODE_IGNORECT) || - (!(wc->radmode[qrvcard] & RADMODE_EXTTONE))) b &= ~8; - else if (!(wc->radmode[qrvcard] & RADMODE_EXTINVERT)) b ^= 8; - if ((wc->radmode[qrvcard + 1] & RADMODE_IGNORECT) || - (!(wc->radmode[qrvcard + 1] & RADMODE_EXTTONE))) b &= ~0x80; - else if (!(wc->radmode[qrvcard + 1] & RADMODE_EXTINVERT)) b ^= 0x80; - /* now b & MASK should be zero, if its active */ - /* check for change in chan 0 */ - if ((!(b & 0xc)) != wc->qrvhook[qrvcard + 2]) - { - wc->qrvdebtime[qrvcard] = wc->debouncetime[qrvcard]; - wc->qrvhook[qrvcard + 2] = !(b & 0xc); - } - /* if timed-out and ready */ - if (wc->qrvdebtime[qrvcard] == 1) - { - b1 = wc->qrvhook[qrvcard + 2]; -if (debug) printk("QRV channel %d rx state changed to %d\n",qrvcard,wc->qrvhook[qrvcard + 2]); - zt_hooksig(&wc->chans[qrvcard], - (b1) ? ZT_RXSIG_OFFHOOK : ZT_RXSIG_ONHOOK); - wc->qrvdebtime[card] = 0; - } - /* check for change in chan 1 */ - if ((!(b & 0xc0)) != wc->qrvhook[qrvcard + 3]) - { - wc->qrvdebtime[qrvcard + 1] = QRV_DEBOUNCETIME; - wc->qrvhook[qrvcard + 3] = !(b & 0xc0); - } - if (wc->qrvdebtime[qrvcard + 1] == 1) - { - b1 = wc->qrvhook[qrvcard + 3]; -if (debug) printk("QRV channel %d rx state changed to %d\n",qrvcard + 1,wc->qrvhook[qrvcard + 3]); - zt_hooksig(&wc->chans[qrvcard + 1], - (b1) ? ZT_RXSIG_OFFHOOK : ZT_RXSIG_ONHOOK); - wc->qrvdebtime[card] = 0; - } - return; -} - -static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) -{ - unsigned char res; - signed char b; - /* Try to track issues that plague slot one FXO's */ - b = wc->cmdq[card].isrshadow[0]; /* Hook/Ring state */ - b &= 0x9b; - if (wc->mods[card].fxo.offhook) { - if (b != 0x9) - wctdm_setreg_intr(wc, card, 5, 0x9); - } else { - if (b != 0x8) - wctdm_setreg_intr(wc, card, 5, 0x8); - } - if (!wc->mods[card].fxo.offhook) { - res = wc->cmdq[card].isrshadow[0]; /* Hook/Ring state */ - if ((res & 0x60) && wc->mods[card].fxo.battery) { - wc->mods[card].fxo.ringdebounce += (ZT_CHUNKSIZE * 4); - if (wc->mods[card].fxo.ringdebounce >= ZT_CHUNKSIZE * RING_DEBOUNCE) { - if (!wc->mods[card].fxo.wasringing) { - wc->mods[card].fxo.wasringing = 1; - zt_hooksig(&wc->chans[card], ZT_RXSIG_RING); - if (debug) - printk("RING on %d/%d!\n", wc->span.spanno, card + 1); - } - wc->mods[card].fxo.ringdebounce = ZT_CHUNKSIZE * RING_DEBOUNCE; - } - } else { - wc->mods[card].fxo.ringdebounce -= ZT_CHUNKSIZE; - if (wc->mods[card].fxo.ringdebounce <= 0) { - if (wc->mods[card].fxo.wasringing) { - wc->mods[card].fxo.wasringing = 0; - zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK); - if (debug) - printk("NO RING on %d/%d!\n", wc->span.spanno, card + 1); - } - wc->mods[card].fxo.ringdebounce = 0; - } - - } - } - b = wc->cmdq[card].isrshadow[1]; /* Voltage */ - if (abs(b) < battthresh) { - wc->mods[card].fxo.nobatttimer++; -#if 0 - if (wc->mods[card].fxo.battery) - printk("Battery loss: %d (%d debounce)\n", b, wc->mods[card].fxo.battdebounce); -#endif - if (wc->mods[card].fxo.battery && !wc->mods[card].fxo.battdebounce) { - if (debug) - printk("NO BATTERY on %d/%d!\n", wc->span.spanno, card + 1); - wc->mods[card].fxo.battery = 0; -#ifdef JAPAN - if ((!wc->ohdebounce) && wc->offhook) { - zt_hooksig(&wc->chans[card], ZT_RXSIG_ONHOOK); - if (debug) - printk("Signalled On Hook\n"); -#ifdef ZERO_BATT_RING - wc->onhook++; -#endif - } -#else - zt_hooksig(&wc->chans[card], ZT_RXSIG_ONHOOK); -#endif - wc->mods[card].fxo.battdebounce = battdebounce; - } else if (!wc->mods[card].fxo.battery) - wc->mods[card].fxo.battdebounce = battdebounce; - } else if (abs(b) > battthresh) { - if (!wc->mods[card].fxo.battery && !wc->mods[card].fxo.battdebounce) { - if (debug) - printk("BATTERY on %d/%d (%s)!\n", wc->span.spanno, card + 1, - (b < 0) ? "-" : "+"); -#ifdef ZERO_BATT_RING - if (wc->onhook) { - wc->onhook = 0; - zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK); - if (debug) - printk("Signalled Off Hook\n"); - } -#else - zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK); -#endif - wc->mods[card].fxo.battery = 1; - wc->mods[card].fxo.nobatttimer = 0; - wc->mods[card].fxo.battdebounce = battdebounce; - } else if (wc->mods[card].fxo.battery) - wc->mods[card].fxo.battdebounce = battdebounce; - - if (wc->mods[card].fxo.lastpol >= 0) { - if (b < 0) { - wc->mods[card].fxo.lastpol = -1; - wc->mods[card].fxo.polaritydebounce = POLARITY_DEBOUNCE; - } - } - if (wc->mods[card].fxo.lastpol <= 0) { - if (b > 0) { - wc->mods[card].fxo.lastpol = 1; - wc->mods[card].fxo.polaritydebounce = POLARITY_DEBOUNCE; - } - } - } else { - /* It's something else... */ - wc->mods[card].fxo.battdebounce = battdebounce; - } - if (wc->mods[card].fxo.battdebounce) - wc->mods[card].fxo.battdebounce--; - if (wc->mods[card].fxo.polaritydebounce) { - wc->mods[card].fxo.polaritydebounce--; - if (wc->mods[card].fxo.polaritydebounce < 1) { - if (wc->mods[card].fxo.lastpol != wc->mods[card].fxo.polarity) { - if (debug) - printk("%lu Polarity reversed (%d -> %d)\n", jiffies, - wc->mods[card].fxo.polarity, - wc->mods[card].fxo.lastpol); - if (wc->mods[card].fxo.polarity) - zt_qevent_lock(&wc->chans[card], ZT_EVENT_POLARITY); - wc->mods[card].fxo.polarity = wc->mods[card].fxo.lastpol; - } - } - } -} - -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->cmdq[card].isrshadow[0]; /* Hook state */ - hook = (res & 1); - - if (hook != wc->mods[card].fxs.lastrxhook) { - /* Reset the debounce (must be multiple of 4ms) */ - wc->mods[card].fxs.debounce = 8 * (4 * 8); -#if 0 - printk("Resetting debounce card %d hook %d, %d\n", card, hook, wc->mods[card].fxs.debounce); -#endif - } else { - if (wc->mods[card].fxs.debounce > 0) { - wc->mods[card].fxs.debounce-= 4 * ZT_CHUNKSIZE; -#if 0 - printk("Sustaining hook %d, %d\n", hook, wc->mods[card].fxs.debounce); -#endif - if (!wc->mods[card].fxs.debounce) { -#if 0 - printk("Counted down debounce, newhook: %d...\n", hook); -#endif - wc->mods[card].fxs.debouncehook = hook; - } - if (!wc->mods[card].fxs.oldrxhook && wc->mods[card].fxs.debouncehook) { - /* Off hook */ - if (debug) - printk("wctdm: Card %d Going off hook\n", card); - zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK); - if (robust) - wctdm_init_proslic(wc, card, 1, 0, 1); - wc->mods[card].fxs.oldrxhook = 1; - - } else if (wc->mods[card].fxs.oldrxhook && !wc->mods[card].fxs.debouncehook) { - /* On hook */ - if (debug) - printk("wctdm: Card %d Going on hook\n", card); - zt_hooksig(&wc->chans[card], ZT_RXSIG_ONHOOK); - wc->mods[card].fxs.oldrxhook = 0; - } - } - } - wc->mods[card].fxs.lastrxhook = hook; -} - - -static inline void wctdm_reinit_descriptor(struct wctdm *wc, int tx, int dbl, char *s) -{ - int o2 = 0; - o2 += dbl * 4; - if (!tx) - o2 += ERING_SIZE * 4; - wc->descripchunk[o2] = 0x80000000; -} - -#ifdef VPM_SUPPORT -static inline void wctdm_vpm_check(struct wctdm *wc, int x) -{ - if (wc->cmdq[x].isrshadow[0]) { - if (debug) - printk("VPM: Detected dtmf ON channel %02x on chip %d!\n", wc->cmdq[x].isrshadow[0], x - NUM_CARDS); - wc->sethook[x] = CMD_WR(0xb9, wc->cmdq[x].isrshadow[0]); - wc->cmdq[x].isrshadow[0] = 0; - /* Cancel most recent lookup, if there is one */ - wc->cmdq[x].cmds[USER_COMMANDS+0] = 0x00000000; - } else if (wc->cmdq[x].isrshadow[1]) { - if (debug) - printk("VPM: Detected dtmf OFF channel %02x on chip %d!\n", wc->cmdq[x].isrshadow[1], x - NUM_CARDS); - wc->sethook[x] = CMD_WR(0xbd, wc->cmdq[x].isrshadow[1]); - wc->cmdq[x].isrshadow[1] = 0; - /* Cancel most recent lookup, if there is one */ - wc->cmdq[x].cmds[USER_COMMANDS+1] = 0x00000000; - } -} - -static int wctdm_echocan(struct zt_chan *chan, int eclen) -{ - struct wctdm *wc = chan->pvt; - int channel; - int unit; - if (!wc->vpm) - return -ENODEV; - channel = (chan->chanpos - 1); - unit = (chan->chanpos - 1) & 0x3; - if (wc->vpm < 2) - channel >>= 2; - - if(debug) - printk("echocan: Unit is %d, Channel is %d length %d\n", - unit, channel, eclen); - if (eclen) - wctdm_vpm_out(wc,unit,channel,0x3e); - else - wctdm_vpm_out(wc,unit,channel,0x01); - return 0; -} -#endif -static inline void wctdm_isr_misc(struct wctdm *wc) -{ - int x; - - for (x=0;xcards;x++) { - if (wc->cardflag & (1 << x)) { - if (wc->modtype[x] == MOD_TYPE_FXS) { - if (!(wc->intcount % 10000)) { - /* Accept an alarm once per 10 seconds */ - if (wc->mods[x].fxs.palarms) - wc->mods[x].fxs.palarms--; - } - wctdm_proslic_check_hook(wc, x); - if (!(wc->intcount & 0xfc)) - wctdm_proslic_recheck_sanity(wc, x); - if (wc->mods[x].fxs.lasttxhook == 0x4) { - /* RINGing, prepare for OHT */ - wc->mods[x].fxs.ohttimer = OHT_TIMER << 3; - wc->mods[x].fxs.idletxhookstate = 0x2; /* OHT mode when idle */ - } else { - if (wc->mods[x].fxs.ohttimer) { - wc->mods[x].fxs.ohttimer-= ZT_CHUNKSIZE; - if (!wc->mods[x].fxs.ohttimer) { - wc->mods[x].fxs.idletxhookstate = 0x1; /* Switch to active */ - if (wc->mods[x].fxs.lasttxhook == 0x2) { - /* Apply the change if appropriate */ - wc->mods[x].fxs.lasttxhook = 0x1; - wc->sethook[x] = CMD_WR(64, wc->mods[x].fxs.lasttxhook); - /* wctdm_setreg_intr(wc, x, 64, wc->mods[x].fxs.lasttxhook); */ - } - } - } - } - } else if (wc->modtype[x] == MOD_TYPE_FXO) { - wctdm_voicedaa_check_hook(wc, x); - } else if (wc->modtype[x] == MOD_TYPE_QRV) { - wctdm_qrvdri_check_hook(wc, x); - } - } - } -#ifdef VPM_SUPPORT - if (wc->vpm > 0) { - for (x=NUM_CARDS;xrdbl * 4; - } else { - o2 += wc->tdbl * 4; - } - if (!(wc->descripchunk[o2] & 0x80000000)) { - if (tx) { - wc->txints++; - wctdm_transmitprep(wc, wc->tdbl); - wctdm_reinit_descriptor(wc, tx, wc->tdbl, "txchk"); - wc->tdbl = (wc->tdbl + 1) % ERING_SIZE; - wctdm_isr_misc(wc); - wc->intcount++; - } else { - wc->rxints++; - wctdm_receiveprep(wc, wc->rdbl); - wctdm_reinit_descriptor(wc, tx, wc->rdbl, "rxchk"); - wc->rdbl = (wc->rdbl + 1) % ERING_SIZE; - } - return 1; - } - return 0; -} - -static void wctdm_init_descriptors(struct wctdm *wc) -{ - volatile unsigned int *descrip; - dma_addr_t descripdma; - dma_addr_t writedma; - dma_addr_t readdma; - int x; - - descrip = wc->descripchunk; - descripdma = wc->descripdma; - writedma = wc->writedma; - readdma = wc->readdma; - - for (x=0;xdescripdma; - - /* Transmit descriptor */ - descrip[0 ] = 0x80000000; - descrip[1 ] = 0xe5800000 | (SFRAME_SIZE); - if (x % 2) - descrip[2 ] = writedma + SFRAME_SIZE; - else - descrip[2 ] = writedma; - descrip[3 ] = descripdma; - - /* Receive descriptor */ - descrip[0 + ERING_SIZE * 4] = 0x80000000; - descrip[1 + ERING_SIZE * 4] = 0x01000000 | (SFRAME_SIZE); - if (x % 2) - descrip[2 + ERING_SIZE * 4] = readdma + SFRAME_SIZE; - else - descrip[2 + ERING_SIZE * 4] = readdma; - descrip[3 + ERING_SIZE * 4] = descripdma + ERING_SIZE * 16; - - /* Advance descriptor */ - descrip += 4; - } -} - -ZAP_IRQ_HANDLER(wctdm_interrupt) -{ - struct wctdm *wc = dev_id; - unsigned int ints; - int res; - - /* Read and clear interrupts */ - ints = wctdm_getctl(wc, 0x0028); - - if (!ints) -#ifdef LINUX26 - return IRQ_NONE; -#else - return; -#endif - - wctdm_setctl(wc, 0x0028, ints); - - ints &= wc->intmask; - if (ints & 0x00000041) { - do { - res = wctdm_check_descriptor(wc, 0); - res |= wctdm_check_descriptor(wc, 1); - } while(res); -#if 0 - while(wctdm_check_descriptor(wc, 0)); - wctdm_setctl(wc, 0x0010, 0x00000000); - } - if (ints & 0x00000005) { - while(wctdm_check_descriptor(wc, 1)); - wctdm_setctl(wc, 0x0008, 0x00000000); -#endif - } -#ifdef LINUX26 - return IRQ_RETVAL(1); -#endif - -} - -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("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("ProSLIC on module %d, product %d, version %d\n", card, (blah & 0x30) >> 4, (blah & 0xf)); - -#if 0 - if ((blah & 0x30) >> 4) { - printk("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("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("ProSLIC on module %d insane (1) %d should be 2\n", card, blah); - return -1; - } else if ( insane_report) - printk("ProSLIC on module %d Reg 8 Reads %d Expected is 0x2\n",card,blah); - - blah = wctdm_getreg(wc, card, 64); - if (blah != 0x0) { - printk("ProSLIC on module %d insane (2)\n", card); - return -1; - } else if ( insane_report) - printk("ProSLIC on module %d Reg 64 Reads %d Expected is 0x0\n",card,blah); - - blah = wctdm_getreg(wc, card, 11); - if (blah != 0x33) { - printk("ProSLIC on module %d insane (3)\n", card); - return -1; - } else if ( insane_report) - printk("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("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("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("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, 0xc0 /* 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) { - printk("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 TDM CARD??\n", - card, (int)(((jiffies - origjiffies) * 1000 / HZ)), - vbat * 375); - return -1; - } else if (debug) { - printk("ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n", - card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ))); - } - - /* 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("Loop current out of range! Setting to default 20mA!\n"); - } - else if (debug) - printk("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("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("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); - -} - -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("Timeout waiting for calibration of module %d\n", card); - return -1; - } - } - - if (debug) { - /* Print calibration parameters */ - printk("Calibration Vector Regs 98 - 107: \n"); - for (x=98;x<108;x++) { - printk("%d: %02x\n", x, wctdm_getreg(wc, card, x)); - } - } - return 0; -} - -static void wait_just_a_bit(int foo) -{ - long newjiffies; - newjiffies = jiffies + foo; - while(jiffies < newjiffies); -} - -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; - - if (wc->modtype[card & 0xfc] == MOD_TYPE_QRV) return -2; - - wc->modtype[card] = MOD_TYPE_NONE; - /* Wait just a bit */ - wait_just_a_bit(HZ/10); - - wc->modtype[card] = MOD_TYPE_FXO; - wait_just_a_bit(HZ/10); - - 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); - - /* 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 = (fxo_modes[_opermode].acim); - wctdm_setreg(wc, card, 30, reg30); - - /* Misc. DAA parameters */ - reg31 = 0xa3; - reg31 |= (fxo_modes[_opermode].ohs2 << 3); - wctdm_setreg(wc, card, 31, reg31); - - /* Set Transmit/Receive timeslot */ - wctdm_setreg(wc, card, 34, (card * 8) & 0xff); - wctdm_setreg(wc, card, 35, (card * 8) >> 8); - wctdm_setreg(wc, card, 36, (card * 8) & 0xff); - wctdm_setreg(wc, card, 37, (card * 8) >> 8); - - /* Enable ISO-Cap */ - wctdm_setreg(wc, card, 6, 0x00); - - /* 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("VoiceDAA did not bring up ISO link properly!\n"); - return -1; - } - if (debug) - printk("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); - - /* Apply negative Tx gain of 4.5db to DAA */ - wctdm_setreg(wc, card, 38, 0x14); /* 4db */ - wctdm_setreg(wc, card, 40, 0x15); /* 0.5db */ - - /* Apply negative Rx gain of 4.5db to DAA */ - wctdm_setreg(wc, card, 39, 0x14); /* 4db */ - wctdm_setreg(wc, card, 41, 0x15); /* 0.5db */ - - - /* Take values for fxotxgain and fxorxgain and apply them to module */ - if (fxotxgain) { - if (fxotxgain >= -150 && fxotxgain < 0) { - wctdm_setreg(wc, card, 38, 16 + (fxotxgain/-10)); - if(fxotxgain % 10) { - wctdm_setreg(wc, card, 40, 16 + (-fxotxgain%10)); - } - } - else if (fxotxgain <= 120 && fxotxgain > 0) { - wctdm_setreg(wc, card, 38, fxotxgain/10); - if(fxotxgain % 10) { - wctdm_setreg(wc, card, 40, (fxotxgain%10)); - } - } - } - if (fxorxgain) { - if (fxorxgain >= -150 && fxorxgain < 0) { - wctdm_setreg(wc, card, 39, 16+ (fxorxgain/-10)); - if(fxotxgain % 10) { - wctdm_setreg(wc, card, 41, 16 + (-fxorxgain%10)); - } - } - else if (fxorxgain <= 120 && fxorxgain > 0) { - wctdm_setreg(wc, card, 39, fxorxgain/10); - if(fxorxgain % 10) { - wctdm_setreg(wc, card, 41, (fxorxgain%10)); - } - } - } - - if(debug) - printk("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; - - if (wc->modtype[card & 0xfc] == MOD_TYPE_QRV) return -2; - - /* By default, don't send on hook */ - wc->mods[card].fxs.idletxhookstate = 1; - - /* Sanity check the ProSLIC */ - if (!sane && wctdm_proslic_insane(wc, card)) - return -2; - - 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, 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("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("ProSLIC module %d failed leakage test. Check for short circuit\n", card); - } - /* Power up again */ - if (wctdm_powerup_proslic(wc, card, fast)) { - printk("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("Proslic failed on Manual Calibration\n"); - if (wctdm_proslic_manual_calibrate(wc, card)) { - printk("Proslic Failed on Second Attempt to Calibrate Manually. (Try -DNO_CALIBRATION in Makefile)\n"); - return -1; - } - printk("Proslic Passed Manual Calibration on Second Attempt\n"); - } - } - else { - if(wctdm_proslic_calibrate(wc, card)) { - //printk("ProSlic died on Auto Calibration.\n"); - if (wctdm_proslic_calibrate(wc, card)) { - printk("Proslic Failed on Second Attempt to Auto Calibrate\n"); - return -1; - } - printk("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("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;xmods[card].fxs.calregs.vals[x] = wctdm_getreg(wc, card, 96 + x); -#endif - - } else { - /* Restore calibration registers */ - for (x=0;xmods[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, (card * 8) & 0xff); // Tx Start count low byte 0 - wctdm_setreg(wc, card, 3, (card * 8) >> 8); // Tx Start count high byte 0 - wctdm_setreg(wc, card, 4, (card * 8) & 0xff); // Rx Start count low byte 0 - wctdm_setreg(wc, card, 5, (card * 8) >> 8); // 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, 22, 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("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("Reducing fast ring power on slot %d (50V peak)\n", card + 1); - } else - printk("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("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("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("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); - wc->mods[card].fxs.lasttxhook = 1; - return 0; -} - -static int wctdm_init_qrvdri(struct wctdm *wc, int card) -{ -unsigned char x,y; -unsigned long endjif; - - /* have to set this, at least for now */ - wc->modtype[card] = MOD_TYPE_QRV; - if (!(card & 3)) /* if at base of card, reset and write it */ - { - wctdm_setreg(wc,card,0,0x80); - wctdm_setreg(wc,card,0,0x55); - wctdm_setreg(wc,card,1,0x69); - wc->qrvhook[card] = wc->qrvhook[card + 1] = 0; - wc->qrvhook[card + 2] = wc->qrvhook[card + 3] = 0xff; - wc->debouncetime[card] = wc->debouncetime[card + 1] = QRV_DEBOUNCETIME; - wc->qrvdebtime[card] = wc->qrvdebtime[card + 1] = 0; - wc->radmode[card] = wc->radmode[card + 1] = 0; - wc->txgain[card] = wc->txgain[card + 1] = 3599; - wc->rxgain[card] = wc->rxgain[card + 1] = 1199; - } else { /* channel is on same card as base, no need to test */ - if (wc->modtype[card & 0x7c] == MOD_TYPE_QRV) - { - /* only lower 2 are valid */ - if (!(card & 2)) return 0; - } - wc->modtype[card] = MOD_TYPE_NONE; - return 1; - } - x = wctdm_getreg(wc,card,0); - y = wctdm_getreg(wc,card,1); - /* if not a QRV card, return as such */ - if ((x != 0x55) || (y != 0x69)) - { - wc->modtype[card] = MOD_TYPE_NONE; - return 1; - } - for(x = 0; x < 0x30; x++) - { - if ((x >= 0x1c) && (x <= 0x1e)) wctdm_setreg(wc,card,x,0xff); - else wctdm_setreg(wc,card,x,0); - } - wctdm_setreg(wc,card,0,0x80); - endjif = jiffies + (HZ/10); - while(endjif > jiffies); - wctdm_setreg(wc,card,0,0x10); - wctdm_setreg(wc,card,0,0x10); - endjif = jiffies + (HZ/10); - while(endjif > jiffies); - /* set up modes */ - wctdm_setreg(wc,card,0,0x1c); - /* set up I/O directions */ - wctdm_setreg(wc,card,1,0x33); - wctdm_setreg(wc,card,2,0x0f); - wctdm_setreg(wc,card,5,0x0f); - /* set up I/O to quiescent state */ - wctdm_setreg(wc,card,3,0x11); /* D0-7 */ - wctdm_setreg(wc,card,4,0xa); /* D8-11 */ - wctdm_setreg(wc,card,7,0); /* CS outputs */ - /* set up timeslots */ - wctdm_setreg(wc,card,0x13,card + 0x80); /* codec 2 tx, ts0 */ - wctdm_setreg(wc,card,0x17,card + 0x80); /* codec 0 rx, ts0 */ - wctdm_setreg(wc,card,0x14,card + 0x81); /* codec 1 tx, ts1 */ - wctdm_setreg(wc,card,0x18,card + 0x81); /* codec 1 rx, ts1 */ - /* set up for max gains */ - wctdm_setreg(wc,card,0x26,0x24); - wctdm_setreg(wc,card,0x27,0x24); - wctdm_setreg(wc,card,0x0b,0x01); /* "Transmit" gain codec 0 */ - wctdm_setreg(wc,card,0x0c,0x01); /* "Transmit" gain codec 1 */ - wctdm_setreg(wc,card,0x0f,0xff); /* "Receive" gain codec 0 */ - wctdm_setreg(wc,card,0x10,0xff); /* "Receive" gain codec 1 */ - return 0; -} - -static void qrv_dosetup(struct zt_chan *chan,struct wctdm *wc) -{ -int qrvcard; -unsigned char r; -long l; - - /* actually do something with the values */ - qrvcard = (chan->chanpos - 1) & 0xfc; - if (debug) printk("@@@@@ radmodes: %d,%d rxgains: %d,%d txgains: %d,%d\n", - wc->radmode[qrvcard],wc->radmode[qrvcard + 1], - wc->rxgain[qrvcard],wc->rxgain[qrvcard + 1], - wc->txgain[qrvcard],wc->txgain[qrvcard + 1]); - r = 0; - if (wc->radmode[qrvcard] & RADMODE_DEEMP) r |= 4; - if (wc->radmode[qrvcard + 1] & RADMODE_DEEMP) r |= 8; - if (wc->rxgain[qrvcard] < 1200) r |= 1; - if (wc->rxgain[qrvcard + 1] < 1200) r |= 2; - wctdm_setreg(wc, qrvcard, 7, r); - if (debug) printk("@@@@@ setting reg 7 to %02x hex\n",r); - r = 0; - if (wc->radmode[qrvcard] & RADMODE_PREEMP) r |= 3; - else if (wc->txgain[qrvcard] >= 3600) r |= 1; - else if (wc->txgain[qrvcard] >= 1200) r |= 2; - if (wc->radmode[qrvcard + 1] & RADMODE_PREEMP) r |= 0xc; - else if (wc->txgain[qrvcard + 1] >= 3600) r |= 4; - else if (wc->txgain[qrvcard + 1] >= 1200) r |= 8; - wctdm_setreg(wc, qrvcard, 4, r); - if (debug) printk("@@@@@ setting reg 4 to %02x hex\n",r); - r = 0; - if (wc->rxgain[qrvcard] >= 2400) r |= 1; - if (wc->rxgain[qrvcard + 1] >= 2400) r |= 2; - wctdm_setreg(wc, qrvcard, 0x25, r); - if (debug) printk("@@@@@ setting reg 0x25 to %02x hex\n",r); - r = 0; - if (wc->txgain[qrvcard] < 2400) r |= 1; else r |= 4; - if (wc->txgain[qrvcard + 1] < 2400) r |= 8; else r |= 0x20; - wctdm_setreg(wc, qrvcard, 0x26, r); - if (debug) printk("@@@@@ setting reg 0x26 to %02x hex\n",r); - l = ((long)(wc->rxgain[qrvcard] % 1200) * 10000) / 46875; - if (l == 0) l = 1; - if (wc->rxgain[qrvcard] >= 2400) l += 181; - wctdm_setreg(wc, qrvcard, 0x0b, (unsigned char)l); - if (debug) printk("@@@@@ setting reg 0x0b to %02x hex\n",(unsigned char)l); - l = ((long)(wc->rxgain[qrvcard + 1] % 1200) * 10000) / 46875; - if (l == 0) l = 1; - if (wc->rxgain[qrvcard + 1] >= 2400) l += 181; - wctdm_setreg(wc, qrvcard, 0x0c, (unsigned char)l); - if (debug) printk("@@@@@ setting reg 0x0c to %02x hex\n",(unsigned char)l); - l = ((long)(wc->txgain[qrvcard] % 1200) * 10000) / 46875; - if (l == 0) l = 1; - wctdm_setreg(wc, qrvcard, 0x0f, (unsigned char)l); - if (debug) printk("@@@@@ setting reg 0x0f to %02x hex\n", (unsigned char)l); - l = ((long)(wc->txgain[qrvcard + 1] % 1200) * 10000) / 46875; - if (l == 0) l = 1; - wctdm_setreg(wc, qrvcard, 0x10,(unsigned char)l); - if (debug) printk("@@@@@ setting reg 0x10 to %02x hex\n",(unsigned char)l); - return; -} - -static int wctdm_ioctl(struct zt_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 wctdm *wc = chan->pvt; - int x; - union { - struct zt_radio_stat s; - struct zt_radio_param p; - } stack; - -#if 0 - /* XXX */ - printk("RxInts: %d, TxInts: %d\n", wc->rxints, wc->txints); - printk("RxIdent: %d, TxIdent: %d\n", wc->rxident, wc->txident); - for (x=0;xcards;x++) - printk("Card %d isrshadow: %02x/%02x\n", x, wc->cmdq[x].isrshadow[0], wc->cmdq[x].isrshadow[1]); - cmddesc = 0; -#endif -#if 0 - if (wc->vpm) { - char tmp[80]; - for (x=0;x<0x200;x++) { - switch (x & 0xf) { - case 0: - sprintf(tmp, "%03x: %02x ", x, wctdm_vpm_in(wc, 0, x)); - break; - case 0xf: - printk("%s%02x\n", tmp, wctdm_vpm_in(wc, 0, x)); - break; - default: - sprintf(tmp + strlen(tmp), "%02x ", wctdm_vpm_in(wc, 0, x)); - break; - } - } - } - -#endif - switch (cmd) { - case ZT_ONHOOKTRANSFER: - if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) - return -EINVAL; - if (get_user(x, (int *)data)) - return -EFAULT; - wc->mods[chan->chanpos - 1].fxs.ohttimer = x << 3; - wc->mods[chan->chanpos - 1].fxs.idletxhookstate = 0x2; /* OHT mode when idle */ - if (wc->mods[chan->chanpos - 1].fxs.lasttxhook == 0x1) { - /* Apply the change if appropriate */ - wc->mods[chan->chanpos - 1].fxs.lasttxhook = 0x2; - wc->sethook[chan->chanpos - 1] = CMD_WR(64, wc->mods[chan->chanpos - 1].fxs.lasttxhook); - /* wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mods[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((struct wctdm_stats *)data, &stats, sizeof(stats))) - return -EFAULT; - break; - case WCTDM_GET_REGS: - if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { - for (x=0;xchanpos -1, x); - for (x=0;xchanpos - 1, x); - } else if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_QRV) { - memset(®s, 0, sizeof(regs)); - for (x=0;x<0x32;x++) - regs.direct[x] = wctdm_getreg(wc, chan->chanpos - 1, x); - } else { - memset(®s, 0, sizeof(regs)); - for (x=0;xchanpos - 1, x); - } - 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->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) - return -EINVAL; - printk("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("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("-- Setting echo registers: \n"); - if (copy_from_user(&echoregs, (struct wctdm_echo_coefs*)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, 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("-- Set echo registers successfully\n"); - - break; - } else { - return -EINVAL; - - } - break; -#ifdef VPM_SUPPORT - case ZT_TONEDETECT: - if (get_user(x, (int *) data)) - return -EFAULT; - if (!wc->vpm) - return -ENOSYS; - if (x && !vpmdtmfsupport) - return -ENOSYS; - if (x & ZT_TONEDETECT_ON) - wc->dtmfmask |= (1 << (chan->chanpos - 1)); - else - wc->dtmfmask &= ~(1 << (chan->chanpos - 1)); - if (x & ZT_TONEDETECT_MUTE) - wc->dtmfmutemask |= (1 << (chan->chanpos - 1)); - else - wc->dtmfmutemask &= ~(1 << (chan->chanpos - 1)); - return 0; -#endif - case ZT_RADIO_GETPARAM: - if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_QRV) - return -ENOTTY; - if (copy_from_user(&stack.p,(struct zt_radio_param *)data,sizeof(struct zt_radio_param))) return -EFAULT; - stack.p.data = 0; /* start with 0 value in output */ - switch(stack.p.radpar) { - case ZT_RADPAR_INVERTCOR: - if (wc->radmode[chan->chanpos - 1] & RADMODE_INVERTCOR) - stack.p.data = 1; - break; - case ZT_RADPAR_IGNORECOR: - if (wc->radmode[chan->chanpos - 1] & RADMODE_IGNORECOR) - stack.p.data = 1; - break; - case ZT_RADPAR_IGNORECT: - if (wc->radmode[chan->chanpos - 1] & RADMODE_IGNORECT) - stack.p.data = 1; - break; - case ZT_RADPAR_EXTRXTONE: - stack.p.data = 0; - if (wc->radmode[chan->chanpos - 1] & RADMODE_EXTTONE) - { - stack.p.data = 1; - if (wc->radmode[chan->chanpos - 1] & RADMODE_EXTINVERT) - { - stack.p.data = 2; - } - } - break; - case ZT_RADPAR_DEBOUNCETIME: - stack.p.data = wc->debouncetime[chan->chanpos - 1]; - break; - case ZT_RADPAR_RXGAIN: - stack.p.data = wc->rxgain[chan->chanpos - 1] - 1199; - break; - case ZT_RADPAR_TXGAIN: - stack.p.data = wc->txgain[chan->chanpos - 1] - 3599; - break; - case ZT_RADPAR_DEEMP: - stack.p.data = 0; - if (wc->radmode[chan->chanpos - 1] & RADMODE_DEEMP) - { - stack.p.data = 1; - } - break; - case ZT_RADPAR_PREEMP: - stack.p.data = 0; - if (wc->radmode[chan->chanpos - 1] & RADMODE_PREEMP) - { - stack.p.data = 1; - } - break; - default: - return -EINVAL; - } - if (copy_to_user((struct zt_radio_param *)data,&stack.p,sizeof(struct zt_radio_param))) return -EFAULT; - break; - case ZT_RADIO_SETPARAM: - if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_QRV) - return -ENOTTY; - if (copy_from_user(&stack.p,(struct zt_radio_param *)data,sizeof(struct zt_radio_param))) return -EFAULT; - switch(stack.p.radpar) { - case ZT_RADPAR_INVERTCOR: - if (stack.p.data) - wc->radmode[chan->chanpos - 1] |= RADMODE_INVERTCOR; - else - wc->radmode[chan->chanpos - 1] &= ~RADMODE_INVERTCOR; - return 0; - case ZT_RADPAR_IGNORECOR: - if (stack.p.data) - wc->radmode[chan->chanpos - 1] |= RADMODE_IGNORECOR; - else - wc->radmode[chan->chanpos - 1] &= ~RADMODE_IGNORECOR; - return 0; - case ZT_RADPAR_IGNORECT: - if (stack.p.data) - wc->radmode[chan->chanpos - 1] |= RADMODE_IGNORECT; - else - wc->radmode[chan->chanpos - 1] &= ~RADMODE_IGNORECT; - return 0; - case ZT_RADPAR_EXTRXTONE: - if (stack.p.data) - wc->radmode[chan->chanpos - 1] |= RADMODE_EXTTONE; - else - wc->radmode[chan->chanpos - 1] &= ~RADMODE_EXTTONE; - if (stack.p.data > 1) - wc->radmode[chan->chanpos - 1] |= RADMODE_EXTINVERT; - else - wc->radmode[chan->chanpos - 1] &= ~RADMODE_EXTINVERT; - return 0; - case ZT_RADPAR_DEBOUNCETIME: - wc->debouncetime[chan->chanpos - 1] = stack.p.data; - return 0; - case ZT_RADPAR_RXGAIN: - /* if out of range */ - if ((stack.p.data <= -1200) || (stack.p.data > 1552)) - { - return -EINVAL; - } - wc->rxgain[chan->chanpos - 1] = stack.p.data + 1199; - break; - case ZT_RADPAR_TXGAIN: - /* if out of range */ - if (wc->radmode[chan->chanpos -1] & RADMODE_PREEMP) - { - if ((stack.p.data <= -2400) || (stack.p.data > 0)) - { - return -EINVAL; - } - } - else - { - if ((stack.p.data <= -3600) || (stack.p.data > 1200)) - { - return -EINVAL; - } - } - wc->txgain[chan->chanpos - 1] = stack.p.data + 3599; - break; - case ZT_RADPAR_DEEMP: - if (stack.p.data) - wc->radmode[chan->chanpos - 1] |= RADMODE_DEEMP; - else - wc->radmode[chan->chanpos - 1] &= ~RADMODE_DEEMP; - wc->rxgain[chan->chanpos - 1] = 1199; - break; - case ZT_RADPAR_PREEMP: - if (stack.p.data) - wc->radmode[chan->chanpos - 1] |= RADMODE_PREEMP; - else - wc->radmode[chan->chanpos - 1] &= ~RADMODE_PREEMP; - wc->txgain[chan->chanpos - 1] = 3599; - break; - default: - return -EINVAL; - } - qrv_dosetup(chan,wc); - return 0; - default: - return -ENOTTY; - } - return 0; -} - -static int wctdm_open(struct zt_chan *chan) -{ - struct wctdm *wc = chan->pvt; - if (!(wc->cardflag & (1 << (chan->chanpos - 1)))) - return -ENODEV; - if (wc->dead) - return -ENODEV; - wc->usecount++; -#ifndef LINUX26 - MOD_INC_USE_COUNT; -#else - try_module_get(THIS_MODULE); -#endif - return 0; -} - -static int wctdm_watchdog(struct zt_span *span, int event) -{ - printk("TDM: Restarting DMA\n"); - wctdm_restart_dma(span->pvt); - return 0; -} - -static int wctdm_close(struct zt_chan *chan) -{ - struct wctdm *wc = chan->pvt; - int x; - signed char reg; - wc->usecount--; -#ifndef LINUX26 - MOD_DEC_USE_COUNT; -#else - module_put(THIS_MODULE); -#endif - for (x=0;xcards;x++) { - if (wc->modtype[x] == MOD_TYPE_FXS) - wc->mods[x].fxs.idletxhookstate = 1; - if (wc->modtype[x] == MOD_TYPE_QRV) - { - int qrvcard = x & 0xfc; - - wc->qrvhook[x] = 0; - wc->qrvhook[x + 2] = 0xff; - wc->debouncetime[x] = QRV_DEBOUNCETIME; - wc->qrvdebtime[x] = 0; - wc->radmode[x] = 0; - wc->txgain[x] = 3599; - wc->rxgain[x] = 1199; - reg = 0; - if (!wc->qrvhook[qrvcard]) reg |= 1; - if (!wc->qrvhook[qrvcard + 1]) reg |= 0x10; - wc->sethook[qrvcard] = CMD_WR(3, reg); - qrv_dosetup(chan,wc); - } - } - /* If we're dead, release us now */ - if (!wc->usecount && wc->dead) - wctdm_release(wc); - return 0; -} - -static int wctdm_hooksig(struct zt_chan *chan, zt_txsig_t txsig) -{ - struct wctdm *wc = chan->pvt; - int reg=0,qrvcard; - if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_QRV) { - qrvcard = (chan->chanpos - 1) & 0xfc; - switch(txsig) { - case ZT_TXSIG_START: - case ZT_TXSIG_OFFHOOK: - wc->qrvhook[chan->chanpos - 1] = 1; - break; - case ZT_TXSIG_ONHOOK: - wc->qrvhook[chan->chanpos - 1] = 0; - break; - default: - printk("wctdm24xxp: Can't set tx state to %d\n", txsig); - } - reg = 0; - if (!wc->qrvhook[qrvcard]) reg |= 1; - if (!wc->qrvhook[qrvcard + 1]) reg |= 0x10; - wc->sethook[qrvcard] = CMD_WR(3, reg); - /* wctdm_setreg(wc, qrvcard, 3, reg); */ - } else if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { - switch(txsig) { - case ZT_TXSIG_START: - case ZT_TXSIG_OFFHOOK: - wc->mods[chan->chanpos - 1].fxo.offhook = 1; - wc->sethook[chan->chanpos - 1] = CMD_WR(5, 0x9); - /* wctdm_setreg(wc, chan->chanpos - 1, 5, 0x9); */ - break; - case ZT_TXSIG_ONHOOK: - wc->mods[chan->chanpos - 1].fxo.offhook = 0; - wc->sethook[chan->chanpos - 1] = CMD_WR(5, 0x8); - /* wctdm_setreg(wc, chan->chanpos - 1, 5, 0x8); */ - break; - default: - printk("wctdm24xxp: Can't set tx state to %d\n", txsig); - } - } else { - switch(txsig) { - case ZT_TXSIG_ONHOOK: - switch(chan->sig) { - case ZT_SIG_EM: - case ZT_SIG_FXOKS: - case ZT_SIG_FXOLS: - wc->mods[chan->chanpos - 1].fxs.lasttxhook = wc->mods[chan->chanpos - 1].fxs.idletxhookstate; - break; - case ZT_SIG_FXOGS: - wc->mods[chan->chanpos - 1].fxs.lasttxhook = 3; - break; - } - break; - case ZT_TXSIG_OFFHOOK: - switch(chan->sig) { - case ZT_SIG_EM: - wc->mods[chan->chanpos - 1].fxs.lasttxhook = 5; - break; - default: - wc->mods[chan->chanpos - 1].fxs.lasttxhook = wc->mods[chan->chanpos - 1].fxs.idletxhookstate; - break; - } - break; - case ZT_TXSIG_START: - wc->mods[chan->chanpos - 1].fxs.lasttxhook = 4; - break; - case ZT_TXSIG_KEWL: - wc->mods[chan->chanpos - 1].fxs.lasttxhook = 0; - break; - default: - printk("wctdm24xxp: Can't set tx state to %d\n", txsig); - } - if (debug) - printk("Setting FXS hook state to %d (%02x)\n", txsig, reg); - - - wc->sethook[chan->chanpos - 1] = CMD_WR(64, wc->mods[chan->chanpos - 1].fxs.lasttxhook); - /* wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mods[chan->chanpos - 1].fxs.lasttxhook); */ - } - return 0; -} - -static void wctdm_dacs_connect(struct wctdm *wc, int srccard, int dstcard) -{ - - if (wc->dacssrc[dstcard] > - 1) { - printk("wctdm_dacs_connect: Can't have double sourcing yet!\n"); - return; - } - if (!((wc->modtype[srccard] == MOD_TYPE_FXS)||(wc->modtype[srccard] == MOD_TYPE_FXO))){ - printk("wctdm_dacs_connect: Unsupported modtype for card %d\n", srccard); - return; - } - if (!((wc->modtype[dstcard] == MOD_TYPE_FXS)||(wc->modtype[dstcard] == MOD_TYPE_FXO))){ - printk("wctdm_dacs_connect: Unsupported modtype for card %d\n", dstcard); - return; - } - if (debug) - printk("connect %d => %d\n", srccard, dstcard); - wc->dacssrc[dstcard] = srccard; - - /* make srccard transmit to srccard+24 on the TDM bus */ - if (wc->modtype[srccard] == MOD_TYPE_FXS) { - /* proslic */ - wctdm_setreg(wc, srccard, PCM_XMIT_START_COUNT_LSB, ((srccard+24) * 8) & 0xff); - wctdm_setreg(wc, srccard, PCM_XMIT_START_COUNT_MSB, ((srccard+24) * 8) >> 8); - } else if(wc->modtype[srccard] == MOD_TYPE_FXO) { - /* daa */ - wctdm_setreg(wc, srccard, 34, ((srccard+24) * 8) & 0xff); /* TX */ - wctdm_setreg(wc, srccard, 35, ((srccard+24) * 8) >> 8); /* TX */ - } - - /* have dstcard receive from srccard+24 on the TDM bus */ - if (wc->modtype[dstcard] == MOD_TYPE_FXS) { - /* proslic */ - wctdm_setreg(wc, dstcard, PCM_RCV_START_COUNT_LSB, ((srccard+24) * 8) & 0xff); - wctdm_setreg(wc, dstcard, PCM_RCV_START_COUNT_MSB, ((srccard+24) * 8) >> 8); - } else if(wc->modtype[dstcard] == MOD_TYPE_FXO) { - /* daa */ - wctdm_setreg(wc, dstcard, 36, ((srccard+24) * 8) & 0xff); /* RX */ - wctdm_setreg(wc, dstcard, 37, ((srccard+24) * 8) >> 8); /* RX */ - } - -} - -static void wctdm_dacs_disconnect(struct wctdm *wc, int card) -{ - if (wc->dacssrc[card] > -1) { - if (debug) - printk("wctdm_dacs_disconnect: restoring TX for %d and RX for %d\n",wc->dacssrc[card], card); - - /* restore TX (source card) */ - if(wc->modtype[wc->dacssrc[card]] == MOD_TYPE_FXS){ - wctdm_setreg(wc, wc->dacssrc[card], PCM_XMIT_START_COUNT_LSB, (wc->dacssrc[card] * 8) & 0xff); - wctdm_setreg(wc, wc->dacssrc[card], PCM_XMIT_START_COUNT_MSB, (wc->dacssrc[card] * 8) >> 8); - } else if(wc->modtype[wc->dacssrc[card]] == MOD_TYPE_FXO){ - wctdm_setreg(wc, card, 34, (card * 8) & 0xff); - wctdm_setreg(wc, card, 35, (card * 8) >> 8); - } else { - printk("WARNING: wctdm_dacs_disconnect() called on unsupported modtype\n"); - } - - /* restore RX (this card) */ - if(wc->modtype[card] == MOD_TYPE_FXS){ - wctdm_setreg(wc, card, PCM_RCV_START_COUNT_LSB, (card * 8) & 0xff); - wctdm_setreg(wc, card, PCM_RCV_START_COUNT_MSB, (card * 8) >> 8); - } else if(wc->modtype[card] == MOD_TYPE_FXO){ - wctdm_setreg(wc, card, 36, (card * 8) & 0xff); - wctdm_setreg(wc, card, 37, (card * 8) >> 8); - } else { - printk("WARNING: wctdm_dacs_disconnect() called on unsupported modtype\n"); - } - - wc->dacssrc[card] = -1; - } -} - -static int wctdm_dacs(struct zt_chan *dst, struct zt_chan *src) -{ - struct wctdm *wc; - - if(!nativebridge) - return 0; /* should this return -1 since unsuccessful? */ - - wc = dst->pvt; - - if(src) { - wctdm_dacs_connect(wc, src->chanpos - 1, dst->chanpos - 1); - if (debug) - printk("dacs connecct: %d -> %d!\n\n", src->chanpos, dst->chanpos); - } else { - wctdm_dacs_disconnect(wc, dst->chanpos - 1); - if (debug) - printk("dacs disconnect: %d!\n", dst->chanpos); - } - return 0; -} - -static int wctdm_initialize(struct wctdm *wc) -{ - int x; - - /* Zapata stuff */ - sprintf(wc->span.name, "WCTDM/%d", wc->pos); - sprintf(wc->span.desc, "%s Board %d", wc->variety, wc->pos + 1); - if (alawoverride) { - printk("ALAW override parameter detected. Device will be operating in ALAW\n"); - wc->span.deflaw = ZT_LAW_ALAW; - } else - wc->span.deflaw = ZT_LAW_MULAW; - for (x=0;xcards;x++) { - sprintf(wc->chans[x].name, "WCTDM/%d/%d", wc->pos, x); - wc->chans[x].sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_SF | ZT_SIG_EM | ZT_SIG_CLEAR; - wc->chans[x].sigcap |= ZT_SIG_FXSKS | ZT_SIG_FXSLS | ZT_SIG_SF | ZT_SIG_CLEAR; - wc->chans[x].chanpos = x+1; - wc->chans[x].pvt = wc; - } - wc->span.chans = wc->chans; - wc->span.channels = wc->type; - wc->span.hooksig = wctdm_hooksig; - wc->span.open = wctdm_open; - wc->span.close = wctdm_close; - wc->span.flags = ZT_FLAG_RBS; - wc->span.ioctl = wctdm_ioctl; - wc->span.watchdog = wctdm_watchdog; - wc->span.dacs= wctdm_dacs; -#ifdef VPM_SUPPORT - wc->span.echocan = wctdm_echocan; -#endif - init_waitqueue_head(&wc->span.maintq); - - wc->span.pvt = wc; - if (zt_register(&wc->span, 0)) { - printk("Unable to register span with zaptel\n"); - return -1; - } - return 0; -} - -static void wctdm_post_initialize(struct wctdm *wc) -{ - int x; - /* Finalize signalling */ - for (x=0;xcards;x++) { - if (wc->cardflag & (1 << x)) { - if (wc->modtype[x] == MOD_TYPE_FXO) - wc->chans[x].sigcap = ZT_SIG_FXSKS | ZT_SIG_FXSLS | ZT_SIG_SF | ZT_SIG_CLEAR; - else if (wc->modtype[x] == MOD_TYPE_FXS) - wc->chans[x].sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_SF | ZT_SIG_EM | ZT_SIG_CLEAR; - else if (wc->modtype[x] == MOD_TYPE_QRV) - wc->chans[x].sigcap = ZT_SIG_SF | ZT_SIG_EM | ZT_SIG_CLEAR; - } - } -} - -static int wctdm_hardware_init(struct wctdm *wc) -{ - /* Hardware stuff */ - unsigned int reg; - unsigned long newjiffies; - - /* Initialize descriptors */ - wctdm_init_descriptors(wc); - - /* Enable I/O Access */ - pci_read_config_dword(wc->dev, 0x0004, ®); - reg |= 0x00000007; - pci_write_config_dword(wc->dev, 0x0004, reg); - printk("PCI Config reg is %08x\n", reg); - - wctdm_setctl(wc, 0x0000, 0xfff88001); - - newjiffies = jiffies + HZ/10; - while(((reg = wctdm_getctl(wc,0x0000)) & 0x00000001) && (newjiffies > jiffies)); - printk("%s: New Reg: %08x!\n", wc->variety, reg); - - - /* Configure watchdogs, access, etc */ - wctdm_setctl(wc, 0x0030, 0x00080048); - wctdm_setctl(wc, 0x0078, 0x00000013 /* | (1 << 28) */); - -#if 0 - /* XXX Enable loopback XXX */ - reg = wctdm_getctl(wc, 0x0030); - wctdm_setctl(wc, 0x0030, reg | 0x00000400); - -#else - reg = wctdm_getctl(wc, 0x00fc); - wctdm_setctl(wc, 0x00fc, (reg & ~0x7) | 0x7); - wctdm_setsdi(wc, 0x00, 0x0100); - wctdm_setsdi(wc, 0x16, 0x2100); - printk("Detected REG0: %08x\n", wctdm_getsdi(wc, 0x00)); - printk("Detected REG1: %08x\n", wctdm_getsdi(wc, 0x01)); - printk("Detected REG2: %08x\n", wctdm_getsdi(wc, 0x02)); - - reg = wctdm_getctl(wc, 0x00fc); - printk("(pre) Reg fc is %08x\n", reg); - - wctdm_setctl(wc, 0x00fc, (reg & ~0x7) | 0x4); - wctdm_setsdi(wc, 0x00, 0x0100); - wctdm_setsdi(wc, 0x16, 0x2100); - reg = wctdm_getctl(wc, 0x00fc); - printk("(post) Reg fc is %08x\n", reg); - printk("Detected REG2: %08x\n", wctdm_getsdi(wc, 0x02)); -#endif - printk("wctdm24xxp: reg is %08x\n", wctdm_getctl(wc, 0x0088)); - - return 0; -} - -static void wctdm_setintmask(struct wctdm *wc, unsigned int intmask) -{ - wc->intmask = intmask; - wctdm_setctl(wc, 0x0038, intmask); -} - -static void wctdm_enable_interrupts(struct wctdm *wc) -{ - /* Enable interrupts */ - wctdm_setintmask(wc, 0x00010041); -} - -static void wctdm_restart_dma(struct wctdm *wc) -{ -} - -static void wctdm_start_dma(struct wctdm *wc) -{ - unsigned int reg; - wmb(); - wctdm_setctl(wc, 0x0020, wc->descripdma); - wctdm_setctl(wc, 0x0018, wc->descripdma + (16 * ERING_SIZE)); - /* Start receiver/transmitter */ - reg = wctdm_getctl(wc, 0x0030); - wctdm_setctl(wc, 0x0030, reg | 0x00002002); - wctdm_setctl(wc, 0x0008, 0x00000000); - wctdm_setctl(wc, 0x0010, 0x00000000); - reg = wctdm_getctl(wc, 0x0028); - wctdm_setctl(wc, 0x0028, reg); - -} - -static void wctdm_stop_dma(struct wctdm *wc) -{ - /* Disable interrupts and reset */ - unsigned int reg; - /* Disable interrupts */ - wctdm_setintmask(wc, 0x00000000); - wctdm_setctl(wc, 0x0084, 0x00000000); - wctdm_setctl(wc, 0x0048, 0x00000000); - /* Reset the part to be on the safe side */ - reg = wctdm_getctl(wc, 0x0000); - reg |= 0x00000001; - wctdm_setctl(wc, 0x0000, reg); -} - -static void wctdm_disable_interrupts(struct wctdm *wc) -{ - /* Disable interrupts */ - wctdm_setintmask(wc, 0x00000000); - wctdm_setctl(wc, 0x0084, 0x00000000); -} - - -#ifdef VPM_SUPPORT -static void wctdm_vpm_set_dtmf_threshold(struct wctdm *wc, unsigned int threshold) -{ - unsigned int x; - - for (x = 0; x < 4; x++) { - wctdm_vpm_out(wc, x, 0xC4, (threshold >> 8) & 0xFF); - wctdm_vpm_out(wc, x, 0xC5, (threshold & 0xFF)); - } - printk("VPM: DTMF threshold set to %d\n", threshold); -} - -static void wctdm_vpm_init(struct wctdm *wc) -{ - unsigned char reg; - unsigned int mask; - unsigned int ver; - unsigned char vpmver=0; - unsigned int i, x, y; - - if (!vpmsupport) { - printk("VPM: Support Disabled\n"); - wc->vpm = 0; - return; - } - - for (x=0;xvpm = 0; - return; - } - - if (!x) { - vpmver = wctdm_vpm_in(wc, x, 0x1a6) & 0xf; - printk("VPM Revision: %02x\n", vpmver); - } - - - /* Setup GPIO's */ - for (y=0;y<4;y++) { - wctdm_vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */ - if (y == 3) - wctdm_vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */ - else - wctdm_vpm_out(wc, x, 0x1ac + y, 0xff); /* GPIO dir */ - wctdm_vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */ - } - - /* Setup TDM path - sets fsync and tdm_clk as inputs */ - reg = wctdm_vpm_in(wc, x, 0x1a3); /* misc_con */ - wctdm_vpm_out(wc, x, 0x1a3, reg & ~2); - - /* Setup Echo length (256 taps) */ - wctdm_vpm_out(wc, x, 0x022, 0); - - /* Setup timeslots */ - if (vpmver == 0x01) { - wctdm_vpm_out(wc, x, 0x02f, 0x00); - wctdm_vpm_out(wc, x, 0x023, 0xff); - mask = 0x11111111 << x; - } else { - wctdm_vpm_out(wc, x, 0x02f, 0x20 | (x << 3)); - wctdm_vpm_out(wc, x, 0x023, 0x3f); - mask = 0x0000003f; - } - - /* Setup the tdm channel masks for all chips*/ - for (i = 0; i < 4; i++) - wctdm_vpm_out(wc, x, 0x33 - i, (mask >> (i << 3)) & 0xff); - - /* Setup convergence rate */ - reg = wctdm_vpm_in(wc,x,0x20); - reg &= 0xE0; - if (alawoverride) { - if (!x) - printk("VPM: A-law mode\n"); - reg |= 0x01; - } else { - if (!x) - printk("VPM: U-law mode\n"); - reg &= ~0x01; - } - wctdm_vpm_out(wc,x,0x20,(reg | 0x20)); - - /* Initialize echo cans */ - for (i = 0 ; i < MAX_TDM_CHAN; i++) { - if (mask & (0x00000001 << i)) - wctdm_vpm_out(wc,x,i,0x00); - } - - for (i=0;i<30;i++) - schluffen(&wc->regq); - - /* Put in bypass mode */ - for (i = 0 ; i < MAX_TDM_CHAN ; i++) { - if (mask & (0x00000001 << i)) { - wctdm_vpm_out(wc,x,i,0x01); - } - } - - /* Enable bypass */ - for (i = 0 ; i < MAX_TDM_CHAN ; i++) { - if (mask & (0x00000001 << i)) - wctdm_vpm_out(wc,x,0x78 + i,0x01); - } - - /* Enable DTMF detectors (always DTMF detect all spans) */ - for (i = 0; i < 6; i++) { - if (vpmver == 0x01) - wctdm_vpm_out(wc, x, 0x98 + i, 0x40 | (i << 2) | x); - else - wctdm_vpm_out(wc, x, 0x98 + i, 0x40 | i); - } - - for (i = 0xB8; i < 0xC0; i++) - wctdm_vpm_out(wc, x, i, 0xFF); - for (i = 0xC0; i < 0xC4; i++) - wctdm_vpm_out(wc, x, i, 0xff); - - } - /* set DTMF detection threshold */ - wctdm_vpm_set_dtmf_threshold(wc, dtmfthreshold); - - if (vpmver == 0x01) - wc->vpm = 2; - else - wc->vpm = 1; -} - -#endif - -static void wctdm_locate_modules(struct wctdm *wc) -{ - int x; - unsigned long flags; - printk("Resetting the modules...\n"); - /* Initialize control register */ - wc->ctlreg = 0x00; - /* Set Reset */ - wctdm_setctl(wc, 0x0048, 0x00000000); - for (x=0;x<10;x++) - schluffen(&wc->regq); - printk("During Resetting the modules...\n"); - /* Clear reset */ - wctdm_setctl(wc, 0x0048, 0x00010000); - for (x=0;x<10;x++) - schluffen(&wc->regq); - printk("After resetting the modules...\n"); - /* Switch to caring only about receive interrupts */ - wctdm_setintmask(wc, 0x00010040); - - /* Make sure all units go into daisy chain mode */ - spin_lock_irqsave(&wc->reglock, flags); - wc->span.irqmisses = 0; - for (x=0;xcards;x++) - wc->modtype[x] = MOD_TYPE_FXSINIT; -#ifdef VPM_SUPPORT - wc->vpm = -1; - for (x=wc->cards;xcards+NUM_EC;x++) - wc->modtype[x] = MOD_TYPE_VPM; -#endif - spin_unlock_irqrestore(&wc->reglock, flags); - /* Wait just a bit */ - for (x=0;x<10;x++) - schluffen(&wc->regq); - spin_lock_irqsave(&wc->reglock, flags); - for (x=0;xcards;x++) - wc->modtype[x] = MOD_TYPE_FXS; - spin_unlock_irqrestore(&wc->reglock, flags); - -#if 0 - /* XXX */ - cmddesc = 0; -#endif - /* Now that all the cards have been reset, we can stop checking them all if there aren't as many */ - spin_lock_irqsave(&wc->reglock, flags); - wc->cards = wc->type; - spin_unlock_irqrestore(&wc->reglock, flags); - - /* Reset modules */ - for (x=0;xcards;x++) { - int sane=0,ret=0,readi=0; -retry: - /* 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("Port %d: Installed -- AUTO FXS/DPO\n", x + 1); - } else { - if(ret!=-2) { - sane=1; - /* 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("Port %d: Installed -- MANUAL FXS\n",x + 1); - } else { - printk("Port %d: FAILED FXS (%s)\n", x + 1, fxshonormode ? fxo_modes[_opermode].name : "FCC"); - } - } else if (!(ret = wctdm_init_voicedaa(wc, x, 0, 0, sane))) { - wc->cardflag |= (1 << x); - printk("Port %d: Installed -- AUTO FXO (%s mode)\n",x + 1, fxo_modes[_opermode].name); - } else if (!wctdm_init_qrvdri(wc,x)) { - wc->cardflag |= 1 << x; - printk("Port %d: Installed -- QRV DRI card\n",x + 1); - } else { - if ((wc->type == 8) && ((x & 0x3) == 1) && !wc->altcs[x]) { - spin_lock_irqsave(&wc->reglock, flags); - wc->modtype[x] = MOD_TYPE_FXSINIT; - wc->altcs[x] = 2; - spin_unlock_irqrestore(&wc->reglock, flags); - schluffen(&wc->regq); - spin_lock_irqsave(&wc->reglock, flags); - wc->modtype[x] = MOD_TYPE_FXS; - spin_unlock_irqrestore(&wc->reglock, flags); - if (debug) - printk("Trying port %d with alternate chip select\n", x + 1); - goto retry; - } else { - printk("Port %d: Not installed\n", x + 1); - wc->modtype[x] = MOD_TYPE_NONE; - wc->cardflag |= (1 << x); - } - } - } - } -#ifdef VPM_SUPPORT - wctdm_vpm_init(wc); - if (wc->vpm) { - printk("VPM: Present and operational (Rev %c)\n", 'A' + wc->vpm - 1); - wc->ctlreg |= 0x10; - } -#endif -} - -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) { - printk("Too many interfaces\n"); - return -EIO; - } - - if (pci_enable_device(pdev)) { - res = -EIO; - } else { - wc = kmalloc(sizeof(struct wctdm), GFP_KERNEL); - if (wc) { - ifaces[x] = wc; - memset(wc, 0, sizeof(struct wctdm)); - spin_lock_init(&wc->reglock); - wc->curcard = -1; - wc->cards = NUM_CARDS; - wc->iobase = pci_resource_start(pdev, 0); - wc->type = d->ports; - wc->dev = pdev; - wc->pos = x; - wc->variety = d->name; - for (y=0;yflags[y] = d->flags; - wc->dacssrc[y] = -1; - } - /* Keep track of whether we need to free the region */ - if (request_region(wc->iobase, 0xff, "wctdm24xxp")) - wc->freeregion = 1; - - /* Allocate enough memory for two zt chunks, receive and transmit. Each sample uses - 32 bits. Allocate an extra set just for control too */ - wc->writechunk = pci_alloc_consistent(pdev, PCI_WINDOW_SIZE, &wc->writedma); - if (!wc->writechunk) { - printk("wctdm: Unable to allocate DMA-able memory\n"); - if (wc->freeregion) - release_region(wc->iobase, 0xff); - return -ENOMEM; - } - - wc->readchunk = wc->writechunk + SFRAME_SIZE / 2; /* in doublewords */ - wc->readdma = wc->writedma + SFRAME_SIZE * 2; /* in bytes */ - - wc->descripchunk = wc->readchunk + SFRAME_SIZE / 2; /* in doublewords */ - wc->descripdma = wc->readdma + SFRAME_SIZE * 2; /* in bytes */ - - /* Initialize Write/Buffers to all blank data */ - memset((void *)wc->writechunk,0x00, SFRAME_SIZE * 2); - memset((void *)wc->readchunk, 0x00, SFRAME_SIZE * 2); - - init_waitqueue_head(&wc->regq); - - if (wctdm_initialize(wc)) { - printk("%s: Unable to register span with zaptel\n", wc->variety); - /* Set Reset Low */ - wctdm_stop_dma(wc); - /* Free Resources */ - free_irq(pdev->irq, wc); - if (wc->freeregion) - release_region(wc->iobase, 0xff); - pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *)wc->writechunk, wc->writedma); - kfree(wc); - return -EIO; - } - - /* 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, SA_SHIRQ, wc->variety, wc)) { - printk("wctdm24xxp: Unable to request IRQ %d\n", pdev->irq); - if (wc->freeregion) - release_region(wc->iobase, 0xff); - pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *)wc->writechunk, wc->writedma); - pci_set_drvdata(pdev, NULL); - kfree(wc); - return -EIO; - } - - - if (wctdm_hardware_init(wc)) { - /* Set Reset Low */ - wctdm_stop_dma(wc); - /* Free Resources */ - free_irq(pdev->irq, wc); - if (wc->freeregion) - release_region(wc->iobase, 0xff); - pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *)wc->writechunk, wc->writedma); - pci_set_drvdata(pdev, NULL); - zt_unregister(&wc->span); - kfree(wc); - return -EIO; - - } - - - /* Enable interrupts */ - wctdm_enable_interrupts(wc); - - /* Start DMA */ - wctdm_start_dma(wc); - - /* Now track down what modules are installed */ - wctdm_locate_modules(wc); - - /* Final initialization */ - wctdm_post_initialize(wc); - - printk("Found a Wildcard TDM: %s (%d modules)\n", wc->variety, wc->type); - res = 0; - } else - res = -ENOMEM; - } - return res; -} - -static void wctdm_release(struct wctdm *wc) -{ - zt_unregister(&wc->span); - if (wc->freeregion) - release_region(wc->iobase, 0xff); - kfree(wc); - printk("Freed a Wildcard\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); - - /* In case hardware is still there */ - wctdm_disable_interrupts(wc); - - /* Immediately free resources */ - pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *)wc->writechunk, wc->writedma); - free_irq(pdev->irq, wc); - - /* Release span, possibly delayed */ - if (!wc->usecount) - wctdm_release(wc); - else - wc->dead = 1; - } -} - -static struct pci_device_id wctdm_pci_tbl[] = { - { 0xd161, 0x2400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wctdm2400 }, - { 0xd161, 0x0800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wctdm800 }, - { 0 } -}; - -MODULE_DEVICE_TABLE(pci, wctdm_pci_tbl); - -static struct pci_driver wctdm_driver = { - name: "wctdm24xxp", - probe: wctdm_init_one, -#ifdef LINUX26 - remove: __devexit_p(wctdm_remove_one), -#else - remove: wctdm_remove_one, -#endif - 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("Invalid/unknown operating mode '%s' specified. Please choose one of:\n", opermode); - for (x=0;x"); -#if defined(MODULE_ALIAS) -MODULE_ALIAS("wctdm8xxp"); -#endif -#ifdef MODULE_LICENSE -MODULE_LICENSE("GPL"); -#endif - -module_init(wctdm_init); -module_exit(wctdm_cleanup); - - - diff --git a/wctdm24xxp/GpakApi.c b/wctdm24xxp/GpakApi.c new file mode 100644 index 0000000..2abb67a --- /dev/null +++ b/wctdm24xxp/GpakApi.c @@ -0,0 +1,1609 @@ +/* + * Copyright (c) 2005, Adaptive Digital Technologies, Inc. + * + * File Name: GpakApi.c + * + * Description: + * This file contains user API functions to communicate with DSPs executing + * G.PAK software. The file is integrated into the host processor connected + * to C55X G.PAK DSPs via a Host Port Interface. + * + * Version: 1.0 + * + * Revision History: + * 06/15/05 - Initial release. + * 11/15/2006 - 24 TDM-TDM Channels EC release + */ + +#include "GpakHpi.h" +#include "GpakCust.h" +#include "GpakApi.h" +#include "gpakenum.h" + +/* DSP to Host interface block offsets. */ +#define REPLY_MSG_PNTR_OFFSET 0 /* I/F blk offset to Reply Msg Pointer */ +#define CMD_MSG_PNTR_OFFSET 2 /* I/F blk offset to Command Msg Pointer */ +#define EVENT_MSG_PNTR_OFFSET 4 /* I/F blk offset to Event Msg Pointer */ +#define PKT_BUFR_MEM_OFFSET 6 /* I/F blk offset to Packet Buffer memory */ +#define DSP_STATUS_OFFSET 8 /* I/F blk offset to DSP Status */ +#define VERSION_ID_OFFSET 9 /* I/F blk offset to G.PAK Version Id */ +#define MAX_CMD_MSG_LEN_OFFSET 10 /* I/F blk offset to Max Cmd Msg Length */ +#define CMD_MSG_LEN_OFFSET 11 /* I/F blk offset to Command Msg Length */ +#define REPLY_MSG_LEN_OFFSET 12 /* I/F blk offset to Reply Msg Length */ +#define NUM_CHANNELS_OFFSET 13 /* I/F blk offset to Num Built Channels */ +#define NUM_PKT_CHANNELS_OFFSET 14 /* I/F blk offset to Num Pkt Channels */ +#define NUM_CONFERENCES_OFFSET 15 /* I/F blk offset to Num Conferences */ +//#define CPU_USAGE_OFFSET_1MS 16 /* I/F blk offset to CPU Usage statistics */ +#define CPU_USAGE_OFFSET 18 /* I/F blk offset to CPU Usage statistics */ +//#define CPU_USAGE_OFFSET_10MS 20 /* I/F blk offset to CPU Usage statistics */ +#define FRAMING_STATS_OFFSET 22 /* I/F blk offset to Framing statistics */ + +//#define GPAK_RELEASE_Rate rate10ms +// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +// Macro to reconstruct a 32-bit value from two 16-bit values. +// Parameter p32: 32-bit-wide destination +// Parameter p16: 16-bit-wide source array of length 2 words +#define RECONSTRUCT_LONGWORD(p32, p16) p32 = (DSP_ADDRESS)p16[0]<<16; \ + p32 |= (unsigned long)p16[1] +// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = + +/* DSP Status value definitions. */ +#define DSP_INIT_STATUS 0x5555 /* DSP Initialized status value */ +#define HOST_INIT_STATUS 0xAAAA /* Host Initialized status value */ + +/* Circular packet buffer information structure offsets. */ +#define CB_BUFR_BASE 0 /* pointer to base of circular buffer */ +#define CB_BUFR_SIZE 2 /* size of buffer (words) */ +#define CB_BUFR_PUT_INDEX 3 /* offset in buffer for next write */ +#define CB_BUFR_TAKE_INDEX 4 /* offset in buffer for next read */ +#define CIRC_BUFFER_INFO_STRUCT_SIZE 6 + +/* Miscellaneous definitions. */ +#define MSG_BUFFER_SIZE 100 /* size (words) of Host msg buffer */ +#define WORD_BUFFER_SIZE 84 /* size of DSP Word buffer (words) */ + +#ifdef __TMS320C55XX__ // debug sections if not on host +#pragma DATA_SECTION(pDspIfBlk,"GPAKAPIDEBUG_SECT") +#pragma DATA_SECTION(MaxCmdMsgLen,"GPAKAPIDEBUG_SECT") +#pragma DATA_SECTION(MaxChannels,"GPAKAPIDEBUG_SECT") +#pragma DATA_SECTION(DlByteBufr,"GPAKAPIDEBUG_SECT") +#pragma DATA_SECTION(DlWordBufr,"GPAKAPIDEBUG_SECT") +#pragma DATA_SECTION(pEventFifoAddress,"GPAKAPIDEBUG_SECT") +#endif + +/* Host variables related to Host to DSP interface. */ +static DSP_ADDRESS pDspIfBlk[MAX_DSP_CORES]; /* DSP address of I/F block */ +static DSP_WORD MaxCmdMsgLen[MAX_DSP_CORES]; /* max Cmd msg length (octets) */ +static unsigned short int MaxChannels[MAX_DSP_CORES]; /* max num channels */ + +//static unsigned short int MaxPktChannels[MAX_DSP_CORES]; /* max num pkt channels */ +//static unsigned short int MaxConfs[MAX_DSP_CORES]; /* max num conferences */ +//static DSP_ADDRESS pPktInBufr[MAX_DSP_CORES][MAX_PKT_CHANNELS]; /* Pkt In buffer */ +//static DSP_ADDRESS pPktOutBufr[MAX_DSP_CORES][MAX_PKT_CHANNELS]; /* Pkt Out buffer */ +static DSP_ADDRESS pEventFifoAddress[MAX_DSP_CORES]; /* event fifo */ + +static unsigned char DlByteBufr[DOWNLOAD_BLOCK_SIZE * 2]; /* Dowload byte buf */ +static DSP_WORD DlWordBufr[DOWNLOAD_BLOCK_SIZE]; /* Dowload word buffer */ + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * CheckDspReset - Check if the DSP was reset. + * + * FUNCTION + * This function determines if the DSP was reset and is ready. If reset + * occurred, it reads interface parameters and calculates DSP addresses. + * + * RETURNS + * -1 = DSP is not ready. + * 0 = Reset did not occur. + * 1 = Reset occurred. + * + */ +static int CheckDspReset( + int DspId /* DSP Identifier (0 to MaxDSPCores-1) */ + ) +{ + DSP_ADDRESS IfBlockPntr; /* Interface Block pointer */ + DSP_WORD DspStatus; /* DSP Status */ + DSP_WORD DspChannels; /* number of DSP channels */ + DSP_WORD DspConfs; /* number of DSP conferences */ + DSP_ADDRESS PktBufrMem; /* address of Packet Buffer */ + DSP_WORD Temp[2]; + unsigned short int i; /* loop index / counter */ + + /* Read the pointer to the Interface Block. */ + gpakReadDspMemory(DspId, DSP_IFBLK_ADDRESS, 2, Temp); + RECONSTRUCT_LONGWORD(IfBlockPntr, Temp); + + /* If the pointer is zero, return with an indication the DSP is not + ready. */ + if (IfBlockPntr == 0) + return (-1); + + /* Read the DSP's Status. */ + gpakReadDspMemory(DspId, IfBlockPntr + DSP_STATUS_OFFSET, 1, &DspStatus); + + /* If status indicates the DSP was reset, read the DSP's interface + parameters and calculate DSP addresses. */ + if (DspStatus == DSP_INIT_STATUS || + ((DspStatus == HOST_INIT_STATUS) && (pDspIfBlk[DspId] == 0))) + { + /* Save the address of the DSP's Interface Block. */ + pDspIfBlk[DspId] = IfBlockPntr; + + /* Read the DSP's interface parameters. */ + gpakReadDspMemory(DspId, IfBlockPntr + MAX_CMD_MSG_LEN_OFFSET, 1, + &(MaxCmdMsgLen[DspId])); + + /* read the number of configured DSP channels */ + gpakReadDspMemory(DspId, IfBlockPntr + NUM_CHANNELS_OFFSET, 1, + &DspChannels); + if (DspChannels > MAX_CHANNELS) + MaxChannels[DspId] = MAX_CHANNELS; + else + MaxChannels[DspId] = (unsigned short int) DspChannels; +#if 0 + /* read the number of configured DSP conferences */ + gpakReadDspMemory(DspId, IfBlockPntr + NUM_CONFERENCES_OFFSET, 1, + &DspConfs); + if (DspConfs > MAX_CONFS) + MaxConfs[DspId] = MAX_CONFS; + else + MaxConfs[DspId] = (unsigned short int) DspConfs; + + + /* read the number of configured DSP packet channels */ + gpakReadDspMemory(DspId, IfBlockPntr + NUM_PKT_CHANNELS_OFFSET, 1, + &DspChannels); + if (DspChannels > MAX_PKT_CHANNELS) + MaxPktChannels[DspId] = MAX_PKT_CHANNELS; + else + MaxPktChannels[DspId] = (unsigned short int) DspChannels; + + + /* read the pointer to the circular buffer infor struct table */ + gpakReadDspMemory(DspId, IfBlockPntr + PKT_BUFR_MEM_OFFSET, 2, Temp); + RECONSTRUCT_LONGWORD(PktBufrMem, Temp); + + + /* Determine the addresses of each channel's Packet buffers. */ + for (i = 0; i < MaxPktChannels[DspId]; i++) + { + pPktInBufr[DspId][i] = PktBufrMem; + pPktOutBufr[DspId][i] = PktBufrMem + CIRC_BUFFER_INFO_STRUCT_SIZE; + PktBufrMem += (CIRC_BUFFER_INFO_STRUCT_SIZE*2); + } +#endif + + /* read the pointer to the event fifo info struct */ + gpakReadDspMemory(DspId, IfBlockPntr + EVENT_MSG_PNTR_OFFSET, 2, Temp); + RECONSTRUCT_LONGWORD(pEventFifoAddress[DspId], Temp); + + /* Set the DSP Status to indicate the host recognized the reset. */ + DspStatus = HOST_INIT_STATUS; + gpakWriteDspMemory(DspId, IfBlockPntr + DSP_STATUS_OFFSET, 1, + &DspStatus); + + /* Return with an indication that a reset occurred. */ + return (1); + } + + /* If status doesn't indicate the host recognized a reset, return with an + indication the DSP is not ready. */ + if ((DspStatus != HOST_INIT_STATUS) || (pDspIfBlk[DspId] == 0)) + return (-1); + + /* Return with an indication that a reset did not occur. */ + return (0); +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * WriteDspCmdMessage - Write a Host Command/Request message to DSP. + * + * FUNCTION + * This function writes a Host Command/Request message into DSP memory and + * informs the DSP of the presence of the message. + * + * RETURNS + * -1 = Unable to write message (msg len or DSP Id invalid or DSP not ready) + * 0 = Temporarily unable to write message (previous Cmd Msg busy) + * 1 = Message written successfully + * + */ +static int WriteDspCmdMessage( + int DspId, /* DSP Identifier (0 to MaxDSPCores-1) */ + DSP_WORD *pMessage, /* pointer to Command message */ + DSP_WORD MsgLength /* length of message (octets) */ + ) +{ + DSP_WORD CmdMsgLength; /* current Cmd message length */ + DSP_WORD Temp[2]; + DSP_ADDRESS BufferPointer; /* message buffer pointer */ + + /* Check if the DSP was reset and is ready. */ + if (CheckDspReset(DspId) == -1) + return (-1); + + /* Make sure the message length is valid. */ + if ((MsgLength < 1) || (MsgLength > MaxCmdMsgLen[DspId])) + return (-1); + + /* Make sure a previous Command message is not in use by the DSP. */ + gpakReadDspMemory(DspId, pDspIfBlk[DspId] + CMD_MSG_LEN_OFFSET, 1, + &CmdMsgLength); + if (CmdMsgLength != 0) + return (0); + + /* Purge any previous Reply message that wasn't read. */ + gpakWriteDspMemory(DspId, pDspIfBlk[DspId] + REPLY_MSG_LEN_OFFSET, 1, + &CmdMsgLength); + + /* Copy the Command message into DSP memory. */ + gpakReadDspMemory(DspId, pDspIfBlk[DspId] + CMD_MSG_PNTR_OFFSET, 2, Temp); + RECONSTRUCT_LONGWORD(BufferPointer, Temp); + gpakWriteDspMemory(DspId, BufferPointer, (MsgLength + 1) / 2, pMessage); + + /* Store the message length in DSP's Command message length (flags DSP that + a Command message is ready). */ + CmdMsgLength = MsgLength; + gpakWriteDspMemory(DspId, pDspIfBlk[DspId] + CMD_MSG_LEN_OFFSET, 1, + &CmdMsgLength); + + /* Return with an indication the message was written. */ + return (1); +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * ReadDspReplyMessage - Read a DSP Reply message from DSP. + * + * FUNCTION + * This function reads a DSP Reply message from DSP memory. + * + * RETURNS + * -1 = Unable to write message (msg len or DSP Id invalid or DSP not ready) + * 0 = No message available (DSP Reply message empty) + * 1 = Message read successfully (message and length stored in variables) + * + */ +static int ReadDspReplyMessage( + int DspId, /* DSP Identifier (0 to MaxDSPCores-1) */ + DSP_WORD *pMessage, /* pointer to Reply message buffer */ + DSP_WORD *pMsgLength /* pointer to msg length var (octets) */ + ) +{ + DSP_WORD MsgLength; /* message length */ + DSP_ADDRESS BufferPointer; /* message buffer pointer */ + DSP_WORD Temp[2]; + + /* Check if the DSP was reset and is ready. */ + if (CheckDspReset(DspId) == -1) + return (-1); + + /* Check if a Reply message is ready. */ + gpakReadDspMemory(DspId, pDspIfBlk[DspId] + REPLY_MSG_LEN_OFFSET, 1, + &MsgLength); + if (MsgLength == 0) + return (0); + + /* Make sure the message length is valid. */ + if (MsgLength > *pMsgLength) + return (-1); + + /* Copy the Reply message from DSP memory. */ + gpakReadDspMemory(DspId, pDspIfBlk[DspId] + REPLY_MSG_PNTR_OFFSET, 2, Temp); + RECONSTRUCT_LONGWORD(BufferPointer, Temp); + gpakReadDspMemory(DspId, BufferPointer, (MsgLength + 1) / 2, pMessage); + + /* Store the message length in the message length variable. */ + *pMsgLength = MsgLength; + + /* Indicate a Reply message is not ready. */ + MsgLength = 0; + gpakWriteDspMemory(DspId, pDspIfBlk[DspId] + REPLY_MSG_LEN_OFFSET, 1, + &MsgLength); + + /* Return with an indication the message was read. */ + return (1); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * ReadCircBuffer - Read from a DSP circular buffer. + * + * FUNCTION + * This function reads a block of words from a DSP circular buffer. The Take + * address is incremented by the number of words read adjusting for buffer + * wrap. + * + * RETURNS + * nothing + * + */ +static void ReadCircBuffer( + int DspId, /* DSP Identifier (0 to MaxDSPCores-1) */ + DSP_ADDRESS BufrBaseAddress, /* address of base of circular buffer */ + DSP_ADDRESS BufrLastAddress, /* address of last word in buffer */ + DSP_ADDRESS *TakeAddress, /* pointer to address in buffer for read */ + DSP_WORD *pWordBuffer, /* pointer to buffer for words read */ + DSP_WORD NumWords /* number of words to read */ + ) +{ + DSP_WORD WordsTillEnd; /* number of words until end of buffer */ + + /* Determine the number of words from the start address until the end of the + buffer. */ + WordsTillEnd = BufrLastAddress - *TakeAddress + 1; + + /* If a buffer wrap will occur, read the first part at the end of the + buffer followed by the second part at the beginning of the buffer. */ + if (NumWords > WordsTillEnd) + { + gpakReadDspMemory(DspId, *TakeAddress, WordsTillEnd, pWordBuffer); + gpakReadDspMemory(DspId, BufrBaseAddress, NumWords - WordsTillEnd, + &(pWordBuffer[WordsTillEnd])); + *TakeAddress = BufrBaseAddress + NumWords - WordsTillEnd; + } + + /* If a buffer wrap will not occur, read all words starting at the current + take address in the buffer. */ + else + { + gpakReadDspMemory(DspId, *TakeAddress, NumWords, pWordBuffer); + if (NumWords == WordsTillEnd) + *TakeAddress = BufrBaseAddress; + else + *TakeAddress = *TakeAddress + NumWords; + } + return; +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * VerifyReply - Verify the reply message is correct for the command sent. + * + * FUNCTION + * This function verifies correct reply message content for the command that + * was just sent. + * + * RETURNS + * 0 = Incorrect + * 1 = Correct + * + */ +static int VerifyReply( + DSP_WORD *pMsgBufr, /* pointer to Reply message buffer */ + int CheckType, /* reply check type */ + DSP_WORD CheckValue /* reply check value */ + ) +{ + + /* Verify Channel or Conference Id. */ + if (CheckType == 1) + { + if (((pMsgBufr[1] >> 8) & 0xFF) != CheckValue) + return (0); + } + + /* Verify Test Mode Id. */ + else if (CheckType == 2) + { + if (pMsgBufr[1] != CheckValue) + return (0); + } + + /* Return with an indication of correct reply. */ + return (1); +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * TransactCmd - Send a command to the DSP and receive it's reply. + * + * FUNCTION + * This function sends the specified command to the DSP and receives the DSP's + * reply. + * + * RETURNS + * Length of reply message (0 = Failure) + * + */ +static unsigned int TransactCmd( + int DspId, /* DSP Identifier (0 to MaxDSPCores-1) */ + DSP_WORD *pMsgBufr, /* pointer to Cmd/Reply message buffer */ + DSP_WORD CmdLength, /* length of command message (octets) */ + DSP_WORD ReplyType, /* required type of reply message */ + DSP_WORD ReplyLength, /* required length of reply message (octets) */ + int ReplyCheckType, /* reply check type */ + DSP_WORD ReplyCheckValue /* reply check value */ + ) +{ + int FuncStatus; /* function status */ + int LoopCount; /* wait loop counter */ + DSP_WORD RcvReplyLength; /* received Reply message length */ + DSP_WORD RcvReplyType; /* received Reply message type code */ + DSP_WORD RetValue; /* return value */ + + /* Default the return value to indicate a failure. */ + RetValue = 0; + + /* Lock access to the DSP. */ + gpakLockAccess(DspId); + + /* Attempt to write the command message to the DSP. */ + LoopCount = 0; + while ((FuncStatus = WriteDspCmdMessage(DspId, pMsgBufr, CmdLength)) != 1) + { + if (FuncStatus == -1) + break; + if (++LoopCount > MAX_WAIT_LOOPS) + break; + gpakHostDelay(); + } + + /* Attempt to read the reply message from the DSP if the command message was + sent successfully. */ + if (FuncStatus == 1) + { + for (LoopCount = 0; LoopCount < MAX_WAIT_LOOPS; LoopCount++) + { + RcvReplyLength = MSG_BUFFER_SIZE * 2; + FuncStatus = ReadDspReplyMessage(DspId, pMsgBufr, &RcvReplyLength); + if (FuncStatus == 1) + { + RcvReplyType = (pMsgBufr[0] >> 8) & 0xFF; + if ((RcvReplyLength >= ReplyLength) && + (RcvReplyType == ReplyType) && + VerifyReply(pMsgBufr, ReplyCheckType, ReplyCheckValue)) + { + RetValue = RcvReplyLength; + break; + } + else if (RcvReplyType == MSG_NULL_REPLY) + break; + } + else if (FuncStatus == -1) + break; + gpakHostDelay(); + } + } + + /* Unlock access to the DSP. */ + gpakUnlockAccess(DspId); + + /* Return the length of the reply message (0 = failure). */ + return (RetValue); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakConfigurePorts - Configure a DSP's serial ports. + * + * FUNCTION + * This function configures a DSP's serial ports. + * + * RETURNS + * Status code indicating success or a specific error. + * + */ +gpakConfigPortStatus_t gpakConfigurePorts( + unsigned short int DspId, /* DSP Id (0 to MaxDSPCores-1) */ + GpakPortConfig_t *pPortConfig, /* pointer to Port Config info */ + GPAK_PortConfigStat_t *pStatus /* pointer to Port Config Status */ + ) +{ + DSP_WORD MsgBuffer[MSG_BUFFER_SIZE]; /* message buffer */ + + /* Make sure the DSP Id is valid. */ + if (DspId >= MAX_DSP_CORES) + return (CpsInvalidDsp); + + /* Build the Configure Serial Ports message. */ + MsgBuffer[0] = MSG_CONFIGURE_PORTS << 8; + MsgBuffer[1] = (DSP_WORD) + ((pPortConfig->SlotsSelect1 << 12) | + ((pPortConfig->FirstBlockNum1 << 8) & 0x0F00) | + ((pPortConfig->SecBlockNum1 << 4) & 0x00F0)); + MsgBuffer[2] = (DSP_WORD) pPortConfig->FirstSlotMask1; + MsgBuffer[3] = (DSP_WORD) pPortConfig->SecSlotMask1; + MsgBuffer[4] = (DSP_WORD) + ((pPortConfig->SlotsSelect2 << 12) | + ((pPortConfig->FirstBlockNum2 << 8) & 0x0F00) | + ((pPortConfig->SecBlockNum2 << 4) & 0x00F0)); + MsgBuffer[5] = (DSP_WORD) pPortConfig->FirstSlotMask2; + MsgBuffer[6] = (DSP_WORD) pPortConfig->SecSlotMask2; + MsgBuffer[7] = (DSP_WORD) + ((pPortConfig->SlotsSelect3 << 12) | + ((pPortConfig->FirstBlockNum3 << 8) & 0x0F00) | + ((pPortConfig->SecBlockNum3 << 4) & 0x00F0)); + MsgBuffer[8] = (DSP_WORD) pPortConfig->FirstSlotMask3; + MsgBuffer[9] = (DSP_WORD) pPortConfig->SecSlotMask3; + + MsgBuffer[10] = (DSP_WORD) + (((pPortConfig->DxDelay1 << 11) & 0x0800) | + ((pPortConfig->RxDataDelay1 << 9) & 0x0600) | + ((pPortConfig->TxDataDelay1 << 7) & 0x0180) | + ((pPortConfig->RxClockPolarity1 << 6) & 0x0040) | + ((pPortConfig->TxClockPolarity1 << 5) & 0x0020) | + ((pPortConfig->RxFrameSyncPolarity1 << 4) & 0x0010) | + ((pPortConfig->TxFrameSyncPolarity1 << 3) & 0x0008) | + ((pPortConfig->CompandingMode1 << 1) & 0x0006) | + (pPortConfig->SerialWordSize1 & 0x0001)); + + MsgBuffer[11] = (DSP_WORD) + (((pPortConfig->DxDelay2 << 11) & 0x0800) | + ((pPortConfig->RxDataDelay2 << 9) & 0x0600) | + ((pPortConfig->TxDataDelay2 << 7) & 0x0180) | + ((pPortConfig->RxClockPolarity2 << 6) & 0x0040) | + ((pPortConfig->TxClockPolarity2 << 5) & 0x0020) | + ((pPortConfig->RxFrameSyncPolarity2 << 4) & 0x0010) | + ((pPortConfig->TxFrameSyncPolarity2 << 3) & 0x0008) | + ((pPortConfig->CompandingMode2 << 1) & 0x0006) | + (pPortConfig->SerialWordSize1 & 0x0001)); + + MsgBuffer[12] = (DSP_WORD) + (((pPortConfig->DxDelay3 << 11) & 0x0800) | + ((pPortConfig->RxDataDelay3 << 9) & 0x0600) | + ((pPortConfig->TxDataDelay3 << 7) & 0x0180) | + ((pPortConfig->RxClockPolarity3 << 6) & 0x0040) | + ((pPortConfig->TxClockPolarity3 << 5) & 0x0020) | + ((pPortConfig->RxFrameSyncPolarity3 << 4) & 0x0010) | + ((pPortConfig->TxFrameSyncPolarity3 << 3) & 0x0008) | + ((pPortConfig->CompandingMode3 << 1) & 0x0006) | + (pPortConfig->SerialWordSize3 & 0x0001)); + + MsgBuffer[13] = (DSP_WORD) pPortConfig->ThirdSlotMask1; + MsgBuffer[14] = (DSP_WORD) pPortConfig->FouthSlotMask1; + MsgBuffer[15] = (DSP_WORD) pPortConfig->FifthSlotMask1; + MsgBuffer[16] = (DSP_WORD) pPortConfig->SixthSlotMask1; + MsgBuffer[17] = (DSP_WORD) pPortConfig->SevenSlotMask1; + MsgBuffer[18] = (DSP_WORD) pPortConfig->EightSlotMask1; + + MsgBuffer[19] = (DSP_WORD) pPortConfig->ThirdSlotMask2;; + MsgBuffer[20] = (DSP_WORD) pPortConfig->FouthSlotMask2; + MsgBuffer[21] = (DSP_WORD) pPortConfig->FifthSlotMask2;; + MsgBuffer[22] = (DSP_WORD) pPortConfig->SixthSlotMask2; + MsgBuffer[23] = (DSP_WORD) pPortConfig->SevenSlotMask2;; + MsgBuffer[24] = (DSP_WORD) pPortConfig->EightSlotMask2; + + MsgBuffer[25] = (DSP_WORD) pPortConfig->ThirdSlotMask3;; + MsgBuffer[26] = (DSP_WORD) pPortConfig->FouthSlotMask3; + MsgBuffer[27] = (DSP_WORD) pPortConfig->FifthSlotMask3;; + MsgBuffer[28] = (DSP_WORD) pPortConfig->SixthSlotMask3; + MsgBuffer[29] = (DSP_WORD) pPortConfig->SevenSlotMask3;; + MsgBuffer[30] = (DSP_WORD) pPortConfig->EightSlotMask3; + + + /* Attempt to send the Configure Serial Ports message to the DSP and receive + it's reply. */ + if (!TransactCmd(DspId, MsgBuffer, 62, MSG_CONFIG_PORTS_REPLY, 4, 0, 0)) + return (CpsDspCommFailure); + + /* Return with an indication of success or failure based on the return + status in the reply message. */ + *pStatus = (GPAK_PortConfigStat_t) (MsgBuffer[1] & 0xFF); + if (*pStatus == Pc_Success) + return (CpsSuccess); + else + return (CpsParmError); +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakConfigureChannel - Configure a DSP's Channel. + * + * FUNCTION + * This function configures a DSP's Channel. + * + * RETURNS + * Status code indicating success or a specific error. + * + */ +gpakConfigChanStatus_t gpakConfigureChannel( + unsigned short int DspId, /* DSP Id (0 to MaxDSPCores-1) */ + unsigned short int ChannelId, /* Channel Id (0 to MaxChannels-1) */ + GpakChanType ChannelType, /* Channel Type */ + GpakChannelConfig_t *pChanConfig, /* pointer to Channel Config info */ + GPAK_ChannelConfigStat_t *pStatus /* pointer to Channel Config Status */ + ) +{ + DSP_WORD MsgBuffer[MSG_BUFFER_SIZE]; /* message buffer */ + DSP_WORD MsgLength; /* message length */ + + /* Make sure the DSP Id is valid. */ + if (DspId >= MAX_DSP_CORES) + return (CcsInvalidDsp); + + /* Make sure the Channel Id is valid. */ + if (ChannelId >= MaxChannels[DspId]) + return (CcsInvalidChannel); + + /* Build the Configure Channel message based on the Channel Type. */ + switch (ChannelType) + { + + /* PCM to Packet channel type. */ + case tdmToTdm: + + MsgBuffer[2] = (DSP_WORD) + ((pChanConfig->PcmInPortA << 8) | + (pChanConfig->PcmInSlotA & 0xFF)); + MsgBuffer[3] = (DSP_WORD) + ((pChanConfig->PcmOutPortA << 8) | + (pChanConfig->PcmOutSlotA & 0xFF)); + + MsgBuffer[4] = (DSP_WORD) + ((pChanConfig->PcmInPortB << 8) | + (pChanConfig->PcmInSlotB & 0xFF)); + MsgBuffer[5] = (DSP_WORD) + ((pChanConfig->PcmOutPortB << 8) | + (pChanConfig->PcmOutSlotB & 0xFF)); + + MsgBuffer[6] = (DSP_WORD) + ( + ((pChanConfig->FaxCngDetB <<11) & 0x0800) | + ((pChanConfig->FaxCngDetA <<10) & 0x0400) | + ((pChanConfig->MuteToneB << 9) & 0x0200) | + ((pChanConfig->MuteToneA << 8) & 0x0100) | + ((pChanConfig->FrameRate << 6) & 0x00C0) | + ((pChanConfig->ToneTypesB << 5) & 0x0020) | + ((pChanConfig->ToneTypesA << 4) & 0x0010) | + ((pChanConfig->SoftwareCompand & 3) << 2) | + (pChanConfig->EcanEnableB << 1) | + (pChanConfig->EcanEnableA & 1) + ); + + MsgBuffer[7] = (DSP_WORD) + pChanConfig->EcanParametersA.EcanTapLength; + MsgBuffer[8] = (DSP_WORD) + pChanConfig->EcanParametersA.EcanNlpType; + MsgBuffer[9] = (DSP_WORD) + pChanConfig->EcanParametersA.EcanAdaptEnable; + MsgBuffer[10] = (DSP_WORD) + pChanConfig->EcanParametersA.EcanG165DetEnable; + MsgBuffer[11] = (DSP_WORD) + pChanConfig->EcanParametersA.EcanDblTalkThresh; + MsgBuffer[12] = (DSP_WORD) + pChanConfig->EcanParametersA.EcanNlpThreshold; + MsgBuffer[13] = (DSP_WORD) + pChanConfig->EcanParametersA.EcanNlpConv; + MsgBuffer[14] = (DSP_WORD) + pChanConfig->EcanParametersA.EcanNlpUnConv; + MsgBuffer[15] = (DSP_WORD) + pChanConfig->EcanParametersA.EcanNlpMaxSuppress; + + MsgBuffer[16] = (DSP_WORD) + pChanConfig->EcanParametersA.EcanCngThreshold; + MsgBuffer[17] = (DSP_WORD) + pChanConfig->EcanParametersA.EcanAdaptLimit; + MsgBuffer[18] = (DSP_WORD) + pChanConfig->EcanParametersA.EcanCrossCorrLimit; + MsgBuffer[19] = (DSP_WORD) + pChanConfig->EcanParametersA.EcanNumFirSegments; + MsgBuffer[20] = (DSP_WORD) + pChanConfig->EcanParametersA.EcanFirSegmentLen; + + MsgBuffer[21] = (DSP_WORD) + pChanConfig->EcanParametersB.EcanTapLength; + MsgBuffer[22] = (DSP_WORD) + pChanConfig->EcanParametersB.EcanNlpType; + MsgBuffer[23] = (DSP_WORD) + pChanConfig->EcanParametersB.EcanAdaptEnable; + MsgBuffer[24] = (DSP_WORD) + pChanConfig->EcanParametersB.EcanG165DetEnable; + MsgBuffer[25] = (DSP_WORD) + pChanConfig->EcanParametersB.EcanDblTalkThresh; + MsgBuffer[26] = (DSP_WORD) + pChanConfig->EcanParametersB.EcanNlpThreshold; + MsgBuffer[27] = (DSP_WORD) + pChanConfig->EcanParametersB.EcanNlpConv; + MsgBuffer[28] = (DSP_WORD) + pChanConfig->EcanParametersB.EcanNlpUnConv; + MsgBuffer[29] = (DSP_WORD) + pChanConfig->EcanParametersB.EcanNlpMaxSuppress; + MsgBuffer[30] = (DSP_WORD) + pChanConfig->EcanParametersB.EcanCngThreshold; + MsgBuffer[31] = (DSP_WORD) + pChanConfig->EcanParametersB.EcanAdaptLimit; + MsgBuffer[32] = (DSP_WORD) + pChanConfig->EcanParametersB.EcanCrossCorrLimit; + MsgBuffer[33] = (DSP_WORD) + pChanConfig->EcanParametersB.EcanNumFirSegments; + MsgBuffer[34] = (DSP_WORD) + pChanConfig->EcanParametersB.EcanFirSegmentLen; + + MsgLength = 70; // byte number == 35*2 + break; + + + /* Unknown (invalid) channel type. */ + default: + *pStatus = Cc_InvalidChannelType; + return (CcsParmError); + } + + MsgBuffer[0] = MSG_CONFIGURE_CHANNEL << 8; + MsgBuffer[1] = (DSP_WORD) ((ChannelId << 8) | (ChannelType & 0xFF)); + + /* Attempt to send the Configure Channel message to the DSP and receive it's + reply. */ + if (!TransactCmd(DspId, MsgBuffer, MsgLength, MSG_CONFIG_CHAN_REPLY, 4, 1, + (DSP_WORD) ChannelId)) + return (CcsDspCommFailure); + + /* Return with an indication of success or failure based on the return + status in the reply message. */ + *pStatus = (GPAK_ChannelConfigStat_t) (MsgBuffer[1] & 0xFF); + if (*pStatus == Cc_Success) + return (CcsSuccess); + else + return (CcsParmError); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakTearDownChannel - Tear Down a DSP's Channel. + * + * FUNCTION + * This function tears down a DSP's Channel. + * + * RETURNS + * Status code indicating success or a specific error. + * + */ +gpakTearDownStatus_t gpakTearDownChannel( + unsigned short int DspId, /* DSP Id (0 to MaxDSPCores-1) */ + unsigned short int ChannelId, /* Channel Id (0 to MaxChannels-1) */ + GPAK_TearDownChanStat_t *pStatus /* pointer to Tear Down Status */ + ) +{ + DSP_WORD MsgBuffer[MSG_BUFFER_SIZE]; /* message buffer */ + + /* Make sure the DSP Id is valid. */ + if (DspId >= MAX_DSP_CORES) + return (TdsInvalidDsp); + + /* Make sure the Channel Id is valid. */ + if (ChannelId >= MaxChannels[DspId]) + return (TdsInvalidChannel); + + /* Build the Tear Down Channel message. */ + MsgBuffer[0] = MSG_TEAR_DOWN_CHANNEL << 8; + MsgBuffer[1] = (DSP_WORD) (ChannelId << 8); + + /* Attempt to send the Tear Down Channel message to the DSP and receive it's + reply. */ + if (!TransactCmd(DspId, MsgBuffer, 3, MSG_TEAR_DOWN_REPLY, 4, 1, + (DSP_WORD) ChannelId)) + return (TdsDspCommFailure); + + /* Return with an indication of success or failure based on the return + status in the reply message. */ + *pStatus = (GPAK_TearDownChanStat_t) (MsgBuffer[1] & 0xFF); + if (*pStatus == Td_Success) + return (TdsSuccess); + else + return (TdsError); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakAlgControl - Control an Algorithm. + * + * FUNCTION + * This function controls an Algorithm + * + * RETURNS + * Status code indicating success or a specific error. + * + */ +gpakAlgControlStat_t gpakAlgControl( + unsigned short int DspId, // DSP identifier + unsigned short int ChannelId, // channel identifier + GpakAlgCtrl_t ControlCode, // algorithm control code + GPAK_AlgControlStat_t *pStatus // pointer to return status + ) +{ + DSP_WORD MsgBuffer[MSG_BUFFER_SIZE]; /* message buffer */ + + /* Make sure the DSP Id is valid. */ + if (DspId >= MAX_DSP_CORES) + return (AcInvalidDsp); + + /* Make sure the Channel Id is valid. */ + if (ChannelId >= MaxChannels[DspId]) + return (AcInvalidChannel); + + MsgBuffer[0] = MSG_ALG_CONTROL << 8; + MsgBuffer[1] = (DSP_WORD) ((ChannelId << 8) | (ControlCode & 0xFF)); + + /* Attempt to send the Tear Down Channel message to the DSP and receive it's + reply. */ + //need_reply_len; + if (!TransactCmd(DspId, MsgBuffer, 4, MSG_ALG_CONTROL_REPLY, 4, 1, + (DSP_WORD) ChannelId)) + return (AcDspCommFailure); + + /* Return with an indication of success or failure based on the return + status in the reply message. */ + *pStatus = (GPAK_AlgControlStat_t) (MsgBuffer[1] & 0xFF); + if (*pStatus == Ac_Success) + return (AcSuccess); + else + return (AcParmError); + +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakReadEventFIFOMessage - read from the event fifo + * + * FUNCTION + * This function reads a single event from the event fifo if one is available + * + * RETURNS + * Status code indicating success or a specific error. + * + * Notes: This function should be called in a loop until the return status + * indicates that the fifo is empty. + * + * If the event code equals "EventLoopbackTeardownComplete", then the + * contents of *pChannelId hold the coderBlockId that was assigned to + * the loopback coder that was torn down. + */ +gpakReadEventFIFOMessageStat_t gpakReadEventFIFOMessage( + unsigned short int DspId, // DSP identifier + unsigned short int *pChannelId, // pointer to channel identifier + GpakAsyncEventCode_t *pEventCode, // pointer to Event Code + GpakAsyncEventData_t *pEventData // pointer to Event Data Struct + ) +{ + DSP_WORD WordBuffer[WORD_BUFFER_SIZE]; /* DSP words buffer */ + GpakAsyncEventCode_t EventCode; /* DSP's event code */ + DSP_WORD EventDataLength; /* Length of event to read */ + DSP_WORD ChannelId; /* DSP's channel Id */ + DSP_ADDRESS EventInfoAddress; /* address of EventFIFO info structure */ + DSP_ADDRESS BufrBaseAddress; /* base address of EventFIFO buffer */ + DSP_ADDRESS BufrLastAddress; /* last address of EventFIFO buffer */ + DSP_ADDRESS TakeAddress; /* current take address in fifo buffer */ + DSP_WORD BufrSize; /* size (in words) of event FIFO buffer */ + DSP_WORD PutIndex; /* event fifo put index */ + DSP_WORD TakeIndex; /* event fifo take index */ + DSP_WORD WordsReady; /* number words ready for read out of event fifo */ + DSP_WORD EventError; /* flag indicating error with event fifo msg */ + DSP_WORD *pDebugData; /* debug data buffer pointer in event data struct */ + + /* Make sure the DSP Id is valid. */ + if (DspId >= MAX_DSP_CORES) { +#if 0 + printk("Invalid DSP\n"); +#endif + return (RefInvalidDsp); + } + + /* Lock access to the DSP. */ + gpakLockAccess(DspId); + +#if 1 + /* Check if the DSP was reset and is ready. */ + if (CheckDspReset(DspId) == -1) + { + gpakUnlockAccess(DspId); +#if 0 + printk("CheckDspReset failed (DspId %d)\n", DspId); +#endif + return (RefDspCommFailure); + } +#endif + + /* Check if an event message is ready in the DSP. */ + EventInfoAddress = pEventFifoAddress[DspId]; + gpakReadDspMemory(DspId, EventInfoAddress, CIRC_BUFFER_INFO_STRUCT_SIZE, + WordBuffer); + RECONSTRUCT_LONGWORD(BufrBaseAddress, ((DSP_WORD *)&WordBuffer[CB_BUFR_BASE])); + BufrSize = WordBuffer[CB_BUFR_SIZE]; + PutIndex = WordBuffer[CB_BUFR_PUT_INDEX]; + TakeIndex = WordBuffer[CB_BUFR_TAKE_INDEX]; + if (PutIndex >= TakeIndex) + WordsReady = PutIndex - TakeIndex; + else + WordsReady = PutIndex + BufrSize - TakeIndex; + + if (WordsReady < 2) + { + gpakUnlockAccess(DspId); + return (RefNoEventAvail); + } + + /* Read the event header from the DSP's Event FIFO. */ + TakeAddress = BufrBaseAddress + TakeIndex; + BufrLastAddress = BufrBaseAddress + BufrSize - 1; + ReadCircBuffer(DspId, BufrBaseAddress, BufrLastAddress, &TakeAddress, + WordBuffer, 2); + TakeIndex += 2; + if (TakeIndex >= BufrSize) + TakeIndex -= BufrSize; + + ChannelId = (WordBuffer[0] >> 8) & 0xFF; + EventCode = (GpakAsyncEventCode_t)(WordBuffer[0] & 0xFF); + EventDataLength = WordBuffer[1]; + EventError = 0; + + switch (EventCode) + { + case EventToneDetect: + if (EventDataLength > WORD_BUFFER_SIZE) + { + gpakUnlockAccess(DspId); +#if 0 + printk("EventDataLength > WORD_BUFFER_SIZE (%d)\n", EventDataLength); +#endif + return (RefInvalidEvent); + } + ReadCircBuffer(DspId, BufrBaseAddress, BufrLastAddress, &TakeAddress, + WordBuffer, EventDataLength); + pEventData->toneEvent.ToneCode = (GpakToneCodes_t) + (WordBuffer[0] & 0xFF); + pEventData->toneEvent.ToneDuration = WordBuffer[1]; + pEventData->toneEvent.Direction = WordBuffer[2]; + pEventData->toneEvent.DebugToneStatus = WordBuffer[3]; + TakeIndex += EventDataLength; + if (TakeIndex >= BufrSize) + TakeIndex -= BufrSize; + if (EventDataLength != 4) { +#if 0 + printk("EventDataLength != 4 it's %d\n", EventDataLength); +#endif + EventError = 1; + } + break; + + default: +#if 0 + printk("Event Code not in switch\n"); +#endif + EventError = 1; + break; + }; + + /* Update the Take index in the DSP's Packet Out buffer information. */ + gpakWriteDspMemory(DspId, EventInfoAddress + CB_BUFR_TAKE_INDEX, 1, + &TakeIndex); + + /* Unlock access to the DSP. */ + gpakUnlockAccess(DspId); + + if (EventError) + return(RefInvalidEvent); + + *pChannelId = ChannelId; + *pEventCode = EventCode; + return(RefEventAvail); + +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakPingDsp - ping the DSP to see if it's alive + * + * FUNCTION + * This function checks if the DSP is still communicating with the host + * and returns the DSP SW version + * + * RETURNS + * Status code indicating success or a specific error. + */ +gpakPingDspStat_t gpakPingDsp( + unsigned short int DspId, // DSP identifier + unsigned short int *pDspSwVersion // DSP software version + ) +{ + DSP_WORD MsgBuffer[MSG_BUFFER_SIZE]; /* message buffer */ + DSP_WORD DspStatus; /* DSP's reply status */ + + /* Make sure the DSP Id is valid. */ + if (DspId >= MAX_DSP_CORES) + return (PngInvalidDsp); + + /* send value of 1, DSP increments it */ + MsgBuffer[0] = (MSG_PING << 8); + + /* Attempt to send the ping message to the DSP and receive it's + reply. */ + if (!TransactCmd(DspId, MsgBuffer, 1, MSG_PING_REPLY, 6, 0, 0)) + return (PngDspCommFailure); + + /* Return with an indication of success or failure based on the return + status in the reply message. */ + DspStatus = (MsgBuffer[1] & 0xFF); + if (DspStatus == 0) + { + *pDspSwVersion = MsgBuffer[2]; + return (PngSuccess); + } + else + return (PngDspCommFailure); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakSerialTxFixedValue - transmit a fixed value on a timeslot + * + * FUNCTION + * This function controls transmission of a fixed value out onto a serial + * port's timeslot. + * + * RETURNS + * Status code indicating success or a specific error. + */ +gpakSerialTxFixedValueStat_t gpakSerialTxFixedValue( + unsigned short int DspId, // DSP identifier + unsigned short int ChannelId, // channel identifier + GpakSerialPort_t PcmOutPort, // PCM Output Serial Port Id + unsigned short int PcmOutSlot, // PCM Output Time Slot + unsigned short int Value, // 16-bit value + GpakActivation State // activation state + ) +{ + DSP_WORD MsgBuffer[MSG_BUFFER_SIZE]; /* message buffer */ + DSP_WORD DspStatus; /* DSP's reply status */ + + /* Make sure the DSP Id is valid. */ + if (DspId >= MAX_DSP_CORES) + return (TfvInvalidDsp); + + /* Make sure the Channel Id is valid. */ + if (ChannelId >= MaxChannels[DspId]) + return (TfvInvalidChannel); + + + /* Build the message. */ + MsgBuffer[0] = MSG_SERIAL_TXVAL << 8; + MsgBuffer[1] = (DSP_WORD) ((ChannelId << 8) | (State & 0xFF)); + MsgBuffer[2] = (DSP_WORD) ((PcmOutPort << 8) | (PcmOutSlot & 0xFF)); + MsgBuffer[3] = (DSP_WORD) Value; + + /* Attempt to send the message to the DSP and receive it's + reply. */ + //need_reply_len; + if (!TransactCmd(DspId, MsgBuffer, 8, MSG_SERIAL_TXVAL_REPLY, 4, + 1, ChannelId)) + return (TfvDspCommFailure); + + /* Return with an indication of success or failure based on the return + status in the reply message. */ + DspStatus = (MsgBuffer[1] & 0xFF); + if (DspStatus == 0) + return (TfvSuccess); + else + return (TfvDspCommFailure); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakControlTdmLoopBack - control a serial port's loopback state + * + * FUNCTION + * This function enables/disables the tdm input to output looback mode on a + * serial port + * + * RETURNS + * Status code indicating success or a specific error. + */ + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ +gpakControlTdmLoopBackStat_t gpakControlTdmLoopBack( + unsigned short int DspId, // DSP identifier + GpakSerialPort_t SerialPort, // Serial Port Id + GpakActivation LoopBackState // Loopback State + ) +{ + DSP_WORD MsgBuffer[MSG_BUFFER_SIZE]; /* message buffer */ + DSP_WORD DspStatus; /* DSP's reply status */ + + /* Make sure the DSP Id is valid. */ + if (DspId >= MAX_DSP_CORES) + return (ClbInvalidDsp); + + /* Build the message. */ + MsgBuffer[0] = MSG_TDM_LOOPBACK << 8; + MsgBuffer[1] = (DSP_WORD) ((SerialPort << 8) | (LoopBackState & 0xFF)); + + /* Attempt to send the message to the DSP and receive it's + reply. */ + //need_reply_len; + if (!TransactCmd(DspId, MsgBuffer, 4, MSG_TDM_LOOPBACK_REPLY, 4, 0, 0)) + return (ClbDspCommFailure); + + /* Return with an indication of success or failure based on the return + status in the reply message. */ + DspStatus = (MsgBuffer[1] & 0xFF); + if (DspStatus == 0) + return (ClbSuccess); + else + return (ClbDspCommFailure); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakReadCpuUsage - Read CPU usage statistics from a DSP. + * + * FUNCTION + * This function reads the CPU usage statistics from a DSP's memory. The + * average CPU usage in units of .1 percent are obtained for each of the frame + * rates. + * + * RETURNS + * Status code indicating success or a specific error. + * + */ +gpakReadCpuUsageStat_t gpakReadCpuUsage( + unsigned short int DspId, // Dsp Identifier + unsigned short int *pPeakUsage, // pointer to peak usage variable + unsigned short int *pPrev1SecPeakUsage // peak usage over previous 1 second + ) +{ + DSP_WORD ReadBuffer[2]; /* DSP read buffer */ + + /* Make sure the DSP Id is valid. */ + if (DspId >= MAX_DSP_CORES) + return (RcuInvalidDsp); + + /* Lock access to the DSP. */ + gpakLockAccess(DspId); + + /* Check if the DSP was reset and is ready. */ + if (CheckDspReset(DspId) == -1) + return (RcuDspCommFailure); + + /* Read the CPU Usage statistics from the DSP. */ + gpakReadDspMemory(DspId, pDspIfBlk[DspId] + CPU_USAGE_OFFSET, 2, + ReadBuffer); + + /* Unlock access to the DSP. */ + gpakUnlockAccess(DspId); + + /* Store the usage statistics in the specified variables. */ + *pPrev1SecPeakUsage = ReadBuffer[0]; + *pPeakUsage = ReadBuffer[1]; + + /* Return with an indication the usage staistics were read successfully. */ + return (RcuSuccess); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakResetCpuUsageStats - reset the cpu usage statistics + * + * FUNCTION + * This function resets the cpu utilization statistics + * + * RETURNS + * Status code indicating success or a specific error. + */ +gpakResetCpuUsageStat_t gpakResetCpuUsageStats( + unsigned short int DspId // DSP identifier + ) +{ + DSP_WORD MsgBuffer[MSG_BUFFER_SIZE]; /* message buffer */ + DSP_WORD DspStatus; /* DSP's reply status */ + + /* Make sure the DSP Id is valid. */ + if (DspId >= MAX_DSP_CORES) + return (RstcInvalidDsp); + + MsgBuffer[0] = (MSG_RESET_USAGE_STATS << 8); + + /* Attempt to send the message to the DSP and receive it's reply. */ + //need_reply_len; + if (!TransactCmd(DspId, MsgBuffer, 2, MSG_RESET_USAGE_STATS_REPLY, 4, 0, 0)) + return (RstcDspCommFailure); + + /* Return with an indication of success or failure based on the return + status in the reply message. */ + DspStatus = (MsgBuffer[1] & 0xFF); + if (DspStatus == 0) + return (RstcSuccess); + else + return (RstcDspCommFailure); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakReadFramingStats + * + * FUNCTION + * This function reads a DSP's framing interrupt statistics + * + * RETURNS + * Status code indicating success or a specific error. + */ +gpakReadFramingStatsStatus_t gpakReadFramingStats( + unsigned short int DspId, // DSP identifier + unsigned short int *pFramingError1Count, // port 1 Framing error count + unsigned short int *pFramingError2Count, // port 2 Framing error count + unsigned short int *pFramingError3Count, // port 3 Framing error count + unsigned short int *pDmaStopErrorCount, // DMA-stoppage error count + unsigned short int *pDmaSlipStatsBuffer // DMA slips count + ) +{ + DSP_WORD ReadBuffer[10]; /* DSP read buffer */ + + /* Make sure the DSP Id is valid. */ + if (DspId >= MAX_DSP_CORES) + return (RfsInvalidDsp); + + /* Lock access to the DSP. */ + gpakLockAccess(DspId); + + /* Check if the DSP was reset and is ready. */ + if (CheckDspReset(DspId) == -1) + return (RfsDspCommFailure); + + /* Read the framing interrupt statistics from the DSP. */ + gpakReadDspMemory(DspId, pDspIfBlk[DspId] + FRAMING_STATS_OFFSET, 10, + ReadBuffer); + + /* Unlock access to the DSP. */ + gpakUnlockAccess(DspId); + + /* Store the framing statistics in the specified variables. */ + *pFramingError1Count = ReadBuffer[0]; + *pFramingError2Count = ReadBuffer[1]; + *pFramingError3Count = ReadBuffer[2]; + *pDmaStopErrorCount = ReadBuffer[3]; + + if(pDmaSlipStatsBuffer != 0) + // If users want to get the DMA slips count + { + pDmaSlipStatsBuffer[0] = ReadBuffer[4]; + pDmaSlipStatsBuffer[1] = ReadBuffer[5]; + pDmaSlipStatsBuffer[2] = ReadBuffer[6]; + pDmaSlipStatsBuffer[3] = ReadBuffer[7]; + pDmaSlipStatsBuffer[4] = ReadBuffer[8]; + pDmaSlipStatsBuffer[5] = ReadBuffer[9]; + + } + /* Return with an indication the statistics were read successfully. */ + return (RfsSuccess); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakResetFramingStats - reset a DSP's framing interrupt statistics + * + * FUNCTION + * This function resets a DSP's framing interrupt statistics + * + * RETURNS + * Status code indicating success or a specific error. + */ +gpakResetFramingStatsStatus_t gpakResetFramingStats( + unsigned short int DspId // DSP identifier + ) +{ + DSP_WORD MsgBuffer[MSG_BUFFER_SIZE]; /* message buffer */ + DSP_WORD DspStatus; /* DSP's reply status */ + + /* Make sure the DSP Id is valid. */ + if (DspId >= MAX_DSP_CORES) + return (RstfInvalidDsp); + + MsgBuffer[0] = (MSG_RESET_FRAME_STATS << 8); + + /* Attempt to send the message to the DSP and receive it's reply. */ + //need_reply_len; + if (!TransactCmd(DspId, MsgBuffer, 2, MSG_RESET_FRAME_STATS_REPLY, 4, 0, 0)) + return (RstfDspCommFailure); + + /* Return with an indication of success or failure based on the return + status in the reply message. */ + DspStatus = (MsgBuffer[1] & 0xFF); + if (DspStatus == 0) + return (RstfSuccess); + else + return (RstfDspCommFailure); +} + +/* + * gpakDownloadDsp - Download a DSP's Program and initialized Data memory. + * + * FUNCTION + * This function reads a DSP's Program and Data memory image from the + * specified file and writes the image to the DSP's memory. + * + * RETURNS + * Status code indicating success or a specific error. + * + */ +gpakDownloadStatus_t gpakDownloadDsp( + unsigned short DspId, /* DSP Identifier (0 to MaxDSPCores-1) */ + GPAK_FILE_ID FileId /* G.PAK Download File Identifier */ + ) +{ + gpakDownloadStatus_t RetStatus; /* function return status */ + int NumRead; /* number of file bytes read */ + DSP_ADDRESS Address; /* DSP address */ + unsigned int WordCount; /* number of words in record */ + unsigned int NumWords; /* number of words to read/write */ + unsigned int i; /* loop index / counter */ + unsigned int j; /* loop index */ + + /* Make sure the DSP Id is valid. */ + if (DspId >= MAX_DSP_CORES) + return (GdlInvalidDsp); + + /* Lock access to the DSP. */ + gpakLockAccess(DspId); + + RetStatus = GdlSuccess; + while (RetStatus == GdlSuccess) + { + + /* Read a record header from the file. */ + NumRead = gpakReadFile(FileId, DlByteBufr, 6); + if (NumRead == -1) + { + RetStatus = GdlFileReadError; + break; + } + if (NumRead != 6) + { + RetStatus = GdlInvalidFile; + break; + } + Address = (((DSP_ADDRESS) DlByteBufr[1]) << 16) | + (((DSP_ADDRESS) DlByteBufr[2]) << 8) | + ((DSP_ADDRESS) DlByteBufr[3]); + WordCount = (((unsigned int) DlByteBufr[4]) << 8) | + ((unsigned int) DlByteBufr[5]); + + /* Check for the End Of File record. */ + if (DlByteBufr[0] == 0xFF) + break; + + /* Verify the record is for a valid memory type. */ + if ((DlByteBufr[0] != 0x00) && (DlByteBufr[0] != 0x01)) + { + RetStatus = GdlInvalidFile; + break; + } + + /* Read a block of words at a time from the file and write to the + DSP's memory .*/ + while (WordCount != 0) + { + if (WordCount < DOWNLOAD_BLOCK_SIZE) + NumWords = WordCount; + else + NumWords = DOWNLOAD_BLOCK_SIZE; + WordCount -= NumWords; + NumRead = gpakReadFile(FileId, DlByteBufr, NumWords * 2); + if (NumRead == -1) + { + RetStatus = GdlFileReadError; + break; + } + if (NumRead != (NumWords * 2)) + { + RetStatus = GdlInvalidFile; + break; + } + for (i = 0, j = 0; i < NumWords; i++, j += 2) + DlWordBufr[i] = (((DSP_WORD) DlByteBufr[j]) << 8) | + ((DSP_WORD) DlByteBufr[j + 1]); + gpakWriteDspMemory(DspId, Address, NumWords, DlWordBufr); + Address += ((DSP_ADDRESS) NumWords); + } + } + + /* Unlock access to the DSP. */ + gpakUnlockAccess(DspId); + + /* Return with an indication of success or failure. */ + return (RetStatus); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakReadCpuUsage - Read CPU usage statistics from a DSP. + * + * FUNCTION + * This function reads the memory map register section of DSP memory. + * + * RETURNS + * Status code indicating success or a specific error. + * + */ +gpakReadDSPMemoryStat_t gpakReadDSPMemoryMap( + unsigned short int DspId, // Dsp Identifier + unsigned short int *pDest, // Buffer on host to hold DSP memory map + DSP_ADDRESS BufrBaseAddress, // DSP memory users want to read out + unsigned short int MemoryLength_Word16 // Length of memory section read out, unit is 16-bit word + ) +{ + DSP_WORD MsgBuffer[MSG_BUFFER_SIZE]; /* message buffer */ + DSP_WORD DspStatus; /* DSP reply's status */ + int i; /* loop index / counter */ + + + /* Make sure the DSP Id is valid. */ + if (DspId >= MAX_DSP_CORES) + return (RmmInvalidDsp); + + /* Verify the message buffer is large enough */ + if (MSG_BUFFER_SIZE < MemoryLength_Word16 ) + return (RmmSizeTooBig); + + MsgBuffer[0] = MSG_READ_DSP_MEMORY << 8; + MsgBuffer[1] = (DSP_WORD) ((BufrBaseAddress >> 16) & 0xFFFF); + MsgBuffer[2] = (DSP_WORD) (BufrBaseAddress & 0xFFFF); + MsgBuffer[3] = (DSP_WORD) MemoryLength_Word16; + + /* Attempt to send the Read memory section message to the DSP and receive it's + reply. */ + //need_reply_len; + if (!TransactCmd(DspId, MsgBuffer, 8, MSG_READ_DSP_MEMORY_REPLY, + (MemoryLength_Word16+2)*2, 0, 0) ) + return (RmmInvalidAddress); + + /* Return with an indication of success or failure based on the return + status in the reply message. */ + DspStatus = (MsgBuffer[1] & 0xFF); + if (DspStatus != 0) + return (RmmFailure); + + for (i = 0; i < MemoryLength_Word16; i++) + pDest[i] = (short int) MsgBuffer[2 + i]; + + + return (RmmSuccess); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakAccessGPIO - change Direction/read/write the GPIO on DSP + * + * FUNCTION + * This function read/write GPIO and change the GPIO direction + * + * + * RETURNS + * Status code indicating success or a specific error. + */ +gpakAccessGPIOStat_t gpakAccessGPIO( + unsigned short int DspId, // DSP identifier + GpakGPIOCotrol_t gpakControlGPIO,// select oeration, changeDIR/write/read + unsigned short int *pGPIOValue // DSP software version + ) +{ + DSP_WORD MsgBuffer[MSG_BUFFER_SIZE]; /* message buffer */ + DSP_WORD DspStatus; /* DSP's reply status */ + + /* Make sure the DSP Id is valid. */ + if (DspId >= MAX_DSP_CORES) + return (GPIOInvalidDsp); + + /* send value of 1, DSP increments it */ + MsgBuffer[0] = (MSG_ACCESSGPIO << 8); + MsgBuffer[1] = (DSP_WORD) ((gpakControlGPIO << 8) | (*pGPIOValue & 0xFF) ); + /* Attempt to send the ping message to the DSP and receive it's + reply. */ + if (!TransactCmd(DspId, MsgBuffer, 4, MSG_ACCESSGPIO_REPLY, 6, 0, 0)) + return (GPIODspCommFailure); + + /* Return with an indication of success or failure based on the return + status in the reply message. */ + DspStatus = (MsgBuffer[1] & 0xFF); + if (DspStatus == 0) + { + *pGPIOValue = MsgBuffer[2]; + return (GPIOSuccess); + } + else + return (GPIODspCommFailure); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakWriteSystemParms - Write a DSP's System Parameters. + * + * FUNCTION + * This function writes a DSP's System Parameters information. + * + * Note: + * Or-together the desired bit-mask #defines that are listed below. Only + * those algorithm parameters whose bit-mask is selected in the UpdateBits + * function parameter will be updated. + * + * RETURNS + * Status code indicating success or a specific error. + * + */ + +gpakWriteSysParmsStatus_t gpakWriteSystemParms( + unsigned short int DspId, // DSP identifier + GpakSystemParms_t *pSysParms, /* pointer to System Parms info var */ + unsigned short int UpdateBits, /* input: flags indicating which parms to update */ + GPAK_SysParmsStat_t *pStatus /* pointer to Write System Parms Status */ + ) +{ + DSP_WORD MsgBuffer[MSG_BUFFER_SIZE]; /* message buffer */ + DSP_WORD DspStatus; /* DSP's reply status */ + + /* Make sure the DSP Id is valid. */ + if (DspId >= MAX_DSP_CORES) + return (WspInvalidDsp); + + /* Build the Write System Parameters message. */ + MsgBuffer[0] = MSG_WRITE_SYS_PARMS << 8; + + if (UpdateBits & DTMF_UPDATE_MASK) + { + MsgBuffer[1] |= DTMF_UPDATE_MASK; + MsgBuffer[8] = (DSP_WORD) pSysParms->MinSigLevel; + MsgBuffer[9] = (DSP_WORD) (pSysParms->FreqDeviation & 0xff); + if (pSysParms->SNRFlag) + MsgBuffer[9] |= (1<<8); + } + + MsgBuffer[10] = (DSP_WORD) 0; + if (UpdateBits & DTMF_TWIST_UPDATE_MASK) + { + MsgBuffer[1] |= DTMF_TWIST_UPDATE_MASK; + MsgBuffer[10] |= (DSP_WORD) (pSysParms->DtmfFwdTwist & 0x000f); + MsgBuffer[10] |= (DSP_WORD) ((pSysParms->DtmfRevTwist << 4) & 0x00f0); + } + + + if (UpdateBits & DTMF_VALID_MASK) + { + MsgBuffer[1] |= DTMF_VALID_MASK; + MsgBuffer[11] = (DSP_WORD) (pSysParms->DtmfValidityMask & 0x00ff); + } + + /* Attempt to send the ping message to the DSP and receive it's + reply. */ + if (!TransactCmd(DspId, MsgBuffer, 24, MSG_WRITE_SYS_PARMS_REPLY, 6, 0, 0)) + return (WspDspCommFailure); + + /* Return with an indication of success or failure based on the return + status in the reply message. */ + *pStatus = (GPAK_SysParmsStat_t) (MsgBuffer[2] ); + + DspStatus = (MsgBuffer[1] & 0xFF); + if (DspStatus == 0) + return (WspSuccess); + else + return (WspDspCommFailure); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakReadSystemParms - Read a DSP's System Parameters. + * + * FUNCTION + * This function reads a DSP's System Parameters information. + * + * RETURNS + * Status code indicating success or a specific error. + * + */ +gpakReadSysParmsStatus_t gpakReadSystemParms( + unsigned short int DspId, // DSP identifier + GpakSystemParms_t *pSysParms /* pointer to System Parms info var */ + ) +{ + + DSP_WORD MsgBuffer[MSG_BUFFER_SIZE]; /* message buffer */ + + /* Make sure the DSP Id is valid. */ + if (DspId >= MAX_DSP_CORES) + return (RspInvalidDsp); + + /* Build the Read System Parameters message. */ + MsgBuffer[0] = MSG_READ_SYS_PARMS << 8; + + /* Attempt to send the ping message to the DSP and receive it's + reply. */ + if (!TransactCmd(DspId, MsgBuffer, 2, MSG_READ_SYS_PARMS_REPLY, 22, 0, 0)) + return (RspDspCommFailure); + + /* Extract the System Parameters information from the message. */ + pSysParms->DtmfValidityMask = (short int)(MsgBuffer[7]) ; + + pSysParms->MinSigLevel = (short int)MsgBuffer[8]; + pSysParms->SNRFlag = (short int)((MsgBuffer[9]>>8) & 0x1); + pSysParms->FreqDeviation = (short int)(MsgBuffer[9] & 0xff); + pSysParms->DtmfFwdTwist = (short int)MsgBuffer[10] & 0x000f; + pSysParms->DtmfRevTwist = (short int)(MsgBuffer[10] >> 4) & 0x000f; + + /* Return with an indication that System Parameters info was obtained. */ + return (RspSuccess); +} diff --git a/wctdm24xxp/GpakApi.h b/wctdm24xxp/GpakApi.h new file mode 100644 index 0000000..8138430 --- /dev/null +++ b/wctdm24xxp/GpakApi.h @@ -0,0 +1,619 @@ +/* + * Copyright (c) 2005 , Adaptive Digital Technologies, Inc. + * + * File Name: GpakApi.h + * + * Description: + * This file contains the function prototypes and data types for the user + * API functions that communicate with DSPs executing G.PAK software. The + * file is used by application software in the host processor connected to + * C55X G.PAK DSPs via a Host Port Interface. + * + * Version: 1.0 + * + * Revision History: + * 06/15/05 - Initial release. + * 11/15/2006 - 24 TDM-TDM Channels EC release + */ + +#ifndef _GPAKAPI_H /* prevent multiple inclusion */ +#define _GPAKAPI_H +#include "gpakErrs.h" +#include "gpakenum.h" + +// Bit masks to select which algorithm's parameters to update: Or-together the +// desired masks into the UpdateBits function parameter. +#define DTMF_UPDATE_MASK 0x0010 // update DTMF params, MinLevel, SNRFlag and Freq +#define DTMF_TWIST_UPDATE_MASK 0x0020 // update DTMF TWIST system params +#define DTMF_VALID_MASK 0x0080 // update DTMF ValidMask params + +#define DSP_DEBUG_BUFF_SIZE 42 // units of 16-bit words + +/* Definition of an Asynchronous Event Data Structure */ +typedef union +{ + struct + { + GpakToneCodes_t ToneCode; // detected tone code + unsigned short int ToneDuration; // tone duration + GpakTdmDirection Direction; // detected on A r B side + short int DebugToneStatus;// reserved for debug info + } toneEvent; + +} GpakAsyncEventData_t; + +/* Definition of an Echo Canceller Parameters information structure. */ +typedef struct +{ + short int EcanTapLength; // Echo Can Num Taps (tail length) + short int EcanNlpType; // Echo Can NLP Type + short int EcanAdaptEnable; // Echo Can Adapt Enable flag + short int EcanG165DetEnable; // Echo Can G165 Detect Enable flag + short int EcanDblTalkThresh; // Echo Can Double Talk threshold + short int EcanNlpThreshold; // Echo Can NLP threshold + short int EcanNlpConv; // Dynamic NLP control, NLP limit when EC about to converged + short int EcanNlpUnConv;// Dynamic NLP control, NLP limit when EC not converged yet + short int EcanNlpMaxSuppress; // suppression level for NLP_SUPP mode + short int EcanCngThreshold; // Echo Can CNG Noise threshold + short int EcanAdaptLimit; // Echo Can Max Adapts per frame + short int EcanCrossCorrLimit; // Echo Can Cross Correlation limit + short int EcanNumFirSegments; // Echo Can Num FIR Segments + short int EcanFirSegmentLen; // Echo Can FIR Segment Length +} GpakEcanParms_t; + +/* Definition of a Channel Configuration information structure. */ +typedef struct +{ + GpakSerialPort_t PcmInPortA; // A side PCM Input Serial Port Id + unsigned short int PcmInSlotA; // A side PCM Input Time Slot + GpakSerialPort_t PcmOutPortA; // A side PCM Output Serial Port Id + unsigned short int PcmOutSlotA; // A side PCM Output Time Slot + GpakSerialPort_t PcmInPortB; // B side PCM Input Serial Port Id + unsigned short int PcmInSlotB; // B side PCM Input Time Slot + GpakSerialPort_t PcmOutPortB; // B side PCM Output Serial Port Id + unsigned short int PcmOutSlotB; // B side PCM Output Time Slot + GpakToneTypes ToneTypesA; // A side Tone Detect Types + GpakToneTypes ToneTypesB; // B side Tone Detect Types + GpakActivation EcanEnableA; // Echo Cancel A Enabled + GpakActivation EcanEnableB; // Echo Cancel B Enabled + GpakEcanParms_t EcanParametersA; // Echo Cancel parameters + GpakEcanParms_t EcanParametersB; // Echo Cancel parameters + GpakCompandModes SoftwareCompand; // software companding + GpakRate_t FrameRate; // Gpak Frame Rate + GpakActivation MuteToneA; // A side mute DTMF Enabled + GpakActivation MuteToneB; // B side mute DTMF Enabled + GpakActivation FaxCngDetA; // A side FaxCng Tone Detector Enabled + GpakActivation FaxCngDetB; // B side FaxCng Tone Detector Enabled + +} GpakChannelConfig_t; + + +/* Definition of a Serial Port Configuration Structure */ +typedef struct +{ + GpakSlotCfg_t SlotsSelect1; // port 1 Slot selection + unsigned short int FirstBlockNum1; // port 1 first group Block Number + unsigned short int FirstSlotMask1; // port 1 first group Slot Mask + unsigned short int SecBlockNum1; // port 1 second group Block Number + unsigned short int SecSlotMask1; // port 1 second group Slot Mask + + GpakSerWordSize_t SerialWordSize1; // port 1 serial word size + GpakCompandModes CompandingMode1; // port 1 companding mode + GpakSerFrameSyncPol_t TxFrameSyncPolarity1; // port 1 Tx Frame Sync Polarity + GpakSerFrameSyncPol_t RxFrameSyncPolarity1; // port 1 Rx Frame Sync Polarity + GpakSerClockPol_t TxClockPolarity1; // port 1 Tx Clock Polarity + GpakSerClockPol_t RxClockPolarity1; // port 1 Rx Clock Polarity + GpakSerDataDelay_t TxDataDelay1; // port 1 Tx data delay + GpakSerDataDelay_t RxDataDelay1; // port 1 Rx data delay + GpakActivation DxDelay1; // port 1 DX Delay + + unsigned short int ThirdSlotMask1; // port 1 3rd group Slot Mask + unsigned short int FouthSlotMask1; // port 1 4th group Slot Mask + unsigned short int FifthSlotMask1; // port 1 5th group Slot Mask + unsigned short int SixthSlotMask1; // port 1 6th group Slot Mask + unsigned short int SevenSlotMask1; // port 1 7th group Slot Mask + unsigned short int EightSlotMask1; // port 1 8th group Slot Mask + + + GpakSlotCfg_t SlotsSelect2; // port 2 Slot selection + unsigned short int FirstBlockNum2; // port 2 first group Block Number + unsigned short int FirstSlotMask2; // port 2 first group Slot Mask + unsigned short int SecBlockNum2; // port 2 second group Block Number + unsigned short int SecSlotMask2; // port 2 second group Slot Mask + GpakSerWordSize_t SerialWordSize2; // port 2 serial word size + GpakCompandModes CompandingMode2; // port 2 companding mode + GpakSerFrameSyncPol_t TxFrameSyncPolarity2; // port 2 Tx Frame Sync Polarity + GpakSerFrameSyncPol_t RxFrameSyncPolarity2; // port 2 Rx Frame Sync Polarity + GpakSerClockPol_t TxClockPolarity2; // port 2 Tx Clock Polarity + GpakSerClockPol_t RxClockPolarity2; // port 2 Rx Clock Polarity + GpakSerDataDelay_t TxDataDelay2; // port 2 Tx data delay + GpakSerDataDelay_t RxDataDelay2; // port 2 Rx data delay + GpakActivation DxDelay2; // port 2 DX Delay + + unsigned short int ThirdSlotMask2; // port 2 3rd group Slot Mask + unsigned short int FouthSlotMask2; // port 2 4th group Slot Mask + unsigned short int FifthSlotMask2; // port 2 5th group Slot Mask + unsigned short int SixthSlotMask2; // port 2 6th group Slot Mask + unsigned short int SevenSlotMask2; // port 2 7th group Slot Mask + unsigned short int EightSlotMask2; // port 2 8th group Slot Mask + + GpakSlotCfg_t SlotsSelect3; // port 3 Slot selection + unsigned short int FirstBlockNum3; // port 3 first group Block Number + unsigned short int FirstSlotMask3; // port 3 first group Slot Mask + unsigned short int SecBlockNum3; // port 3 second group Block Number + unsigned short int SecSlotMask3; // port 3 second group Slot Mask + GpakSerWordSize_t SerialWordSize3; // port 3 serial word size + GpakCompandModes CompandingMode3; // port 3 companding mode + GpakSerFrameSyncPol_t TxFrameSyncPolarity3; // port 3 Tx Frame Sync Polarity + GpakSerFrameSyncPol_t RxFrameSyncPolarity3; // port 3 Rx Frame Sync Polarity + GpakSerClockPol_t TxClockPolarity3; // port 3 Tx Clock Polarity + GpakSerClockPol_t RxClockPolarity3; // port 3 Rx Clock Polarity + GpakSerDataDelay_t TxDataDelay3; // port 3 Tx data delay + GpakSerDataDelay_t RxDataDelay3; // port 3 Rx data delay + GpakActivation DxDelay3; // port 3 DX Delay + + unsigned short int ThirdSlotMask3; // port 3 3rd group Slot Mask + unsigned short int FouthSlotMask3; // port 3 4th group Slot Mask + unsigned short int FifthSlotMask3; // port 3 5th group Slot Mask + unsigned short int SixthSlotMask3; // port 3 6th group Slot Mask + unsigned short int SevenSlotMask3; // port 3 7th group Slot Mask + unsigned short int EightSlotMask3; // port 3 8th group Slot Mask + +} GpakPortConfig_t; + +/* Definition of a Tone Generation Parameter Structure */ +/* +typedef struct +{ + GpakToneGenType_t ToneType; // Tone Type + unsigned short int Frequency[4]; // Frequency (Hz) + short int Level[4]; // Frequency's Level (1 dBm) + unsigned short int OnTime[4]; // On Times (msecs) + unsigned short int OffTime[4]; // Off Times (msecs) +} GpakToneGenParms_t; +*/ +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* gpakConfigureChannel return status. */ +typedef enum +{ + CcsSuccess = 0, /* Channel Configured successfully */ + CcsParmError = 1, /* Channel Config Parameter error */ + CcsInvalidChannel = 2, /* invalid channel */ + CcsInvalidDsp = 3, /* invalid DSP */ + CcsDspCommFailure = 4 /* failed to communicate with DSP */ +} gpakConfigChanStatus_t; + +/* + * gpakConfigureChannel - Configure a DSP's Channel. + * + * FUNCTION + * This function configures a DSP's Channel. + * + * RETURNS + * Status code indicating success or a specific error. + * + */ +extern gpakConfigChanStatus_t gpakConfigureChannel( + unsigned short int DspId, // DSP identifier + unsigned short int ChannelId, // channel identifier + GpakChanType ChannelType, // channel type + GpakChannelConfig_t *pChanConfig, // pointer to channel config info + GPAK_ChannelConfigStat_t *pStatus // pointer to Channel Config Status + ); + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* gpakTearDownChannel return status. */ +typedef enum +{ + TdsSuccess = 0, /* Channel Tear Down successful */ + TdsError = 1, /* Channel Tear Down error */ + TdsInvalidChannel = 2, /* invalid channel */ + TdsInvalidDsp = 3, /* invalid DSP */ + TdsDspCommFailure = 4 /* failed to communicate with DSP */ +} gpakTearDownStatus_t; + +/* + * gpakTearDownChannel - Tear Down a DSP's Channel. + * + * FUNCTION + * This function tears down a DSP's Channel. + * + * RETURNS + * Status code indicating success or a specific error. + * + */ + +extern gpakTearDownStatus_t gpakTearDownChannel( + unsigned short int DspId, // DSP identifier + unsigned short int ChannelId, // channel identifier + GPAK_TearDownChanStat_t *pStatus // pointer to Tear Down Status + ); + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* gpakAlgControl return status. */ +typedef enum +{ + AcSuccess = 0, /* control successful */ + AcInvalidChannel = 1, /* invalid channel identifier */ + AcInvalidDsp = 2, /* invalid DSP */ + AcParmError = 3, /* invalid control parameter */ + AcDspCommFailure = 4 /* failed to communicate with DSP */ +} gpakAlgControlStat_t; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakAlgControl - Control an Algorithm. + * + * FUNCTION + * This function controls an Algorithm + * + * RETURNS + * Status code indicating success or a specific error. + * + */ +extern gpakAlgControlStat_t gpakAlgControl( + unsigned short int DspId, // DSP identifier + unsigned short int ChannelId, // channel identifier + GpakAlgCtrl_t ControlCode, // algorithm control code + GPAK_AlgControlStat_t *pStatus // pointer to return status + ); + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* gpakConfigurePorts return status. */ +typedef enum +{ + CpsSuccess = 0, /* Serial Ports configured successfully */ + CpsParmError = 1, /* Configure Ports Parameter error */ + CpsInvalidDsp = 2, /* invalid DSP */ + CpsDspCommFailure = 3 /* failed to communicate with DSP */ +} gpakConfigPortStatus_t; + +/* + * gpakConfigurePorts - Configure a DSP's serial ports. + * + * FUNCTION + * This function configures a DSP's serial ports. + * + * RETURNS + * Status code indicating success or a specific error. + * + */ +extern gpakConfigPortStatus_t gpakConfigurePorts( + unsigned short int DspId, // DSP identifier + GpakPortConfig_t *pPortConfig, // pointer to Port Config info + GPAK_PortConfigStat_t *pStatus // pointer to Port Config Status + ); + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* gpakDownloadDsp return status. */ +typedef enum +{ + GdlSuccess = 0, /* DSP download successful */ + GdlFileReadError = 1, /* error reading Download file */ + GdlInvalidFile = 2, /* invalid Download file content */ + GdlInvalidDsp = 3 /* invalid DSP */ +} gpakDownloadStatus_t; + +/* + * gpakDownloadDsp - Download a DSP's Program and initialized Data memory. + * + * FUNCTION + * This function reads a DSP's Program and Data memory image from the + * specified file and writes the image to the DSP's memory. + * + * RETURNS + * Status code indicating success or a specific error. + * + */ +extern gpakDownloadStatus_t gpakDownloadDsp( + unsigned short int DspId, // DSP identifier + GPAK_FILE_ID FileId // G.PAK download file identifier + ); + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* gpakReadEventFIFOMessage return status */ +typedef enum +{ + RefEventAvail = 0, /* an event was successfully read from the fifo */ + RefNoEventAvail = 1, /* no event was in the fifo */ + RefInvalidDsp = 2, /* invalid DSP identifier */ + RefInvalidEvent = 3, /* invalid event */ + RefDspCommFailure = 4 /* error communicating with DSP */ +} gpakReadEventFIFOMessageStat_t; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakReadEventFIFOMessage - read from the event fifo + * + * FUNCTION + * This function reads a single event from the event fifo if one is available + * + * RETURNS + * Status code indicating success or a specific error. + * + * Note: This function should be called in a loop until the return status + * indicates that the fifo is empty. + */ +extern gpakReadEventFIFOMessageStat_t gpakReadEventFIFOMessage( + unsigned short int DspId, // DSP identifier + unsigned short int *pChannelId, // pointer to channel identifier + GpakAsyncEventCode_t *pEventCode, // pointer to Event Code + GpakAsyncEventData_t *pEventData // pointer to Event Data Struct + ); + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +/* gpakPingDsp return status values */ +typedef enum +{ + PngSuccess = 0, /* DSP responded successfully */ + PngInvalidDsp = 1, /* invalid DSP identifier */ + PngDspCommFailure = 2 /* error communicating with DSP */ +} gpakPingDspStat_t; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakPingDsp - ping the DSP to see if it's alive + * + * FUNCTION + * This function checks if the DSP is still communicating with the host + * + * RETURNS + * Status code indicating success or a specific error. + */ +extern gpakPingDspStat_t gpakPingDsp( + unsigned short int DspId, // DSP identifier + unsigned short int *pDspSwVersion // DSP software version + ); + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +/* gpakSerialTxFixedValue return status values */ +typedef enum +{ + TfvSuccess = 0, /* operation successful */ + TfvInvalidChannel = 1, /* invalid channel identifier */ + TfvInvalidDsp = 2, /* invalid DSP identifier */ + TfvDspCommFailure = 3 /* failed to communicate with DSP */ +} gpakSerialTxFixedValueStat_t; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakSerialTxFixedValue - transmit a fixed value on a timeslot + * + * FUNCTION + * This function controls transmission of a fixed value out onto a serial + * port's timeslot. + * + * RETURNS + * Status code indicating success or a specific error. + */ +extern gpakSerialTxFixedValueStat_t gpakSerialTxFixedValue( + unsigned short int DspId, // DSP identifier + unsigned short int ChannelId, // channel identifier + GpakSerialPort_t PcmOutPort, // PCM Output Serial Port Id + unsigned short int PcmOutSlot, // PCM Output Time Slot + unsigned short int Value, // 16-bit value + GpakActivation State // activation state + ); + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +/* gpakControlTdmLoopBack return status values */ +typedef enum +{ + ClbSuccess = 0, /* operation successful */ + ClbSerPortInactive = 1, /* serial port is inactive */ + ClbInvalidDsp = 2, /* invalid DSP identifier */ + ClbDspCommFailure = 3 /* failed to communicate with DSP */ +} gpakControlTdmLoopBackStat_t; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakControlTdmLoopBack - control a serial port's loopback state + * + * FUNCTION + * This function enables/disables the tdm input to output looback mode on a + * serial port + * + * RETURNS + * Status code indicating success or a specific error. + */ + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ +gpakControlTdmLoopBackStat_t gpakControlTdmLoopBack( + unsigned short int DspId, // DSP identifier + GpakSerialPort_t SerialPort, // Serial Port Id + GpakActivation LoopBackState // Loopback State + ); + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +/* gpakReadCpuUsage return status values */ +typedef enum +{ + RcuSuccess = 0, /* operation successful */ + RcuInvalidDsp = 1, /* invalid DSP identifier */ + RcuDspCommFailure = 2 /* communication failure */ +} gpakReadCpuUsageStat_t; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakReadCpuUsage - read the cpu usage statistics + * + * FUNCTION + * This function reads cpu utilization from the DSP. + * + * RETURNS + * Status code indicating success or a specific error. + */ +extern gpakReadCpuUsageStat_t gpakReadCpuUsage( + unsigned short int DspId, // DSP identifier + unsigned short int *pPeakUsage, // pointer to peak usage variable + unsigned short int *pPrev1SecPeakUsage // peak usage over previous 1 second + ); + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +/* gpakResetCpuUsageStats return status values */ +typedef enum +{ + RstcSuccess = 0, /* operation successful */ + RstcInvalidDsp = 1, /* invalid DSP identifier */ + RstcDspCommFailure = 2 /* communication failure */ +} gpakResetCpuUsageStat_t; +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakResetCpuUsageStats - reset the cpu usage statistics + * + * FUNCTION + * This function resets the cpu utilization statistics + * + * RETURNS + * Status code indicating success or a specific error. + */ +extern gpakResetCpuUsageStat_t gpakResetCpuUsageStats( + unsigned short int DspId // DSP identifier + ); + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +/* gpakReadFramingStats return status values */ +typedef enum +{ + RfsSuccess = 0, /* operation successful */ + RfsInvalidDsp = 1, /* invalid DSP identifier */ + RfsDspCommFailure = 2 /* communication failure */ +} gpakReadFramingStatsStatus_t; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakReadFramingStats + * + * FUNCTION + * This function reads a DSP's framing interrupt statistics + * + * RETURNS + * Status code indicating success or a specific error. + */ +extern gpakReadFramingStatsStatus_t gpakReadFramingStats( + unsigned short int DspId, // DSP identifier + unsigned short int *pFramingError1Count, // port 1 Framing error count + unsigned short int *pFramingError2Count, // port 2 Framing error count + unsigned short int *pFramingError3Count, // port 3 Framing error count + unsigned short int *pDmaStopErrorCount, // DMA-stoppage error count + unsigned short int *pDmaSlipStatsBuffer // DMA slips count + ); + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +/* gpakResetFramingStats return values */ +typedef enum +{ + RstfSuccess = 0, /* operation successful */ + RstfInvalidDsp = 1, /* invalid DSP identifier */ + RstfDspCommFailure = 2 /* communication failure */ +} gpakResetFramingStatsStatus_t; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakResetFramingStats - reset a DSP's framing interrupt statistics + * + * FUNCTION + * This function resets a DSP's framing interrupt statistics + * + * RETURNS + * Status code indicating success or a specific error. + */ +extern gpakResetFramingStatsStatus_t gpakResetFramingStats( + unsigned short int DspId // DSP identifier + ); + + +typedef enum +{ + RmmSuccess =0, + RmmInvalidDsp = 1, + RmmSizeTooBig = 2, + RmmFailure = 3, + RmmInvalidAddress = 4 + +} gpakReadDSPMemoryStat_t; +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakResetFramingStats - read a section of DSP memory + * to get access DSP registers, since 0x00--0x60 not HPI-accessable + * + * FUNCTION + * This function resets a DSP's framing interrupt statistics + * + * RETURNS + * Status code indicating success or a specific error. + */ + +extern gpakReadDSPMemoryStat_t gpakReadDSPMemoryMap( + unsigned short int DspId, // Dsp Identifier + unsigned short int *pDest, // Buffer on host to hold DSP memory map + DSP_ADDRESS BufrBaseAddress, // DSP memory users want to read out + unsigned short int MemoryLength_Word16 // Length of memory section read out, unit is 16-bit word + ); + +typedef enum +{ + GPIOSuccess =0, + GPIOInvalidDsp = 1, + GPIODspCommFailure = 2 +}gpakAccessGPIOStat_t; + +extern gpakAccessGPIOStat_t gpakAccessGPIO( + unsigned short int DspId, // DSP identifier + GpakGPIOCotrol_t gpakControlGPIO,// select oeration, changeDIR/write/read + unsigned short int *pGPIOValue // pointer for the read/write value or DIR mask + ); + +/* gpakWriteSystemParms return status. */ +typedef enum +{ + WspSuccess = 0, /* System Parameters written successfully */ + WspParmError = 1, /* Write System Parms's Parameter error */ + WspInvalidDsp = 2, /* invalid DSP */ + WspDspCommFailure = 3 /* failed to communicate with DSP */ +} gpakWriteSysParmsStatus_t; + +/* Definition of a System Parameters information structure. */ +typedef struct +{ + /* DTMF Parameters */ + short int MinSigLevel; /* 0 = Disabled, Min Sig Power Level for detection */ + short int SNRFlag; /* 0 = Disabled, relax SNR tolerances */ + short int FreqDeviation; /* 0 = Disabled, X Percent Deviation times 10 (e.g. 1.7% is entered as 17) */ + short int DtmfFwdTwist; /* 0 to 8 db */ + short int DtmfRevTwist; /* 0 to 8 db */ + + short int DtmfValidityMask; /* This flag allows users to relax the trailing conditions of the tone */ + +} GpakSystemParms_t; +/* gpakReadSystemParms return status. */ +typedef enum +{ + RspSuccess = 0, /* System Parameters read successfully */ + RspInvalidDsp = 1, /* invalid DSP */ + RspDspCommFailure = 2 /* failed to communicate with DSP */ +} gpakReadSysParmsStatus_t; + +extern gpakReadSysParmsStatus_t gpakReadSystemParms( + unsigned short int DspId, // DSP identifier + GpakSystemParms_t *pSysParms /* pointer to System Parms info var */ + ); + +extern gpakWriteSysParmsStatus_t gpakWriteSystemParms( + unsigned short int DspId, // DSP identifier + GpakSystemParms_t *pSysParms, /* pointer to System Parms info var */ + unsigned short int UpdateBits, /* input: flags indicating which parms to update */ + GPAK_SysParmsStat_t *pStatus /* pointer to Write System Parms Status */ + ); + +#endif // end multiple inclusion + diff --git a/wctdm24xxp/GpakCust.c b/wctdm24xxp/GpakCust.c new file mode 100644 index 0000000..bdce428 --- /dev/null +++ b/wctdm24xxp/GpakCust.c @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2005, Adaptive Digital Technologies, Inc. + * + * File Name: GpakCust.c + * + * Description: + * This file contains host system dependent functions to support generic + * G.PAK API functions. The file is integrated into the host processor + * connected to C55x G.PAK DSPs via a Host Port Interface. + * + * Note: This file needs to be modified by the G.PAK system integrator. + * + * Version: 1.0 + * + * Revision History: + * 06/15/05 - Initial release. + * + */ + +#include "GpakCust.h" +#include "wctdm24xxp.h" +#include +#include + +char vpm150mtone_to_zaptone(GpakToneCodes_t tone) +{ + switch (tone) { + case DtmfDigit0: + return '0'; + case DtmfDigit1: + return '1'; + case DtmfDigit2: + return '2'; + case DtmfDigit3: + return '3'; + case DtmfDigit4: + return '4'; + case DtmfDigit5: + return '5'; + case DtmfDigit6: + return '6'; + case DtmfDigit7: + return '7'; + case DtmfDigit8: + return '8'; + case DtmfDigit9: + return '9'; + case DtmfDigitPnd: + return '#'; + case DtmfDigitSt: + return '*'; + case DtmfDigitA: + return 'A'; + case DtmfDigitB: + return 'B'; + case DtmfDigitC: + return 'C'; + case DtmfDigitD: + return 'D'; + case EndofCngDigit: + return 'f'; + default: + return 0; + } +} + +static inline struct wctdm * wc_find_iface(unsigned short dspid) +{ + int i; + struct wctdm *ret = NULL; + unsigned long flags; + + spin_lock_irqsave(&ifacelock, flags); + for (i = 0; i < WC_MAX_IFACES; i++) + if (ifaces[i] && ifaces[i]->vpm150m && (ifaces[i]->vpm150m->dspid == dspid)) + ret = ifaces[i]; + spin_unlock_irqrestore(&ifacelock, flags); + + return ret; +} + +static inline struct vpm150m_cmd * vpm150m_empty_slot(struct wctdm *wc) +{ + int x; + + for (x = 0; x < VPM150M_MAX_COMMANDS; x++) + if (!wc->vpm150m->cmdq[x].desc) + return &wc->vpm150m->cmdq[x]; + return NULL; +} + + +unsigned short wctdm_vpm150m_getreg_full(struct wctdm *wc, int pagechange, unsigned int len, unsigned short addr, unsigned short *outbuf) +{ + unsigned long flags; + volatile struct vpm150m_cmd *hit; + unsigned short ret = 0; + int i; + do { + spin_lock_irqsave(&wc->reglock, flags); + hit = vpm150m_empty_slot(wc); + if (hit) { + hit->desc = __VPM150M_RD; + if (pagechange) { + hit->desc |= __VPM150M_RWPAGE; + } + hit->datalen = len; + hit->addr = addr; + for (i = 0; i < hit->datalen; i++) + hit->data[i] = 0x0000; + } + spin_unlock_irqrestore(&wc->reglock, flags); + if (!hit) { + if ((ret = schluffen(&wc->regq))) + return ret; + } + } while (!hit); + do { + spin_lock_irqsave(&wc->reglock, flags); + if (hit->desc & __VPM150M_FIN) { + for (i = 0; i < hit->datalen; i++) + outbuf[i] = hit->data[i]; + hit->desc = 0; + hit = NULL; + } + spin_unlock_irqrestore(&wc->reglock, flags); + if (hit) { + if ((ret = schluffen(&wc->regq))) + return ret; + } + } while (hit); + return ret; +} + +int wctdm_vpm150m_setreg_full(struct wctdm *wc, int pagechange, unsigned int len, unsigned int addr, unsigned short *data) +{ + unsigned long flags; + struct vpm150m_cmd *hit; + int ret, i; + do { + spin_lock_irqsave(&wc->reglock, flags); + hit = vpm150m_empty_slot(wc); + if (hit) { + hit->desc = __VPM150M_WR; + if (pagechange) + hit->desc |= __VPM150M_RWPAGE; + hit->addr = addr; + hit->datalen = len; + for (i = 0; i < len; i++) + hit->data[i] = data[i]; + } + spin_unlock_irqrestore(&wc->reglock, flags); + if (!hit) { + if ((ret = schluffen(&wc->regq))) + return ret; + } + } while (!hit); + return (hit) ? 0 : -1; +} + +int wctdm_vpm150m_setpage(struct wctdm *wc, unsigned short addr) +{ + addr &= 0xf; + /* Let's optimize this a little bit */ + if (wc->vpm150m->curpage == addr) + return 0; + else { + wc->vpm150m->curpage = addr; + } + + return wctdm_vpm150m_setreg_full(wc, 1, 1, 0, &addr); +} + +unsigned char wctdm_vpm150m_getpage(struct wctdm *wc) +{ + unsigned short res; + wctdm_vpm150m_getreg_full(wc, 1, 1, 0, &res); + return res; +} + +unsigned short wctdm_vpm150m_getreg(struct wctdm *wc, unsigned int len, unsigned int addr, unsigned short *data) +{ + unsigned short res; + wctdm_vpm150m_setpage(wc, addr >> 16); + if ((addr >> 16) != ((addr + len) >> 16)) + printk("getreg: You found it!\n"); + res = wctdm_vpm150m_getreg_full(wc, 0, len, addr & 0xffff, data); + return res; +} + +int wctdm_vpm150m_setreg(struct wctdm *wc, unsigned int len, unsigned int addr, unsigned short *data) +{ + int res; + wctdm_vpm150m_setpage(wc, addr >> 16); + if ((addr >> 16) != ((addr + len) >> 16)) + printk("getreg: You found it!\n"); + res = wctdm_vpm150m_setreg_full(wc, 0, len, addr & 0xffff, data); + return res; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakReadDspMemory - Read DSP memory. + * + * FUNCTION + * This function reads a contiguous block of words from DSP memory starting at + * the specified address. + * + * RETURNS + * nothing + * + */ +void gpakReadDspMemory( + unsigned short int DspId, /* DSP Identifier (0 to MAX_DSP_CORES-1) */ + DSP_ADDRESS DspAddress, /* DSP's memory address of first word */ + unsigned int NumWords, /* number of contiguous words to read */ + DSP_WORD *pWordValues /* pointer to array of word values variable */ + ) +{ + struct wctdm *wc = wc_find_iface(DspId); + int i; + int transcount; + + //printk("Reading %d words from memory\n"); + if (wc && wc->vpm150m) { + for (i = 0; i < NumWords;) { + if ((NumWords - i) > VPM150M_MAX_DATA) + transcount = VPM150M_MAX_DATA; + else + transcount = NumWords - i; + wctdm_vpm150m_getreg(wc, transcount, DspAddress + i, &pWordValues[i]); + i += transcount; + } + } + return; +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakWriteDspMemory - Write DSP memory. + * + * FUNCTION + * This function writes a contiguous block of words to DSP memory starting at + * the specified address. + * + * RETURNS + * nothing + * + */ +void gpakWriteDspMemory( + unsigned short int DspId, /* DSP Identifier (0 to MAX_DSP_CORES-1) */ + DSP_ADDRESS DspAddress, /* DSP's memory address of first word */ + unsigned int NumWords, /* number of contiguous words to write */ + DSP_WORD *pWordValues /* pointer to array of word values to write */ + ) +{ + + struct wctdm *wc = wc_find_iface(DspId); + int i; + int transcount; + + //printk("Writing %d words to memory\n", NumWords); + if (wc && wc->vpm150m) { + for (i = 0; i < NumWords;) { + if ((NumWords - i) > VPM150M_MAX_DATA) + transcount = VPM150M_MAX_DATA; + else + transcount = NumWords - i; + + wctdm_vpm150m_setreg(wc, transcount, DspAddress + i, &pWordValues[i]); + i += transcount; + } +#if 0 + for (i = 0; i < NumWords; i++) { + if (wctdm_vpm150m_getreg(wc, DspAddress + i) != pWordValues[i]) { + printk("Error in write. Address %x is not %x\n", DspAddress + i, pWordValues[i]); + } + } +#endif + } + return; + +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakHostDelay - Delay for a fixed time interval. + * + * FUNCTION + * This function delays for a fixed time interval before returning. The time + * interval is the Host Port Interface sampling period when polling a DSP for + * replies to command messages. + * + * RETURNS + * nothing + * + */ +void gpakHostDelay(void) +{ +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakLockAccess - Lock access to the specified DSP. + * + * FUNCTION + * This function aquires exclusive access to the specified DSP. + * + * RETURNS + * nothing + * + */ +void gpakLockAccess(unsigned short DspId) +{ + struct wctdm *wc; + + wc = wc_find_iface(DspId); + + if (wc) { + struct vpm150m *vpm = wc->vpm150m; + + if (vpm) + down_interruptible(&vpm->sem); + } +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakUnlockAccess - Unlock access to the specified DSP. + * + * FUNCTION + * This function releases exclusive access to the specified DSP. + * + * RETURNS + * nothing + * + */ +void gpakUnlockAccess(unsigned short DspId) +{ + struct wctdm *wc; + + wc = wc_find_iface(DspId); + + if (wc) { + struct vpm150m *vpm = wc->vpm150m; + + if (vpm) + up(&vpm->sem); + } +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakReadFile - Read a block of bytes from a G.PAK Download file. + * + * FUNCTION + * This function reads a contiguous block of bytes from a G.PAK Download file + * starting at the current file position. + * + * RETURNS + * The number of bytes read from the file. + * -1 indicates an error occurred. + * 0 indicates all bytes have been read (end of file) + * + */ +int gpakReadFile( + GPAK_FILE_ID FileId, /* G.PAK Download File Identifier */ + unsigned char *pBuffer, /* pointer to buffer for storing bytes */ + unsigned int NumBytes /* number of bytes to read */ + ) +{ + struct wctdm_firmware *fw = FileId; + unsigned int i, count; + + if (!fw || !fw->fw) + return -1; + + if (NumBytes > (fw->fw->size - fw->offset)) + count = fw->fw->size - fw->offset; + else + count = NumBytes; + + for (i = 0; i < count; i++) + pBuffer[i] = fw->fw->data[fw->offset + i]; + + fw->offset += count; + + return count; +} diff --git a/wctdm24xxp/GpakCust.h b/wctdm24xxp/GpakCust.h new file mode 100644 index 0000000..dfb6103 --- /dev/null +++ b/wctdm24xxp/GpakCust.h @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2005, Adaptive Digital Technologies, Inc. + * + * File Name: GpakCust.h + * + * Description: + * This file contains host system dependent definitions and prototypes of + * functions to support generic G.PAK API functions. The file is used when + * integrating G.PAK API functions in a specific host processor environment. + * + * Note: This file may need to be modified by the G.PAK system integrator. + * + * Version: 1.0 + * + * Revision History: + * 06/15/05 - Initial release. + * + */ + +#ifndef _GPAKCUST_H /* prevent multiple inclusion */ +#define _GPAKCUST_H + +#include "wctdm24xxp.h" +#include +#include "gpakenum.h" + + +struct wctdm_firmware { + const struct firmware *fw; + unsigned int offset; +}; + +/* Host and DSP system dependent related definitions. */ +#define MAX_DSP_CORES 128 /* maximum number of DSP cores */ +//#define MAX_CONFS 1 /* maximum number of conferences */ +//#define MAX_PKT_CHANNELS 8 /* maximum number of packet channels */ +#define MAX_CHANNELS 32 /* maximum number of channels */ +#define MAX_WAIT_LOOPS 50 /* max number of wait delay loops */ +#define DSP_IFBLK_ADDRESS 0x0100 /* DSP address of I/F block pointer */ +#define DOWNLOAD_BLOCK_SIZE 512 /* download block size (DSP words) */ +//#define MAX_CIDPAYLOAD_BYTES 512 /* max size of a CID payload (octets) */ +typedef unsigned short DSP_WORD; /* 16 bit DSP word */ +typedef unsigned int DSP_ADDRESS; /* 32 bit DSP address */ +typedef struct wctdm_firmware* GPAK_FILE_ID; /* G.PAK Download file identifier */ + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakReadDspMemory - Read DSP memory. + * + * FUNCTION + * This function reads a contiguous block of words from DSP memory starting at + * the specified address. + * + * RETURNS + * nothing + * + */ +extern void gpakReadDspMemory( + unsigned short int DspId, /* DSP Identifier (0 to MAX_DSP_CORES-1) */ + DSP_ADDRESS DspAddress, /* DSP's memory address of first word */ + unsigned int NumWords, /* number of contiguous words to read */ + DSP_WORD *pWordValues /* pointer to array of word values variable */ + ); + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakWriteDspMemory - Write DSP memory. + * + * FUNCTION + * This function writes a contiguous block of words to DSP memory starting at + * the specified address. + * + * RETURNS + * nothing + * + */ +extern void gpakWriteDspMemory( + unsigned short int DspId, /* DSP Identifier (0 to MAX_DSP_CORES-1) */ + DSP_ADDRESS DspAddress, /* DSP's memory address of first word */ + unsigned int NumWords, /* number of contiguous words to write */ + DSP_WORD *pWordValues /* pointer to array of word values to write */ + ); + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakHostDelay - Delay for a fixed time interval. + * + * FUNCTION + * This function delays for a fixed time interval before returning. The time + * interval is the Host Port Interface sampling period when polling a DSP for + * replies to command messages. + * + * RETURNS + * nothing + * + */ +extern void gpakHostDelay(void); + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakLockAccess - Lock access to the specified DSP. + * + * FUNCTION + * This function aquires exclusive access to the specified DSP. + * + * RETURNS + * nothing + * + */ +extern void gpakLockAccess( + unsigned short int DspId /* DSP Identifier (0 to MAX_DSP_CORES-1) */ + ); + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakUnlockAccess - Unlock access to the specified DSP. + * + * FUNCTION + * This function releases exclusive access to the specified DSP. + * + * RETURNS + * nothing + * + */ +extern void gpakUnlockAccess( + unsigned short int DspId /* DSP Identifier (0 to MAX_DSP_CORES-1) */ + ); + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakReadFile - Read a block of bytes from a G.PAK Download file. + * + * FUNCTION + * This function reads a contiguous block of bytes from a G.PAK Download file + * starting at the current file position. + * + * RETURNS + * The number of bytes read from the file. + * -1 indicates an error occurred. + * 0 indicates all bytes have been read (end of file) + * + */ +extern int gpakReadFile( + GPAK_FILE_ID FileId, /* G.PAK Download File Identifier */ + unsigned char *pBuffer, /* pointer to buffer for storing bytes */ + unsigned int NumBytes /* number of bytes to read */ + ); + + +unsigned char wctdm_vpm150m_getpage(struct wctdm *wc); + +int wctdm_vpm150m_setpage(struct wctdm *wc, unsigned short addr); + +int wctdm_vpm150m_setreg(struct wctdm *wc, unsigned int len, unsigned int addr, unsigned short *data); + +unsigned short wctdm_vpm150m_getreg(struct wctdm *wc, unsigned int len, unsigned int addr, unsigned short *data); + +char vpm150mtone_to_zaptone(GpakToneCodes_t tone); + +#endif /* prevent multiple inclusion */ + + diff --git a/wctdm24xxp/GpakHpi.h b/wctdm24xxp/GpakHpi.h new file mode 100644 index 0000000..9dbf67a --- /dev/null +++ b/wctdm24xxp/GpakHpi.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2001, Adaptive Digital Technologies, Inc. + * + * File Name: GpakHpi.h + * + * Description: + * This file contains common definitions related to the G.PAK interface + * between a host processor and a DSP processor via the Host Port Interface. + * + * Version: 1.0 + * + * Revision History: + * 10/17/01 - Initial release. + * + */ + +#ifndef _GPAKHPI_H /* prevent multiple inclusion */ +#define _GPAKHPI_H + + +/* Definition of G.PAK Command/Reply message type codes. */ +#define MSG_NULL_REPLY 0 /* Null Reply (unsupported Command) */ +#define MSG_SYS_CONFIG_RQST 1 /* System Configuration Request */ +#define MSG_SYS_CONFIG_REPLY 2 /* System Configuration Reply */ +#define MSG_READ_SYS_PARMS 3 /* Read System Parameters */ +#define MSG_READ_SYS_PARMS_REPLY 4 /* Read System Parameters Reply */ +#define MSG_WRITE_SYS_PARMS 5 /* Write System Parameters */ +#define MSG_WRITE_SYS_PARMS_REPLY 6 /* Write System Parameters Reply */ +#define MSG_CONFIGURE_PORTS 7 /* Configure Serial Ports */ +#define MSG_CONFIG_PORTS_REPLY 8 /* Configure Serial Ports Reply */ +#define MSG_CONFIGURE_CHANNEL 9 /* Configure Channel */ +#define MSG_CONFIG_CHAN_REPLY 10 /* Configure Channel Reply */ +#define MSG_TEAR_DOWN_CHANNEL 11 /* Tear Down Channel */ +#define MSG_TEAR_DOWN_REPLY 12 /* Tear Down Channel Reply */ +#define MSG_CHAN_STATUS_RQST 13 /* Channel Status Request */ +#define MSG_CHAN_STATUS_REPLY 14 /* Channel Status Reply */ + +#define MSG_TEST_MODE 17 /* Configure/Perform Test Mode */ +#define MSG_TEST_REPLY 18 /* Configure/Perform Test Mode Reply */ + +#define MSG_ALG_CONTROL 27 /* algorithm control */ +#define MSG_ALG_CONTROL_REPLY 28 /* algorithm control reply */ +#define MSG_GET_TXCID_ADDRESS 29 /* get tx cid buffer start address */ +#define MSG_GET_TXCID_ADDRESS_REPLY 30 /* get tx cid buffer start addr reply */ + +#define MSG_PING 35 /* ping command */ +#define MSG_PING_REPLY 36 /* ping command reply */ +#define MSG_SERIAL_TXVAL 37 /* transmit serial fixed value */ +#define MSG_SERIAL_TXVAL_REPLY 38 /* transmit serial fixed value reply */ +#define MSG_TDM_LOOPBACK 39 /* tdm loopback control */ +#define MSG_TDM_LOOPBACK_REPLY 40 /* tdm loopback control reply */ +#define MSG_RESET_USAGE_STATS 41 /* reset cpu usage stats */ +#define MSG_RESET_USAGE_STATS_REPLY 42 /* reset cpu usage stats reply */ + +#define MSG_RESET_FRAME_STATS 47 /* reset framing stats */ +#define MSG_RESET_FRAME_STATS_REPLY 48 /* reset framing stats reply */ + +#define MSG_READ_DSP_MEMORY 49 /* read small section of DSP's memory */ +#define MSG_READ_DSP_MEMORY_REPLY 50 /* read memory reply */ + +#define MSG_ACCESSGPIO 51 +#define MSG_ACCESSGPIO_REPLY 52 +#endif /* prevent multiple inclusion */ diff --git a/wctdm24xxp/Makefile b/wctdm24xxp/Makefile new file mode 100644 index 0000000..9927928 --- /dev/null +++ b/wctdm24xxp/Makefile @@ -0,0 +1,22 @@ +ifneq ($(KBUILD_EXTMOD),) + +include $(src)/Makefile.kernel26 + +else + +all: wctdm24xxp.o + +%.o: %.c + $(CC) $(KFLAGS) -o $@ -c $< + +base.o: ../zaptel.h GpakCust.h ../wctdm.h + +GpakCust.o: GpakCust.h + +wctdm24xxp.o: base.o GpakCust.o GpakApi.o + $(LD) -r -o $@ $^ + +clean: + rm -f *.o + +endif diff --git a/wctdm24xxp/Makefile.kernel26 b/wctdm24xxp/Makefile.kernel26 new file mode 100644 index 0000000..f9fbf0c --- /dev/null +++ b/wctdm24xxp/Makefile.kernel26 @@ -0,0 +1,12 @@ +obj-m += wctdm24xxp.o + +EXTRA_CFLAGS := -I$(src)/.. -Wno-undef + +wctdm24xxp-objs := base.o GpakCust.o GpakApi.o + +$(obj)/base.o: $(src)/GpakCust.h $(src)/wctdm24xxp.h +$(obj)/base.o: $(src)/../zaptel.h + +$(obj)/GpakCust.o: $(src)/GpakCust.h + +$(obj)/GpakApi.o: $(src)/GpakApi.h diff --git a/wctdm24xxp/VPMADT032.bin b/wctdm24xxp/VPMADT032.bin new file mode 100644 index 0000000..e882060 Binary files /dev/null and b/wctdm24xxp/VPMADT032.bin differ diff --git a/wctdm24xxp/base.c b/wctdm24xxp/base.c new file mode 100644 index 0000000..a1abe1d --- /dev/null +++ b/wctdm24xxp/base.c @@ -0,0 +1,4201 @@ +/* + * Wilcard TDM2400P TDM FXS/FXO Interface Driver for Zapata Telephony interface + * + * Written by Mark Spencer + * Support for TDM800P and VPM150M by Matthew Fredrickson + * + * Copyright (C) 2005,2006, Digium, Inc. + * All rights reserved. + * + * Sections for QRV cards written by Jim Dixon + * Copyright (C) 2006, Jim Dixon and QRV Communications + * 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. + * + */ +/* For QRV DRI cards, gain is signed short, expressed in hundredths of +db (in reference to 1v Peak @ 1000Hz) , as follows: + +Rx Gain: -11.99 to 15.52 db +Tx Gain - No Pre-Emphasis: -35.99 to 12.00 db +Tx Gain - W/Pre-Emphasis: -23.99 to 0.00 db +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../proslic.h" +#include "../wctdm.h" + +#include "GpakCust.h" +#include "GpakApi.h" + +/* Comment to disable VPM support */ + +#include "wctdm24xxp.h" + +/* + Experimental max loop current limit for the proslic + Loop current limit is from 20 mA to 41 mA in steps of 3 + (according to datasheet) + So set the value below to: + 0x00 : 20mA (default) + 0x01 : 23mA + 0x02 : 26mA + 0x03 : 29mA + 0x04 : 32mA + 0x05 : 35mA + 0x06 : 37mA + 0x07 : 41mA +*/ +static int loopcurrent = 20; + +static alpha indirect_regs[] = +{ +{0,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", 0x4C00 /* 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}, +}; + +#ifdef FANCY_ECHOCAN +static char ectab[] = { +0, 0, 0, 1, 2, 3, 4, 6, 8, 9, 11, 13, 16, 18, 20, 22, 24, 25, 27, 28, 29, 30, 31, 31, 32, +32, 32, 32, 32, 32, 32, 32, 32, 32, 32 ,32 ,32, 32, +32, 32, 32, 32, 32, 32, 32, 32, 32, 32 ,32 ,32, 32, +32, 32, 32, 32, 32, 32, 32, 32, 32, 32 ,32 ,32, 32, +31, 31, 30, 29, 28, 27, 25, 23, 22, 20, 18, 16, 13, 11, 9, 8, 6, 4, 3, 2, 1, 0, 0, +}; +static int ectrans[4] = { 0, 1, 3, 2 }; +#define EC_SIZE (sizeof(ectab)) +#define EC_SIZE_Q (sizeof(ectab) / 4) +#endif + +/* Undefine to enable Power alarm / Transistor debug -- note: do not + enable for normal operation! */ +/* #define PAQ_DEBUG */ + +static struct fxo_mode { + char *name; + /* FXO */ + int ohs; + int ohs2; + int rz; + int rt; + int ilim; + int dcv; + int mini; + int acim; + int ring_osc; + int ring_x; +} fxo_modes[] = +{ + { "FCC", 0, 0, 0, 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, }, +}; + +#ifdef STANDALONE_ZAPATA +#include "zaptel.h" +#else +#include +#endif + +#ifdef LINUX26 +#include +#endif + + +#define DEBUG_CARD (1 << 0) +#define DEBUG_ECHOCAN (1 << 1) + +struct wctdm_desc { + char *name; + int flags; + int ports; +}; + +static struct wctdm_desc wctdm2400 = { "Wildcard TDM2400P", 0, 24 }; +static struct wctdm_desc wctdm800 = { "Wildcard TDM800P", 0, 8 }; +static struct wctdm_desc wcaex2400 = { "Wildcard AEX2400", 0, 24 }; +static struct wctdm_desc wcaex800 = { "Wildcard AEX800", 0, 8 }; + +static int acim2tiss[16] = { 0x0, 0x1, 0x4, 0x5, 0x7, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x2, 0x0, 0x3 }; + +struct wctdm *ifaces[WC_MAX_IFACES]; +spinlock_t ifacelock = SPIN_LOCK_UNLOCKED; + +static void wctdm_release(struct wctdm *wc); + +static int battdebounce = DEFAULT_BATT_DEBOUNCE; +static int battthresh = DEFAULT_BATT_THRESH; +static int debug = 0; +static int robust = 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 fxo_addrs[4] = { 0x00, 0x08, 0x04, 0x0c }; +static int fxotxgain = 0; +static int fxorxgain = 0; +static int fxstxgain = 0; +static int fxsrxgain = 0; +static int nativebridge = 1; +#ifdef VPM_SUPPORT +static int vpmsupport = 1; +static int vpmdtmfsupport = 0; +#define VPM_DEFAULT_DTMFTHRESHOLD 1250 +static int dtmfthreshold = VPM_DEFAULT_DTMFTHRESHOLD; + +static const char *vpm150m_firmware = "VPMADT032.bin"; +#endif + +static int wctdm_init_proslic(struct wctdm *wc, int card, int fast , int manual, int sane); + +/* sleep in user space until woken up. Equivilant of tsleep() in BSD */ +int schluffen(wait_queue_head_t *q) +{ + DECLARE_WAITQUEUE(wait, current); + add_wait_queue(q, &wait); + current->state = TASK_INTERRUPTIBLE; + if (!signal_pending(current)) schedule(); + current->state = TASK_RUNNING; + remove_wait_queue(q, &wait); + if (signal_pending(current)) return -ERESTARTSYS; + return(0); +} + +static inline int empty_slot(struct wctdm *wc, int card) +{ + int x; + for (x=0;xcmdq[card].cmds[x]) + return x; + } + return -1; +} + +#ifdef VPM_SUPPORT +static inline void cmd_dequeue_vpm150m(struct wctdm *wc, volatile unsigned char *writechunk, int whichframe) +{ + unsigned long flags; + struct vpm150m_cmd *curcmd = NULL; + struct vpm150m *vpm150m = wc->vpm150m; + int x; + unsigned char leds = ~((wc->intcount / 1000) % 8) & 0x7; + + /* Skip audio */ + writechunk += 24; + + spin_lock_irqsave(&wc->reglock, flags); + + if (test_bit(VPM150M_SPIRESET, &vpm150m->control) || test_bit(VPM150M_HPIRESET, &vpm150m->control)) { + if (debug & DEBUG_ECHOCAN) + printk("HW Resetting VPMADT032...\n"); + for (x = 24; x < 28; x++) { + if (x == 24) { + if (test_and_clear_bit(VPM150M_SPIRESET, &vpm150m->control)) + writechunk[CMD_BYTE(x, 0, 0)] = 0x08; + else if (test_and_clear_bit(VPM150M_HPIRESET, &vpm150m->control)) + writechunk[CMD_BYTE(x, 0, 0)] = 0x0b; + } else + writechunk[CMD_BYTE(x, 0, 0)] = 0x00 | leds; + writechunk[CMD_BYTE(x, 1, 0)] = 0; + writechunk[CMD_BYTE(x, 2, 0)] = 0x00; + } + spin_unlock_irqrestore(&wc->reglock, flags); + return; + } + + + /* Search for something waiting to transmit */ + for (x = 0; x < VPM150M_MAX_COMMANDS; x++) { + if ((vpm150m->cmdq[x].desc & (__VPM150M_RD | __VPM150M_WR)) && + !(vpm150m->cmdq[x].desc & (__VPM150M_FIN | __VPM150M_TX))) { + curcmd = &vpm150m->cmdq[x]; + curcmd->txident = wc->txident; + curcmd->desc |= __VPM150M_TX; + break; + } + } + if (curcmd) { +#if 0 + printk("Found command txident = %d, desc = 0x%x, addr = 0x%x, data = 0x%x\n", curcmd->txident, curcmd->desc, curcmd->addr, curcmd->data); +#endif + if (curcmd->desc & __VPM150M_RWPAGE) { + /* Set CTRL access to page*/ + writechunk[CMD_BYTE(24, 0, 0)] = (0x8 << 4); + writechunk[CMD_BYTE(24, 1, 0)] = 0; + writechunk[CMD_BYTE(24, 2, 0)] = 0x20; + + /* Do a page write */ + if (curcmd->desc & __VPM150M_WR) + writechunk[CMD_BYTE(25, 0, 0)] = ((0x8 | 0x4) << 4); + else + writechunk[CMD_BYTE(25, 0, 0)] = ((0x8 | 0x4 | 0x1) << 4); + writechunk[CMD_BYTE(25, 1, 0)] = 0; + if (curcmd->desc & __VPM150M_WR) + writechunk[CMD_BYTE(25, 2, 0)] = curcmd->data[0] & 0xf; + else + writechunk[CMD_BYTE(25, 2, 0)] = 0; + + /* Clear XADD */ + writechunk[CMD_BYTE(26, 0, 0)] = (0x8 << 4); + writechunk[CMD_BYTE(26, 1, 0)] = 0; + writechunk[CMD_BYTE(26, 2, 0)] = 0; + + /* Fill in to buffer to size */ + writechunk[CMD_BYTE(27, 0, 0)] = 0; + writechunk[CMD_BYTE(27, 1, 0)] = 0; + writechunk[CMD_BYTE(27, 2, 0)] = 0; + + } else { + /* Set address */ + writechunk[CMD_BYTE(24, 0, 0)] = ((0x8 | 0x4) << 4); + writechunk[CMD_BYTE(24, 1, 0)] = (curcmd->addr >> 8) & 0xff; + writechunk[CMD_BYTE(24, 2, 0)] = curcmd->addr & 0xff; + + /* Send/Get our data */ + if (curcmd->desc & __VPM150M_WR) { + if (curcmd->datalen > 1) + writechunk[CMD_BYTE(25, 0, 0)] = ((0x8 | (0x1 << 1)) << 4); + else + writechunk[CMD_BYTE(25, 0, 0)] = ((0x8 | (0x3 << 1)) << 4); + } else + if (curcmd->datalen > 1) + writechunk[CMD_BYTE(25, 0, 0)] = ((0x8 | (0x1 << 1) | 0x1) << 4); + else + writechunk[CMD_BYTE(25, 0, 0)] = ((0x8 | (0x3 << 1) | 0x1) << 4); + writechunk[CMD_BYTE(25, 1, 0)] = (curcmd->data[0] >> 8) & 0xff; + writechunk[CMD_BYTE(25, 2, 0)] = curcmd->data[0] & 0xff; + + if (curcmd->datalen > 1) { + if (curcmd->desc & __VPM150M_WR) + writechunk[CMD_BYTE(26, 0, 0)] = ((0x8 | (0x1 << 1)) << 4); + else + writechunk[CMD_BYTE(26, 0, 0)] = ((0x8 | (0x1 << 1) | 0x1) << 4); + writechunk[CMD_BYTE(26, 1, 0)] = (curcmd->data[1] >> 8) & 0xff; + writechunk[CMD_BYTE(26, 2, 0)] = curcmd->data[1] & 0xff; + } else { + /* Fill in the rest */ + writechunk[CMD_BYTE(26, 0, 0)] = 0; + writechunk[CMD_BYTE(26, 1, 0)] = 0; + writechunk[CMD_BYTE(26, 2, 0)] = 0; + } + + if (curcmd->datalen > 2) { + if (curcmd->desc & __VPM150M_WR) + writechunk[CMD_BYTE(27, 0, 0)] = ((0x8 | (0x1 << 1)) << 4); + else + writechunk[CMD_BYTE(27, 0, 0)] = ((0x8 | (0x1 << 1) | 0x1) << 4); + writechunk[CMD_BYTE(27, 1, 0)] = (curcmd->data[2] >> 8) & 0xff; + writechunk[CMD_BYTE(27, 2, 0)] = curcmd->data[2] & 0xff; + } else { + /* Fill in the rest */ + writechunk[CMD_BYTE(27, 0, 0)] = 0; + writechunk[CMD_BYTE(27, 1, 0)] = 0; + writechunk[CMD_BYTE(27, 2, 0)] = 0; + } + + + } + } else if (test_and_clear_bit(VPM150M_SWRESET, &vpm150m->control)) { + printk("Booting VPMADT032\n"); + for (x = 24; x < 28; x++) { + if (x == 24) + writechunk[CMD_BYTE(x, 0, 0)] = (0x8 << 4); + else + writechunk[CMD_BYTE(x, 0, 0)] = 0x00; + writechunk[CMD_BYTE(x, 1, 0)] = 0; + if (x == 24) + writechunk[CMD_BYTE(x, 2, 0)] = 0x01; + else + writechunk[CMD_BYTE(x, 2, 0)] = 0x00; + } + } else { + for (x = 24; x < 28; x++) { + writechunk[CMD_BYTE(x, 0, 0)] = 0x00; + writechunk[CMD_BYTE(x, 1, 0)] = 0x00; + writechunk[CMD_BYTE(x, 2, 0)] = 0x00; + } + } + + /* Add our leds in */ + for (x = 24; x < 28; x++) + writechunk[CMD_BYTE(x, 0, 0)] |= leds; + + /* Now let's figure out if we need to check for DTMF */ + if (test_bit(VPM150M_ACTIVE, &vpm150m->control) && !whichframe && !(wc->intcount % 100)) + queue_work(vpm150m->wq, &vpm150m->work); + + spin_unlock_irqrestore(&wc->reglock, flags); +} +#endif /* VPM_SUPPORT */ + +static inline void cmd_dequeue(struct wctdm *wc, volatile unsigned char *writechunk, int card, int pos) +{ + unsigned long flags; + unsigned int curcmd=0; + int x; + int subaddr = card & 0x3; +#ifdef FANCY_ECHOCAN + int ecval; + ecval = wc->echocanpos; + ecval += EC_SIZE_Q * ectrans[(card & 0x3)]; + ecval = ecval % EC_SIZE; +#endif + + /* if a QRV card, map it to its first channel */ + if ((wc->modtype[card] == MOD_TYPE_QRV) && (card & 3)) + { + return; + } + if (wc->altcs[card]) + subaddr = 0; + + + + /* Skip audio */ + writechunk += 24; + spin_lock_irqsave(&wc->reglock, flags); + /* Search for something waiting to transmit */ + if (pos) { + for (x=0;xcmdq[card].cmds[x] & (__CMD_RD | __CMD_WR)) && + !(wc->cmdq[card].cmds[x] & (__CMD_TX | __CMD_FIN))) { + curcmd = wc->cmdq[card].cmds[x]; +#if 0 + printk("Transmitting command '%08x' in slot %d\n", wc->cmdq[card].cmds[x], wc->txident); +#endif + wc->cmdq[card].cmds[x] |= (wc->txident << 24) | __CMD_TX; + break; + } + } + } + if (!curcmd) { + /* If nothing else, use filler */ + if (wc->modtype[card] == MOD_TYPE_FXS) + curcmd = CMD_RD(64); + else if (wc->modtype[card] == MOD_TYPE_FXO) + curcmd = CMD_RD(12); + else if (wc->modtype[card] == MOD_TYPE_QRV) + curcmd = CMD_RD(3); + else if (wc->modtype[card] == MOD_TYPE_VPM) { +#ifdef FANCY_ECHOCAN + if (wc->blinktimer >= 0xf) { + curcmd = CMD_WR(0x1ab, 0x0f); + } else if (wc->blinktimer == (ectab[ecval] >> 1)) { + curcmd = CMD_WR(0x1ab, 0x00); + } else +#endif + curcmd = CMD_RD(0x1a0); + } + } + if (wc->modtype[card] == MOD_TYPE_FXS) { + writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = (1 << (subaddr)); + if (curcmd & __CMD_WR) + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0x7f; + else + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x80 | ((curcmd >> 8) & 0x7f); + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; + } else if (wc->modtype[card] == MOD_TYPE_FXO) { + if (curcmd & __CMD_WR) + writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x20 | fxo_addrs[subaddr]; + else + writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x60 | fxo_addrs[subaddr]; + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0xff; + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; + } else if (wc->modtype[card] == MOD_TYPE_FXSINIT) { + /* Special case, we initialize the FXS's into the three-byte command mode then + switch to the regular mode. To send it into thee byte mode, treat the path as + 6 two-byte commands and in the last one we initialize register 0 to 0x80. All modules + read this as the command to switch to daisy chain mode and we're done. */ + writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00; + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00; + if ((card & 0x1) == 0x1) + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x80; + else + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00; +#ifdef VPM_SUPPORT + } else if (wc->modtype[card] == MOD_TYPE_VPM) { + if (curcmd & __CMD_WR) + writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = ((card & 0x3) << 4) | 0xc | ((curcmd >> 16) & 0x1); + else + writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = ((card & 0x3) << 4) | 0xa | ((curcmd >> 16) & 0x1); + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0xff; + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; + } else if (wc->modtype[card] == MOD_TYPE_VPM150M) { +#endif + } else if (wc->modtype[card] == MOD_TYPE_QRV) { + + writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00; + if (!curcmd) + { + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00; + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00; + } + else + { + if (curcmd & __CMD_WR) + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x40 | ((curcmd >> 8) & 0x3f); + else + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0xc0 | ((curcmd >> 8) & 0x3f); + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; + } + } else if (wc->modtype[card] == MOD_TYPE_NONE) { + writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00; + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00; + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00; + } +#if 0 + /* XXX */ + if (cmddesc < 40) + printk("Pass %d, card = %d (modtype=%d), pos = %d, CMD_BYTES = %d,%d,%d, (%02x,%02x,%02x) curcmd = %08x\n", cmddesc, card, wc->modtype[card], pos, CMD_BYTE(card, 0), CMD_BYTE(card, 1), CMD_BYTE(card, 2), writechunk[CMD_BYTE(card, 0)], writechunk[CMD_BYTE(card, 1)], writechunk[CMD_BYTE(card, 2)], curcmd); +#endif + spin_unlock_irqrestore(&wc->reglock, flags); +#if 0 + /* XXX */ + cmddesc++; +#endif +} + +#ifdef VPM_SUPPORT +static inline void cmd_decifer_vpm150m(struct wctdm *wc, volatile unsigned char *readchunk) +{ + unsigned long flags; + unsigned char ident; + int x, i; + + /* Skip audio */ + readchunk += 24; + spin_lock_irqsave(&wc->reglock, flags); + /* Search for any pending results */ + for (x = 0; x < VPM150M_MAX_COMMANDS; x++) { + if ((wc->vpm150m->cmdq[x].desc & (__VPM150M_RD | __VPM150M_WR)) && + (wc->vpm150m->cmdq[x].desc & (__VPM150M_TX)) && + !(wc->vpm150m->cmdq[x].desc & (__VPM150M_FIN))) { + ident = wc->vpm150m->cmdq[x].txident; + if (ident == wc->rxident) { + /* Store result */ + for (i = 0; i < wc->vpm150m->cmdq[x].datalen; i++) { + wc->vpm150m->cmdq[x].data[i] = (0xff & readchunk[CMD_BYTE((25 + i), 1, 0)]) << 8; + wc->vpm150m->cmdq[x].data[i] |= readchunk[CMD_BYTE((25 + i), 2, 0)]; + } + if (wc->vpm150m->cmdq[x].desc & __VPM150M_WR) { + /* Go ahead and clear out writes since they need no acknowledgement */ + wc->vpm150m->cmdq[x].desc = 0; + } else + wc->vpm150m->cmdq[x].desc |= __VPM150M_FIN; + break; + } + } + } + spin_unlock_irqrestore(&wc->reglock, flags); +} +#endif /* VPM_SUPPORT */ + +static inline void cmd_decifer(struct wctdm *wc, volatile unsigned char *readchunk, int card) +{ + unsigned long flags; + unsigned char ident; + int x; + + /* if a QRV card, map it to its first channel */ + if ((wc->modtype[card] == MOD_TYPE_QRV) && (card & 3)) + { + return; + } + /* Skip audio */ + readchunk += 24; + spin_lock_irqsave(&wc->reglock, flags); + /* Search for any pending results */ + for (x=0;xcmdq[card].cmds[x] & (__CMD_RD | __CMD_WR)) && + (wc->cmdq[card].cmds[x] & (__CMD_TX)) && + !(wc->cmdq[card].cmds[x] & (__CMD_FIN))) { + ident = (wc->cmdq[card].cmds[x] >> 24) & 0xff; + if (ident == wc->rxident) { + /* Store result */ + wc->cmdq[card].cmds[x] |= readchunk[CMD_BYTE(card, 2, wc->altcs[card])]; + wc->cmdq[card].cmds[x] |= __CMD_FIN; + if (wc->cmdq[card].cmds[x] & __CMD_WR) { + /* Go ahead and clear out writes since they need no acknowledgement */ + wc->cmdq[card].cmds[x] = 0x00000000; + } else if (x >= USER_COMMANDS) { + /* Clear out ISR reads */ + wc->cmdq[card].isrshadow[x - USER_COMMANDS] = wc->cmdq[card].cmds[x] & 0xff; + wc->cmdq[card].cmds[x] = 0x00000000; + } + break; + } + } + } +#if 0 + /* XXX */ + if (!pos && (cmddesc < 256)) + printk("Card %d: Command '%08x' => %02x\n",card, wc->cmdq[card].lasttx[pos], wc->cmdq[card].lastrd[pos]); +#endif + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static inline void cmd_checkisr(struct wctdm *wc, int card) +{ + if (!wc->cmdq[card].cmds[USER_COMMANDS + 0]) { + if (wc->sethook[card]) { + wc->cmdq[card].cmds[USER_COMMANDS + 0] = wc->sethook[card]; + wc->sethook[card] = 0; + } else if (wc->modtype[card] == MOD_TYPE_FXS) { + wc->cmdq[card].cmds[USER_COMMANDS + 0] = CMD_RD(68); /* Hook state */ + } else if (wc->modtype[card] == MOD_TYPE_FXO) { + wc->cmdq[card].cmds[USER_COMMANDS + 0] = CMD_RD(5); /* Hook/Ring state */ + } else if (wc->modtype[card] == MOD_TYPE_QRV) { + wc->cmdq[card & 0xfc].cmds[USER_COMMANDS + 0] = CMD_RD(3); /* COR/CTCSS state */ +#ifdef VPM_SUPPORT + } else if (wc->modtype[card] == MOD_TYPE_VPM) { + wc->cmdq[card].cmds[USER_COMMANDS + 0] = CMD_RD(0xb9); /* DTMF interrupt */ +#endif + } + } + if (!wc->cmdq[card].cmds[USER_COMMANDS + 1]) { + if (wc->modtype[card] == MOD_TYPE_FXS) { +#ifdef PAQ_DEBUG + wc->cmdq[card].cmds[USER_COMMANDS + 1] = CMD_RD(19); /* Transistor interrupts */ +#else + wc->cmdq[card].cmds[USER_COMMANDS + 1] = CMD_RD(64); /* Battery mode */ +#endif + } else if (wc->modtype[card] == MOD_TYPE_FXO) { + wc->cmdq[card].cmds[USER_COMMANDS + 1] = CMD_RD(29); /* Battery */ + } else if (wc->modtype[card] == MOD_TYPE_QRV) { + wc->cmdq[card & 0xfc].cmds[USER_COMMANDS + 1] = CMD_RD(3); /* Battery */ +#ifdef VPM_SUPPORT + } else if (wc->modtype[card] == MOD_TYPE_VPM) { + wc->cmdq[card].cmds[USER_COMMANDS + 1] = CMD_RD(0xbd); /* DTMF interrupt */ +#endif + } + } +} + +static inline void wctdm_transmitprep(struct wctdm *wc, int dbl) +{ + volatile unsigned char *writechunk; + int x,y; + + dbl = dbl % 2; + + writechunk = (volatile unsigned char *)(wc->writechunk); + if (dbl) + /* Write is at interrupt address. Start writing from normal offset */ + writechunk += SFRAME_SIZE; + + /* Calculate Transmission */ + zt_transmit(&wc->span); + + for (x=0;xcards;y++) { + if (!x) + cmd_checkisr(wc, y); + + if (y < wc->type) + writechunk[y] = wc->chans[y].writechunk[x]; + cmd_dequeue(wc, writechunk, y, x); + } +#ifdef VPM_SUPPORT + if (!x) + wc->blinktimer++; + if (wc->vpm) { + for (y=24;y<28;y++) { + if (!x) + cmd_checkisr(wc, y); + cmd_dequeue(wc, writechunk, y, x); + } +#ifdef FANCY_ECHOCAN + if (wc->vpm && wc->blinktimer >= 0xf) { + wc->blinktimer = -1; + wc->echocanpos++; + } +#endif + } else if (wc->vpm150m) { + cmd_dequeue_vpm150m(wc, writechunk, x); + } +#endif + if (x < ZT_CHUNKSIZE - 1) { + writechunk[EFRAME_SIZE] = wc->ctlreg; + writechunk[EFRAME_SIZE + 1] = wc->txident++; + } + writechunk += (EFRAME_SIZE + EFRAME_GAP); + } +} + +static inline void __wctdm_setctl(struct wctdm *wc, unsigned int addr, unsigned int val) +{ + outl(val, wc->iobase + addr); +} + +static inline unsigned int __wctdm_getctl(struct wctdm *wc, unsigned int addr) +{ + return inl(wc->iobase + addr); +} + +static inline void wctdm_setctl(struct wctdm *wc, unsigned int addr, unsigned int val) +{ + unsigned long flags; + spin_lock_irqsave(&wc->reglock, flags); + __wctdm_setctl(wc, addr, val); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static inline int wctdm_setreg_full(struct wctdm *wc, int card, int addr, int val, int inisr) +{ + unsigned long flags; + int hit=0; + int ret; + + /* if a QRV card, use only its first channel */ + if (wc->modtype[card] == MOD_TYPE_QRV) + { + if (card & 3) return(0); + } + do { + spin_lock_irqsave(&wc->reglock, flags); + hit = empty_slot(wc, card); + if (hit > -1) { + wc->cmdq[card].cmds[hit] = CMD_WR(addr, val); + } + spin_unlock_irqrestore(&wc->reglock, flags); + if (inisr) + break; + if (hit < 0) { + if ((ret = schluffen(&wc->regq))) + return ret; + } + } while (hit < 0); + return (hit > -1) ? 0 : -1; +} + +static inline int wctdm_setreg_intr(struct wctdm *wc, int card, int addr, int val) +{ + return wctdm_setreg_full(wc, card, addr, val, 1); +} +static inline int wctdm_setreg(struct wctdm *wc, int card, int addr, int val) +{ + return wctdm_setreg_full(wc, card, addr, val, 0); +} + +static inline int wctdm_getreg(struct wctdm *wc, int card, int addr) +{ + unsigned long flags; + int hit; + int ret=0; + + /* if a QRV card, use only its first channel */ + if (wc->modtype[card] == MOD_TYPE_QRV) + { + if (card & 3) return(0); + } + do { + spin_lock_irqsave(&wc->reglock, flags); + hit = empty_slot(wc, card); + if (hit > -1) { + wc->cmdq[card].cmds[hit] = CMD_RD(addr); + } + spin_unlock_irqrestore(&wc->reglock, flags); + if (hit < 0) { + if ((ret = schluffen(&wc->regq))) + return ret; + } + } while (hit < 0); + do { + spin_lock_irqsave(&wc->reglock, flags); + if (wc->cmdq[card].cmds[hit] & __CMD_FIN) { + ret = wc->cmdq[card].cmds[hit] & 0xff; + wc->cmdq[card].cmds[hit] = 0x00000000; + hit = -1; + } + spin_unlock_irqrestore(&wc->reglock, flags); + if (hit > -1) { + if ((ret = schluffen(&wc->regq))) + return ret; + } + } while (hit > -1); + return ret; +} + +static inline unsigned int wctdm_getctl(struct wctdm *wc, unsigned int addr) +{ + unsigned long flags; + unsigned int val; + spin_lock_irqsave(&wc->reglock, flags); + val = __wctdm_getctl(wc, addr); + spin_unlock_irqrestore(&wc->reglock, flags); + return val; +} + +static inline int __wctdm_sdi_clk(struct wctdm *wc) +{ + unsigned int ret; + wc->sdi &= ~SDI_CLK; + __wctdm_setctl(wc, 0x0048, wc->sdi); + ret = __wctdm_getctl(wc, 0x0048); + wc->sdi |= SDI_CLK; + __wctdm_setctl(wc, 0x0048, wc->sdi); + return ret & SDI_DIN; +} + +static inline void __wctdm_sdi_sendbits(struct wctdm *wc, unsigned int bits, int count) +{ + wc->sdi &= ~SDI_DREAD; + __wctdm_setctl(wc, 0x0048, wc->sdi); + while(count--) { + if (bits & (1 << count)) + wc->sdi |= SDI_DOUT; + else + wc->sdi &= ~SDI_DOUT; + __wctdm_sdi_clk(wc); + } +} + +static inline unsigned int __wctdm_sdi_recvbits(struct wctdm *wc, int count) +{ + unsigned int bits=0; + wc->sdi |= SDI_DREAD; + __wctdm_setctl(wc, 0x0048, wc->sdi); + while(count--) { + bits <<= 1; + if (__wctdm_sdi_clk(wc)) + bits |= 1; + else + bits &= ~1; + } + return bits; +} + +static inline void __wctdm_setsdi(struct wctdm *wc, unsigned char addr, unsigned short value) +{ + unsigned int bits; + /* Send preamble */ + bits = 0xffffffff; + __wctdm_sdi_sendbits(wc, bits, 32); + bits = (0x5 << 12) | (1 << 7) | (addr << 2) | 0x2; + __wctdm_sdi_sendbits(wc, bits, 16); + __wctdm_sdi_sendbits(wc, value, 16); + +} + +static inline unsigned short __wctdm_getsdi(struct wctdm *wc, unsigned char addr) +{ + unsigned int bits; + /* Send preamble */ + bits = 0xffffffff; + __wctdm_sdi_sendbits(wc, bits, 32); + bits = (0x6 << 10) | (1 << 5) | (addr); + __wctdm_sdi_sendbits(wc, bits, 14); + return __wctdm_sdi_recvbits(wc, 18); +} + +static inline void wctdm_setsdi(struct wctdm *wc, unsigned char addr, unsigned short value) +{ + unsigned long flags; + spin_lock_irqsave(&wc->reglock, flags); + __wctdm_setsdi(wc, addr, value); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static inline unsigned short wctdm_getsdi(struct wctdm *wc, unsigned char addr) +{ + unsigned long flags; + unsigned short val; + spin_lock_irqsave(&wc->reglock, flags); + val = __wctdm_getsdi(wc, addr); + spin_unlock_irqrestore(&wc->reglock, flags); + return val; +} + +#ifdef VPM_SUPPORT +static inline unsigned char wctdm_vpm_in(struct wctdm *wc, int unit, const unsigned int addr) +{ + return wctdm_getreg(wc, unit + NUM_CARDS, addr); +} + +static inline void wctdm_vpm_out(struct wctdm *wc, int unit, const unsigned int addr, const unsigned char val) +{ + wctdm_setreg(wc, unit + NUM_CARDS, addr, val); +} + +static inline void cmd_vpm150m_retransmit(struct wctdm *wc) +{ + unsigned long flags; + int x; + + spin_lock_irqsave(&wc->reglock, flags); + for (x = 0; x < VPM150M_MAX_COMMANDS; x++) { + if (!(wc->vpm150m->cmdq[x].desc & __VPM150M_FIN)) { + //printk("Retransmit!\n"); + wc->vpm150m->cmdq[x].desc &= ~(__VPM150M_TX); + } + } + spin_unlock_irqrestore(&wc->reglock, flags); + +} +#endif + +static inline void cmd_retransmit(struct wctdm *wc) +{ + int x,y; + unsigned long flags; + /* Force retransmissions */ + spin_lock_irqsave(&wc->reglock, flags); + for (x=0;xcards;y++) { + if (!(wc->cmdq[y].cmds[x] & __CMD_FIN)) + wc->cmdq[y].cmds[x] &= ~(__CMD_TX | (0xff << 24)); + } + } + spin_unlock_irqrestore(&wc->reglock, flags); +#ifdef VPM_SUPPORT + if (wc->vpm150m) + cmd_vpm150m_retransmit(wc); +#endif +} + +static inline void wctdm_receiveprep(struct wctdm *wc, int dbl) +{ + volatile unsigned char *readchunk; + int x,y; + unsigned char expected; + + dbl = dbl % 2; + + readchunk = (volatile unsigned char *)wc->readchunk; + if (dbl) + readchunk += SFRAME_SIZE; + for (x=0;xrxident+1; + wc->rxident = readchunk[EFRAME_SIZE + 1]; + if (wc->rxident != expected) { + wc->span.irqmisses++; + cmd_retransmit(wc); + } + } + for (y=0;y < wc->cards;y++) { + if (y < wc->type) { + wc->chans[y].readchunk[x] = readchunk[y]; + } + cmd_decifer(wc, readchunk, y); + } +#ifdef VPM_SUPPORT + if (wc->vpm) { + for (y=NUM_CARDS;y < NUM_CARDS + NUM_EC; y++) + cmd_decifer(wc, readchunk, y); + } else if (wc->vpm150m) + cmd_decifer_vpm150m(wc, readchunk); +#endif +#if 0 + if (cmddesc < 1024) { + printk("RC Result: %02x\n", readchunk[EFRAME_SIZE+1]); + } +#endif + readchunk += (EFRAME_SIZE + EFRAME_GAP); + } + /* XXX We're wasting 8 taps. We should get closer :( */ + for (x=0;xtype;x++) { + if (wc->cardflag & (1 << x)) + zt_ec_chunk(&wc->chans[x], wc->chans[x].readchunk, wc->chans[x].writechunk); + } + zt_receive(&wc->span); + /* Wake up anyone sleeping to read/write a new register */ + wake_up_interruptible(&wc->regq); +} + +static void wctdm_stop_dma(struct wctdm *wc); +static void wctdm_restart_dma(struct wctdm *wc); + +static int wait_access(struct wctdm *wc, int card) +{ + unsigned char data=0; + long origjiffies; + int count = 0; + + #define MAX 10 /* 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(" ##### Loop error (%02x) #####\n", data); + + return 0; +} + +static unsigned char translate_3215(unsigned char address) +{ + int x; + for (x=0;xflags[card] & FLAG_3215) { + address = translate_3215(address); + if (address == 255) + return 0; + } + 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; + }; + return res; +} + +static int wctdm_proslic_getreg_indirect(struct wctdm *wc, int card, unsigned char address) +{ + int res = -1; + char *p=NULL; + /* Translate 3215 addresses */ + if (wc->flags[card] & FLAG_3215) { + address = translate_3215(address); + if (address == 255) + return 0; + } + 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"; + if (p) + printk(p); + return res; +} + +static int wctdm_proslic_init_indirect_regs(struct wctdm *wc, int card) +{ + unsigned char i; + + for (i=0; iflags[card] & FLAG_3215) || (indirect_regs[i].altaddr != 255))) + { + printk("!!!!!!! %s iREG %X = %X should be %X\n", + indirect_regs[i].name,indirect_regs[i].address,j,initial ); + passed = 0; + } + } + + if (passed) { + if (debug & DEBUG_CARD) + printk("Init Indirect Registers completed successfully.\n"); + } else { + printk(" !!!!! Init Indirect Registers UNSUCCESSFULLY.\n"); + return -1; + } + return 0; +} + +static inline void wctdm_proslic_recheck_sanity(struct wctdm *wc, int card) +{ + int res; +#ifdef PAQ_DEBUG + res = wc->cmdq[card].isrshadow[1]; + res &= ~0x3; + if (res) { + wc->cmdq[card].isrshadow[1]=0; + wc->mods[card].fxs.palarms++; + if (wc->mods[card].fxs.palarms < MAX_ALARMS) { + printk("Power alarm (%02x) on module %d, resetting!\n", res, card + 1); + if (wc->mods[card].fxs.lasttxhook == 4) + wc->mods[card].fxs.lasttxhook = 1; + wc->sethook[card] = CMD_WR(19, res); +#if 0 + wc->sethook[card] = CMD_WR(64, wc->mods[card].fxs.lasttxhook); +#endif + + /* wctdm_setreg_intr(wc, card, 64, wc->mods[card].fxs.lasttxhook); */ + /* Update shadow register to avoid extra power alarms until next read */ + wc->cmdq[card].isrshadow[1] = 0; + } else { + if (wc->mods[card].fxs.palarms == MAX_ALARMS) + printk("Too many power alarms on card %d, NOT resetting!\n", card + 1); + } + } +#else + res = wc->cmdq[card].isrshadow[1]; + if (!res && (res != wc->mods[card].fxs.lasttxhook)) { + wc->mods[card].fxs.palarms++; + if (wc->mods[card].fxs.palarms < MAX_ALARMS) { + printk("Power alarm on module %d, resetting!\n", card + 1); + if (wc->mods[card].fxs.lasttxhook == 4) + wc->mods[card].fxs.lasttxhook = 1; + wc->sethook[card] = CMD_WR(64, wc->mods[card].fxs.lasttxhook); + + /* wctdm_setreg_intr(wc, card, 64, wc->mods[card].fxs.lasttxhook); */ + /* Update shadow register to avoid extra power alarms until next read */ + wc->cmdq[card].isrshadow[1] = wc->mods[card].fxs.lasttxhook; + } else { + if (wc->mods[card].fxs.palarms == MAX_ALARMS) + printk("Too many power alarms on card %d, NOT resetting!\n", card + 1); + } + } +#endif +} + +static inline void wctdm_qrvdri_check_hook(struct wctdm *wc, int card) +{ + signed char b,b1; + int qrvcard = card & 0xfc; + + + if (wc->qrvdebtime[card] >= 2) wc->qrvdebtime[card]--; + b = wc->cmdq[qrvcard].isrshadow[0]; /* Hook/Ring state */ + b &= 0xcc; /* use bits 3-4 and 6-7 only */ + + if (wc->radmode[qrvcard] & RADMODE_IGNORECOR) b &= ~4; + else if (!(wc->radmode[qrvcard] & RADMODE_INVERTCOR)) b ^= 4; + if (wc->radmode[qrvcard + 1] | RADMODE_IGNORECOR) b &= ~0x40; + else if (!(wc->radmode[qrvcard + 1] | RADMODE_INVERTCOR)) b ^= 0x40; + + if ((wc->radmode[qrvcard] & RADMODE_IGNORECT) || + (!(wc->radmode[qrvcard] & RADMODE_EXTTONE))) b &= ~8; + else if (!(wc->radmode[qrvcard] & RADMODE_EXTINVERT)) b ^= 8; + if ((wc->radmode[qrvcard + 1] & RADMODE_IGNORECT) || + (!(wc->radmode[qrvcard + 1] & RADMODE_EXTTONE))) b &= ~0x80; + else if (!(wc->radmode[qrvcard + 1] & RADMODE_EXTINVERT)) b ^= 0x80; + /* now b & MASK should be zero, if its active */ + /* check for change in chan 0 */ + if ((!(b & 0xc)) != wc->qrvhook[qrvcard + 2]) + { + wc->qrvdebtime[qrvcard] = wc->debouncetime[qrvcard]; + wc->qrvhook[qrvcard + 2] = !(b & 0xc); + } + /* if timed-out and ready */ + if (wc->qrvdebtime[qrvcard] == 1) + { + b1 = wc->qrvhook[qrvcard + 2]; +if (debug) printk("QRV channel %d rx state changed to %d\n",qrvcard,wc->qrvhook[qrvcard + 2]); + zt_hooksig(&wc->chans[qrvcard], + (b1) ? ZT_RXSIG_OFFHOOK : ZT_RXSIG_ONHOOK); + wc->qrvdebtime[card] = 0; + } + /* check for change in chan 1 */ + if ((!(b & 0xc0)) != wc->qrvhook[qrvcard + 3]) + { + wc->qrvdebtime[qrvcard + 1] = QRV_DEBOUNCETIME; + wc->qrvhook[qrvcard + 3] = !(b & 0xc0); + } + if (wc->qrvdebtime[qrvcard + 1] == 1) + { + b1 = wc->qrvhook[qrvcard + 3]; +if (debug) printk("QRV channel %d rx state changed to %d\n",qrvcard + 1,wc->qrvhook[qrvcard + 3]); + zt_hooksig(&wc->chans[qrvcard + 1], + (b1) ? ZT_RXSIG_OFFHOOK : ZT_RXSIG_ONHOOK); + wc->qrvdebtime[card] = 0; + } + return; +} + +static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) +{ + unsigned char res; + signed char b; + /* Try to track issues that plague slot one FXO's */ + b = wc->cmdq[card].isrshadow[0]; /* Hook/Ring state */ + b &= 0x9b; + if (wc->mods[card].fxo.offhook) { + if (b != 0x9) + wctdm_setreg_intr(wc, card, 5, 0x9); + } else { + if (b != 0x8) + wctdm_setreg_intr(wc, card, 5, 0x8); + } + if (!wc->mods[card].fxo.offhook) { + res = wc->cmdq[card].isrshadow[0]; /* Hook/Ring state */ + if ((res & 0x60) && wc->mods[card].fxo.battery) { + wc->mods[card].fxo.ringdebounce += (ZT_CHUNKSIZE * 4); + if (wc->mods[card].fxo.ringdebounce >= ZT_CHUNKSIZE * RING_DEBOUNCE) { + if (!wc->mods[card].fxo.wasringing) { + wc->mods[card].fxo.wasringing = 1; + zt_hooksig(&wc->chans[card], ZT_RXSIG_RING); + if (debug & DEBUG_CARD) + printk("RING on %d/%d!\n", wc->span.spanno, card + 1); + } + wc->mods[card].fxo.ringdebounce = ZT_CHUNKSIZE * RING_DEBOUNCE; + } + } else { + wc->mods[card].fxo.ringdebounce -= ZT_CHUNKSIZE; + if (wc->mods[card].fxo.ringdebounce <= 0) { + if (wc->mods[card].fxo.wasringing) { + wc->mods[card].fxo.wasringing = 0; + zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK); + if (debug & DEBUG_CARD) + printk("NO RING on %d/%d!\n", wc->span.spanno, card + 1); + } + wc->mods[card].fxo.ringdebounce = 0; + } + + } + } + b = wc->cmdq[card].isrshadow[1]; /* Voltage */ + if (abs(b) < battthresh) { + wc->mods[card].fxo.nobatttimer++; +#if 0 + if (wc->mods[card].fxo.battery) + printk("Battery loss: %d (%d debounce)\n", b, wc->mods[card].fxo.battdebounce); +#endif + if (wc->mods[card].fxo.battery && !wc->mods[card].fxo.battdebounce) { + if (debug & DEBUG_CARD) + printk("NO BATTERY on %d/%d!\n", wc->span.spanno, card + 1); + wc->mods[card].fxo.battery = 0; +#ifdef JAPAN + if ((!wc->ohdebounce) && wc->offhook) { + zt_hooksig(&wc->chans[card], ZT_RXSIG_ONHOOK); + if (debug & DEBUG_CARD) + printk("Signalled On Hook\n"); +#ifdef ZERO_BATT_RING + wc->onhook++; +#endif + } +#else + zt_hooksig(&wc->chans[card], ZT_RXSIG_ONHOOK); +#endif + wc->mods[card].fxo.battdebounce = battdebounce; + } else if (!wc->mods[card].fxo.battery) + wc->mods[card].fxo.battdebounce = battdebounce; + } else if (abs(b) > battthresh) { + if (!wc->mods[card].fxo.battery && !wc->mods[card].fxo.battdebounce) { + if (debug & DEBUG_CARD) + printk("BATTERY on %d/%d (%s)!\n", wc->span.spanno, card + 1, + (b < 0) ? "-" : "+"); +#ifdef ZERO_BATT_RING + if (wc->onhook) { + wc->onhook = 0; + zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK); + if (debug & DEBUG_CARD) + printk("Signalled Off Hook\n"); + } +#else + zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK); +#endif + wc->mods[card].fxo.battery = 1; + wc->mods[card].fxo.nobatttimer = 0; + wc->mods[card].fxo.battdebounce = battdebounce; + } else if (wc->mods[card].fxo.battery) + wc->mods[card].fxo.battdebounce = battdebounce; + + if (wc->mods[card].fxo.lastpol >= 0) { + if (b < 0) { + wc->mods[card].fxo.lastpol = -1; + wc->mods[card].fxo.polaritydebounce = POLARITY_DEBOUNCE; + } + } + if (wc->mods[card].fxo.lastpol <= 0) { + if (b > 0) { + wc->mods[card].fxo.lastpol = 1; + wc->mods[card].fxo.polaritydebounce = POLARITY_DEBOUNCE; + } + } + } else { + /* It's something else... */ + wc->mods[card].fxo.battdebounce = battdebounce; + } + if (wc->mods[card].fxo.battdebounce) + wc->mods[card].fxo.battdebounce--; + if (wc->mods[card].fxo.polaritydebounce) { + wc->mods[card].fxo.polaritydebounce--; + if (wc->mods[card].fxo.polaritydebounce < 1) { + if (wc->mods[card].fxo.lastpol != wc->mods[card].fxo.polarity) { + if (debug & DEBUG_CARD) + printk("%lu Polarity reversed (%d -> %d)\n", jiffies, + wc->mods[card].fxo.polarity, + wc->mods[card].fxo.lastpol); + if (wc->mods[card].fxo.polarity) + zt_qevent_lock(&wc->chans[card], ZT_EVENT_POLARITY); + wc->mods[card].fxo.polarity = wc->mods[card].fxo.lastpol; + } + } + } +} + +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->cmdq[card].isrshadow[0]; /* Hook state */ + hook = (res & 1); + + if (hook != wc->mods[card].fxs.lastrxhook) { + /* Reset the debounce (must be multiple of 4ms) */ + wc->mods[card].fxs.debounce = 8 * (4 * 8); +#if 0 + printk("Resetting debounce card %d hook %d, %d\n", card, hook, wc->mods[card].fxs.debounce); +#endif + } else { + if (wc->mods[card].fxs.debounce > 0) { + wc->mods[card].fxs.debounce-= 4 * ZT_CHUNKSIZE; +#if 0 + printk("Sustaining hook %d, %d\n", hook, wc->mods[card].fxs.debounce); +#endif + if (!wc->mods[card].fxs.debounce) { +#if 0 + printk("Counted down debounce, newhook: %d...\n", hook); +#endif + wc->mods[card].fxs.debouncehook = hook; + } + if (!wc->mods[card].fxs.oldrxhook && wc->mods[card].fxs.debouncehook) { + /* Off hook */ + if (debug & DEBUG_CARD) + printk("wctdm: Card %d Going off hook\n", card); + zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK); + if (robust) + wctdm_init_proslic(wc, card, 1, 0, 1); + wc->mods[card].fxs.oldrxhook = 1; + + } else if (wc->mods[card].fxs.oldrxhook && !wc->mods[card].fxs.debouncehook) { + /* On hook */ + if (debug & DEBUG_CARD) + printk("wctdm: Card %d Going on hook\n", card); + zt_hooksig(&wc->chans[card], ZT_RXSIG_ONHOOK); + wc->mods[card].fxs.oldrxhook = 0; + } + } + } + wc->mods[card].fxs.lastrxhook = hook; +} + + +static inline void wctdm_reinit_descriptor(struct wctdm *wc, int tx, int dbl, char *s) +{ + int o2 = 0; + o2 += dbl * 4; + if (!tx) + o2 += ERING_SIZE * 4; + wc->descripchunk[o2] = 0x80000000; +} + +#ifdef VPM_SUPPORT +static inline void wctdm_vpm_check(struct wctdm *wc, int x) +{ + if (wc->cmdq[x].isrshadow[0]) { + if (debug & DEBUG_ECHOCAN) + printk("VPM: Detected dtmf ON channel %02x on chip %d!\n", wc->cmdq[x].isrshadow[0], x - NUM_CARDS); + wc->sethook[x] = CMD_WR(0xb9, wc->cmdq[x].isrshadow[0]); + wc->cmdq[x].isrshadow[0] = 0; + /* Cancel most recent lookup, if there is one */ + wc->cmdq[x].cmds[USER_COMMANDS+0] = 0x00000000; + } else if (wc->cmdq[x].isrshadow[1]) { + if (debug & DEBUG_ECHOCAN) + printk("VPM: Detected dtmf OFF channel %02x on chip %d!\n", wc->cmdq[x].isrshadow[1], x - NUM_CARDS); + wc->sethook[x] = CMD_WR(0xbd, wc->cmdq[x].isrshadow[1]); + wc->cmdq[x].isrshadow[1] = 0; + /* Cancel most recent lookup, if there is one */ + wc->cmdq[x].cmds[USER_COMMANDS+1] = 0x00000000; + } +} + +static int wctdm_echocan(struct zt_chan *chan, int eclen) +{ + struct wctdm *wc = chan->pvt; + if (wc->vpm) { + int channel; + int unit; + + channel = (chan->chanpos - 1); + unit = (chan->chanpos - 1) & 0x3; + if (wc->vpm < 2) + channel >>= 2; + + if(debug & DEBUG_ECHOCAN) + printk("echocan: Unit is %d, Channel is %d length %d\n", + unit, channel, eclen); + if (eclen) + wctdm_vpm_out(wc,unit,channel,0x3e); + else + wctdm_vpm_out(wc,unit,channel,0x01); + + return 0; + } else if (wc->vpm150m) { + struct vpm150m *vpm150m = wc->vpm150m; + + if (eclen) { + set_bit(chan->chanpos - 1, &vpm150m->desiredecstate); + if (test_bit(VPM150M_ACTIVE, &vpm150m->control)) + queue_work(vpm150m->wq, &vpm150m->work); + } else { + clear_bit(chan->chanpos - 1, &vpm150m->desiredecstate); + if (test_bit(VPM150M_ACTIVE, &vpm150m->control)) + queue_work(vpm150m->wq, &vpm150m->work); + } + return 0; + } else + return -ENODEV; +} +#endif + +static inline void wctdm_isr_misc(struct wctdm *wc) +{ + int x; + + for (x=0;xcards;x++) { + if (wc->cardflag & (1 << x)) { + if (wc->modtype[x] == MOD_TYPE_FXS) { + if (!(wc->intcount % 10000)) { + /* Accept an alarm once per 10 seconds */ + if (wc->mods[x].fxs.palarms) + wc->mods[x].fxs.palarms--; + } + wctdm_proslic_check_hook(wc, x); + if (!(wc->intcount & 0xfc)) + wctdm_proslic_recheck_sanity(wc, x); + if (wc->mods[x].fxs.lasttxhook == 0x4) { + /* RINGing, prepare for OHT */ + wc->mods[x].fxs.ohttimer = OHT_TIMER << 3; + wc->mods[x].fxs.idletxhookstate = 0x2; /* OHT mode when idle */ + } else { + if (wc->mods[x].fxs.ohttimer) { + wc->mods[x].fxs.ohttimer-= ZT_CHUNKSIZE; + if (!wc->mods[x].fxs.ohttimer) { + wc->mods[x].fxs.idletxhookstate = 0x1; /* Switch to active */ + if (wc->mods[x].fxs.lasttxhook == 0x2) { + /* Apply the change if appropriate */ + wc->mods[x].fxs.lasttxhook = 0x1; + wc->sethook[x] = CMD_WR(64, wc->mods[x].fxs.lasttxhook); + /* wctdm_setreg_intr(wc, x, 64, wc->mods[x].fxs.lasttxhook); */ + } + } + } + } + } else if (wc->modtype[x] == MOD_TYPE_FXO) { + wctdm_voicedaa_check_hook(wc, x); + } else if (wc->modtype[x] == MOD_TYPE_QRV) { + wctdm_qrvdri_check_hook(wc, x); + } + } + } +#ifdef VPM_SUPPORT + if (wc->vpm > 0) { + for (x=NUM_CARDS;xrdbl * 4; + } else { + o2 += wc->tdbl * 4; + } + if (!(wc->descripchunk[o2] & 0x80000000)) { + if (tx) { + wc->txints++; + wctdm_transmitprep(wc, wc->tdbl); + wctdm_reinit_descriptor(wc, tx, wc->tdbl, "txchk"); + wc->tdbl = (wc->tdbl + 1) % ERING_SIZE; + wctdm_isr_misc(wc); + wc->intcount++; + } else { + wc->rxints++; + wctdm_receiveprep(wc, wc->rdbl); + wctdm_reinit_descriptor(wc, tx, wc->rdbl, "rxchk"); + wc->rdbl = (wc->rdbl + 1) % ERING_SIZE; + } + return 1; + } + return 0; +} + +static void wctdm_init_descriptors(struct wctdm *wc) +{ + volatile unsigned int *descrip; + dma_addr_t descripdma; + dma_addr_t writedma; + dma_addr_t readdma; + int x; + + descrip = wc->descripchunk; + descripdma = wc->descripdma; + writedma = wc->writedma; + readdma = wc->readdma; + + for (x=0;xdescripdma; + + /* Transmit descriptor */ + descrip[0 ] = 0x80000000; + descrip[1 ] = 0xe5800000 | (SFRAME_SIZE); + if (x % 2) + descrip[2 ] = writedma + SFRAME_SIZE; + else + descrip[2 ] = writedma; + descrip[3 ] = descripdma; + + /* Receive descriptor */ + descrip[0 + ERING_SIZE * 4] = 0x80000000; + descrip[1 + ERING_SIZE * 4] = 0x01000000 | (SFRAME_SIZE); + if (x % 2) + descrip[2 + ERING_SIZE * 4] = readdma + SFRAME_SIZE; + else + descrip[2 + ERING_SIZE * 4] = readdma; + descrip[3 + ERING_SIZE * 4] = descripdma + ERING_SIZE * 16; + + /* Advance descriptor */ + descrip += 4; + } +} + +ZAP_IRQ_HANDLER(wctdm_interrupt) +{ + struct wctdm *wc = dev_id; + unsigned int ints; + int res; + + /* Read and clear interrupts */ + ints = wctdm_getctl(wc, 0x0028); + + if (!ints) +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + + wctdm_setctl(wc, 0x0028, ints); + + ints &= wc->intmask; + if (ints & 0x00000041) { + do { + res = wctdm_check_descriptor(wc, 0); + res |= wctdm_check_descriptor(wc, 1); + } while(res); +#if 0 + while(wctdm_check_descriptor(wc, 0)); + wctdm_setctl(wc, 0x0010, 0x00000000); + } + if (ints & 0x00000005) { + while(wctdm_check_descriptor(wc, 1)); + wctdm_setctl(wc, 0x0008, 0x00000000); +#endif + } +#ifdef LINUX26 + return IRQ_RETVAL(1); +#endif + +} + +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 & DEBUG_CARD) + printk("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 & DEBUG_CARD) + printk("ProSLIC on module %d, product %d, version %d\n", card, (blah & 0x30) >> 4, (blah & 0xf)); + +#if 0 + if ((blah & 0x30) >> 4) { + printk("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("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("ProSLIC on module %d insane (1) %d should be 2\n", card, blah); + return -1; + } else if ( insane_report) + printk("ProSLIC on module %d Reg 8 Reads %d Expected is 0x2\n",card,blah); + + blah = wctdm_getreg(wc, card, 64); + if (blah != 0x0) { + printk("ProSLIC on module %d insane (2)\n", card); + return -1; + } else if ( insane_report) + printk("ProSLIC on module %d Reg 64 Reads %d Expected is 0x0\n",card,blah); + + blah = wctdm_getreg(wc, card, 11); + if (blah != 0x33) { + printk("ProSLIC on module %d insane (3)\n", card); + return -1; + } else if ( insane_report) + printk("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 & DEBUG_CARD) + printk("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("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 & DEBUG_CARD) { + printk("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, 0xc0 /* 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) { + printk("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 TDM CARD??\n", + card, (int)(((jiffies - origjiffies) * 1000 / HZ)), + vbat * 375); + return -1; + } else if (debug & DEBUG_CARD) { + printk("ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n", + card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ))); + } + + /* 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 & DEBUG_CARD) + printk("Loop current out of range! Setting to default 20mA!\n"); + } + else if (debug & DEBUG_CARD) + printk("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("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("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); + +} + +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("Timeout waiting for calibration of module %d\n", card); + return -1; + } + } + + if (debug & DEBUG_CARD) { + /* Print calibration parameters */ + printk("Calibration Vector Regs 98 - 107: \n"); + for (x=98;x<108;x++) { + printk("%d: %02x\n", x, wctdm_getreg(wc, card, x)); + } + } + return 0; +} + +static void wait_just_a_bit(int foo) +{ + long newjiffies; + newjiffies = jiffies + foo; + while(jiffies < newjiffies); +} + +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; + + if (wc->modtype[card & 0xfc] == MOD_TYPE_QRV) return -2; + + wc->modtype[card] = MOD_TYPE_NONE; + /* Wait just a bit */ + wait_just_a_bit(HZ/10); + + wc->modtype[card] = MOD_TYPE_FXO; + wait_just_a_bit(HZ/10); + + 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); + + /* 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 = (fxo_modes[_opermode].acim); + wctdm_setreg(wc, card, 30, reg30); + + /* Misc. DAA parameters */ + reg31 = 0xa3; + reg31 |= (fxo_modes[_opermode].ohs2 << 3); + wctdm_setreg(wc, card, 31, reg31); + + /* Set Transmit/Receive timeslot */ + wctdm_setreg(wc, card, 34, (card * 8) & 0xff); + wctdm_setreg(wc, card, 35, (card * 8) >> 8); + wctdm_setreg(wc, card, 36, (card * 8) & 0xff); + wctdm_setreg(wc, card, 37, (card * 8) >> 8); + + /* Enable ISO-Cap */ + wctdm_setreg(wc, card, 6, 0x00); + + /* 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("VoiceDAA did not bring up ISO link properly!\n"); + return -1; + } + if (debug & DEBUG_CARD) + printk("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); + + /* Apply negative Tx gain of 4.5db to DAA */ + wctdm_setreg(wc, card, 38, 0x14); /* 4db */ + wctdm_setreg(wc, card, 40, 0x15); /* 0.5db */ + + /* Apply negative Rx gain of 4.5db to DAA */ + wctdm_setreg(wc, card, 39, 0x14); /* 4db */ + wctdm_setreg(wc, card, 41, 0x15); /* 0.5db */ + + + /* Take values for fxotxgain and fxorxgain and apply them to module */ + if (fxotxgain) { + if (fxotxgain >= -150 && fxotxgain < 0) { + wctdm_setreg(wc, card, 38, 16 + (fxotxgain/-10)); + if(fxotxgain % 10) { + wctdm_setreg(wc, card, 40, 16 + (-fxotxgain%10)); + } + } + else if (fxotxgain <= 120 && fxotxgain > 0) { + wctdm_setreg(wc, card, 38, fxotxgain/10); + if(fxotxgain % 10) { + wctdm_setreg(wc, card, 40, (fxotxgain%10)); + } + } + } + if (fxorxgain) { + if (fxorxgain >= -150 && fxorxgain < 0) { + wctdm_setreg(wc, card, 39, 16+ (fxorxgain/-10)); + if(fxotxgain % 10) { + wctdm_setreg(wc, card, 41, 16 + (-fxorxgain%10)); + } + } + else if (fxorxgain <= 120 && fxorxgain > 0) { + wctdm_setreg(wc, card, 39, fxorxgain/10); + if(fxorxgain % 10) { + wctdm_setreg(wc, card, 41, (fxorxgain%10)); + } + } + } + + if(debug) + printk("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; + + if (wc->modtype[card & 0xfc] == MOD_TYPE_QRV) return -2; + + /* By default, don't send on hook */ + wc->mods[card].fxs.idletxhookstate = 1; + + /* Sanity check the ProSLIC */ + if (!sane && wctdm_proslic_insane(wc, card)) + return -2; + + 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, 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("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("ProSLIC module %d failed leakage test. Check for short circuit\n", card); + } + /* Power up again */ + if (wctdm_powerup_proslic(wc, card, fast)) { + printk("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("Proslic failed on Manual Calibration\n"); + if (wctdm_proslic_manual_calibrate(wc, card)) { + printk("Proslic Failed on Second Attempt to Calibrate Manually. (Try -DNO_CALIBRATION in Makefile)\n"); + return -1; + } + printk("Proslic Passed Manual Calibration on Second Attempt\n"); + } + } + else { + if(wctdm_proslic_calibrate(wc, card)) { + //printk("ProSlic died on Auto Calibration.\n"); + if (wctdm_proslic_calibrate(wc, card)) { + printk("Proslic Failed on Second Attempt to Auto Calibrate\n"); + return -1; + } + printk("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("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;xmods[card].fxs.calregs.vals[x] = wctdm_getreg(wc, card, 96 + x); +#endif + + } else { + /* Restore calibration registers */ + for (x=0;xmods[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, (card * 8) & 0xff); // Tx Start count low byte 0 + wctdm_setreg(wc, card, 3, (card * 8) >> 8); // Tx Start count high byte 0 + wctdm_setreg(wc, card, 4, (card * 8) & 0xff); // Rx Start count low byte 0 + wctdm_setreg(wc, card, 5, (card * 8) >> 8); // 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, 22, 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("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("Reducing fast ring power on slot %d (50V peak)\n", card + 1); + } else + printk("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("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("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("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); + wc->mods[card].fxs.lasttxhook = 1; + return 0; +} + +static int wctdm_init_qrvdri(struct wctdm *wc, int card) +{ + unsigned char x,y; + unsigned long endjif; + + /* have to set this, at least for now */ + wc->modtype[card] = MOD_TYPE_QRV; + if (!(card & 3)) /* if at base of card, reset and write it */ + { + wctdm_setreg(wc,card,0,0x80); + wctdm_setreg(wc,card,0,0x55); + wctdm_setreg(wc,card,1,0x69); + wc->qrvhook[card] = wc->qrvhook[card + 1] = 0; + wc->qrvhook[card + 2] = wc->qrvhook[card + 3] = 0xff; + wc->debouncetime[card] = wc->debouncetime[card + 1] = QRV_DEBOUNCETIME; + wc->qrvdebtime[card] = wc->qrvdebtime[card + 1] = 0; + wc->radmode[card] = wc->radmode[card + 1] = 0; + wc->txgain[card] = wc->txgain[card + 1] = 3599; + wc->rxgain[card] = wc->rxgain[card + 1] = 1199; + } else { /* channel is on same card as base, no need to test */ + if (wc->modtype[card & 0x7c] == MOD_TYPE_QRV) + { + /* only lower 2 are valid */ + if (!(card & 2)) return 0; + } + wc->modtype[card] = MOD_TYPE_NONE; + return 1; + } + x = wctdm_getreg(wc,card,0); + y = wctdm_getreg(wc,card,1); + /* if not a QRV card, return as such */ + if ((x != 0x55) || (y != 0x69)) + { + wc->modtype[card] = MOD_TYPE_NONE; + return 1; + } + for(x = 0; x < 0x30; x++) + { + if ((x >= 0x1c) && (x <= 0x1e)) wctdm_setreg(wc,card,x,0xff); + else wctdm_setreg(wc,card,x,0); + } + wctdm_setreg(wc,card,0,0x80); + endjif = jiffies + (HZ/10); + while(endjif > jiffies); + wctdm_setreg(wc,card,0,0x10); + wctdm_setreg(wc,card,0,0x10); + endjif = jiffies + (HZ/10); + while(endjif > jiffies); + /* set up modes */ + wctdm_setreg(wc,card,0,0x1c); + /* set up I/O directions */ + wctdm_setreg(wc,card,1,0x33); + wctdm_setreg(wc,card,2,0x0f); + wctdm_setreg(wc,card,5,0x0f); + /* set up I/O to quiescent state */ + wctdm_setreg(wc,card,3,0x11); /* D0-7 */ + wctdm_setreg(wc,card,4,0xa); /* D8-11 */ + wctdm_setreg(wc,card,7,0); /* CS outputs */ + /* set up timeslots */ + wctdm_setreg(wc,card,0x13,card + 0x80); /* codec 2 tx, ts0 */ + wctdm_setreg(wc,card,0x17,card + 0x80); /* codec 0 rx, ts0 */ + wctdm_setreg(wc,card,0x14,card + 0x81); /* codec 1 tx, ts1 */ + wctdm_setreg(wc,card,0x18,card + 0x81); /* codec 1 rx, ts1 */ + /* set up for max gains */ + wctdm_setreg(wc,card,0x26,0x24); + wctdm_setreg(wc,card,0x27,0x24); + wctdm_setreg(wc,card,0x0b,0x01); /* "Transmit" gain codec 0 */ + wctdm_setreg(wc,card,0x0c,0x01); /* "Transmit" gain codec 1 */ + wctdm_setreg(wc,card,0x0f,0xff); /* "Receive" gain codec 0 */ + wctdm_setreg(wc,card,0x10,0xff); /* "Receive" gain codec 1 */ + return 0; +} + +static void qrv_dosetup(struct zt_chan *chan,struct wctdm *wc) +{ +int qrvcard; +unsigned char r; +long l; + + /* actually do something with the values */ + qrvcard = (chan->chanpos - 1) & 0xfc; + if (debug) printk("@@@@@ radmodes: %d,%d rxgains: %d,%d txgains: %d,%d\n", + wc->radmode[qrvcard],wc->radmode[qrvcard + 1], + wc->rxgain[qrvcard],wc->rxgain[qrvcard + 1], + wc->txgain[qrvcard],wc->txgain[qrvcard + 1]); + r = 0; + if (wc->radmode[qrvcard] & RADMODE_DEEMP) r |= 4; + if (wc->radmode[qrvcard + 1] & RADMODE_DEEMP) r |= 8; + if (wc->rxgain[qrvcard] < 1200) r |= 1; + if (wc->rxgain[qrvcard + 1] < 1200) r |= 2; + wctdm_setreg(wc, qrvcard, 7, r); + if (debug) printk("@@@@@ setting reg 7 to %02x hex\n",r); + r = 0; + if (wc->radmode[qrvcard] & RADMODE_PREEMP) r |= 3; + else if (wc->txgain[qrvcard] >= 3600) r |= 1; + else if (wc->txgain[qrvcard] >= 1200) r |= 2; + if (wc->radmode[qrvcard + 1] & RADMODE_PREEMP) r |= 0xc; + else if (wc->txgain[qrvcard + 1] >= 3600) r |= 4; + else if (wc->txgain[qrvcard + 1] >= 1200) r |= 8; + wctdm_setreg(wc, qrvcard, 4, r); + if (debug) printk("@@@@@ setting reg 4 to %02x hex\n",r); + r = 0; + if (wc->rxgain[qrvcard] >= 2400) r |= 1; + if (wc->rxgain[qrvcard + 1] >= 2400) r |= 2; + wctdm_setreg(wc, qrvcard, 0x25, r); + if (debug) printk("@@@@@ setting reg 0x25 to %02x hex\n",r); + r = 0; + if (wc->txgain[qrvcard] < 2400) r |= 1; else r |= 4; + if (wc->txgain[qrvcard + 1] < 2400) r |= 8; else r |= 0x20; + wctdm_setreg(wc, qrvcard, 0x26, r); + if (debug) printk("@@@@@ setting reg 0x26 to %02x hex\n",r); + l = ((long)(wc->rxgain[qrvcard] % 1200) * 10000) / 46875; + if (l == 0) l = 1; + if (wc->rxgain[qrvcard] >= 2400) l += 181; + wctdm_setreg(wc, qrvcard, 0x0b, (unsigned char)l); + if (debug) printk("@@@@@ setting reg 0x0b to %02x hex\n",(unsigned char)l); + l = ((long)(wc->rxgain[qrvcard + 1] % 1200) * 10000) / 46875; + if (l == 0) l = 1; + if (wc->rxgain[qrvcard + 1] >= 2400) l += 181; + wctdm_setreg(wc, qrvcard, 0x0c, (unsigned char)l); + if (debug) printk("@@@@@ setting reg 0x0c to %02x hex\n",(unsigned char)l); + l = ((long)(wc->txgain[qrvcard] % 1200) * 10000) / 46875; + if (l == 0) l = 1; + wctdm_setreg(wc, qrvcard, 0x0f, (unsigned char)l); + if (debug) printk("@@@@@ setting reg 0x0f to %02x hex\n", (unsigned char)l); + l = ((long)(wc->txgain[qrvcard + 1] % 1200) * 10000) / 46875; + if (l == 0) l = 1; + wctdm_setreg(wc, qrvcard, 0x10,(unsigned char)l); + if (debug) printk("@@@@@ setting reg 0x10 to %02x hex\n",(unsigned char)l); + return; +} + +static int wctdm_ioctl(struct zt_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 wctdm *wc = chan->pvt; + int x; + union { + struct zt_radio_stat s; + struct zt_radio_param p; + } stack; + +#if 0 + /* XXX */ + printk("RxInts: %d, TxInts: %d\n", wc->rxints, wc->txints); + printk("RxIdent: %d, TxIdent: %d\n", wc->rxident, wc->txident); + for (x=0;xcards;x++) + printk("Card %d isrshadow: %02x/%02x\n", x, wc->cmdq[x].isrshadow[0], wc->cmdq[x].isrshadow[1]); + cmddesc = 0; +#endif +#if 0 + if (wc->vpm) { + char tmp[80]; + for (x=0;x<0x200;x++) { + switch (x & 0xf) { + case 0: + sprintf(tmp, "%03x: %02x ", x, wctdm_vpm_in(wc, 0, x)); + break; + case 0xf: + printk("%s%02x\n", tmp, wctdm_vpm_in(wc, 0, x)); + break; + default: + sprintf(tmp + strlen(tmp), "%02x ", wctdm_vpm_in(wc, 0, x)); + break; + } + } + } + +#endif + switch (cmd) { + case ZT_ONHOOKTRANSFER: + if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) + return -EINVAL; + if (get_user(x, (int *)data)) + return -EFAULT; + wc->mods[chan->chanpos - 1].fxs.ohttimer = x << 3; + wc->mods[chan->chanpos - 1].fxs.idletxhookstate = 0x2; /* OHT mode when idle */ + if (wc->mods[chan->chanpos - 1].fxs.lasttxhook == 0x1) { + /* Apply the change if appropriate */ + wc->mods[chan->chanpos - 1].fxs.lasttxhook = 0x2; + wc->sethook[chan->chanpos - 1] = CMD_WR(64, wc->mods[chan->chanpos - 1].fxs.lasttxhook); + /* wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mods[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((struct wctdm_stats *)data, &stats, sizeof(stats))) + return -EFAULT; + break; + case WCTDM_GET_REGS: + if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { + for (x=0;xchanpos -1, x); + for (x=0;xchanpos - 1, x); + } else if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_QRV) { + memset(®s, 0, sizeof(regs)); + for (x=0;x<0x32;x++) + regs.direct[x] = wctdm_getreg(wc, chan->chanpos - 1, x); + } else { + memset(®s, 0, sizeof(regs)); + for (x=0;xchanpos - 1, x); + } + 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->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) + return -EINVAL; + printk("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("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("-- Setting echo registers: \n"); + if (copy_from_user(&echoregs, (struct wctdm_echo_coefs*)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, 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("-- Set echo registers successfully\n"); + + break; + } else { + return -EINVAL; + + } + break; +#ifdef VPM_SUPPORT + case ZT_TONEDETECT: + if (get_user(x, (int *) data)) + return -EFAULT; + if (!wc->vpm && !wc->vpm150m) + return -ENOSYS; + if ((wc->vpm || wc->vpm150m) && (x && !vpmdtmfsupport)) + return -ENOSYS; + if (x & ZT_TONEDETECT_ON) { + set_bit(chan->chanpos - 1, &wc->dtmfmask); + } else { + clear_bit(chan->chanpos - 1, &wc->dtmfmask); + } + if (x & ZT_TONEDETECT_MUTE) { + if (wc->vpm150m) { + set_bit(chan->chanpos - 1, &wc->vpm150m->desireddtmfmutestate); + } + } else { + if (wc->vpm150m) { + clear_bit(chan->chanpos - 1, &wc->vpm150m->desireddtmfmutestate); + } + } + return 0; +#endif + case ZT_RADIO_GETPARAM: + if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_QRV) + return -ENOTTY; + if (copy_from_user(&stack.p,(struct zt_radio_param *)data,sizeof(struct zt_radio_param))) return -EFAULT; + stack.p.data = 0; /* start with 0 value in output */ + switch(stack.p.radpar) { + case ZT_RADPAR_INVERTCOR: + if (wc->radmode[chan->chanpos - 1] & RADMODE_INVERTCOR) + stack.p.data = 1; + break; + case ZT_RADPAR_IGNORECOR: + if (wc->radmode[chan->chanpos - 1] & RADMODE_IGNORECOR) + stack.p.data = 1; + break; + case ZT_RADPAR_IGNORECT: + if (wc->radmode[chan->chanpos - 1] & RADMODE_IGNORECT) + stack.p.data = 1; + break; + case ZT_RADPAR_EXTRXTONE: + stack.p.data = 0; + if (wc->radmode[chan->chanpos - 1] & RADMODE_EXTTONE) + { + stack.p.data = 1; + if (wc->radmode[chan->chanpos - 1] & RADMODE_EXTINVERT) + { + stack.p.data = 2; + } + } + break; + case ZT_RADPAR_DEBOUNCETIME: + stack.p.data = wc->debouncetime[chan->chanpos - 1]; + break; + case ZT_RADPAR_RXGAIN: + stack.p.data = wc->rxgain[chan->chanpos - 1] - 1199; + break; + case ZT_RADPAR_TXGAIN: + stack.p.data = wc->txgain[chan->chanpos - 1] - 3599; + break; + case ZT_RADPAR_DEEMP: + stack.p.data = 0; + if (wc->radmode[chan->chanpos - 1] & RADMODE_DEEMP) + { + stack.p.data = 1; + } + break; + case ZT_RADPAR_PREEMP: + stack.p.data = 0; + if (wc->radmode[chan->chanpos - 1] & RADMODE_PREEMP) + { + stack.p.data = 1; + } + break; + default: + return -EINVAL; + } + if (copy_to_user((struct zt_radio_param *)data,&stack.p,sizeof(struct zt_radio_param))) return -EFAULT; + break; + case ZT_RADIO_SETPARAM: + if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_QRV) + return -ENOTTY; + if (copy_from_user(&stack.p,(struct zt_radio_param *)data,sizeof(struct zt_radio_param))) return -EFAULT; + switch(stack.p.radpar) { + case ZT_RADPAR_INVERTCOR: + if (stack.p.data) + wc->radmode[chan->chanpos - 1] |= RADMODE_INVERTCOR; + else + wc->radmode[chan->chanpos - 1] &= ~RADMODE_INVERTCOR; + return 0; + case ZT_RADPAR_IGNORECOR: + if (stack.p.data) + wc->radmode[chan->chanpos - 1] |= RADMODE_IGNORECOR; + else + wc->radmode[chan->chanpos - 1] &= ~RADMODE_IGNORECOR; + return 0; + case ZT_RADPAR_IGNORECT: + if (stack.p.data) + wc->radmode[chan->chanpos - 1] |= RADMODE_IGNORECT; + else + wc->radmode[chan->chanpos - 1] &= ~RADMODE_IGNORECT; + return 0; + case ZT_RADPAR_EXTRXTONE: + if (stack.p.data) + wc->radmode[chan->chanpos - 1] |= RADMODE_EXTTONE; + else + wc->radmode[chan->chanpos - 1] &= ~RADMODE_EXTTONE; + if (stack.p.data > 1) + wc->radmode[chan->chanpos - 1] |= RADMODE_EXTINVERT; + else + wc->radmode[chan->chanpos - 1] &= ~RADMODE_EXTINVERT; + return 0; + case ZT_RADPAR_DEBOUNCETIME: + wc->debouncetime[chan->chanpos - 1] = stack.p.data; + return 0; + case ZT_RADPAR_RXGAIN: + /* if out of range */ + if ((stack.p.data <= -1200) || (stack.p.data > 1552)) + { + return -EINVAL; + } + wc->rxgain[chan->chanpos - 1] = stack.p.data + 1199; + break; + case ZT_RADPAR_TXGAIN: + /* if out of range */ + if (wc->radmode[chan->chanpos -1] & RADMODE_PREEMP) + { + if ((stack.p.data <= -2400) || (stack.p.data > 0)) + { + return -EINVAL; + } + } + else + { + if ((stack.p.data <= -3600) || (stack.p.data > 1200)) + { + return -EINVAL; + } + } + wc->txgain[chan->chanpos - 1] = stack.p.data + 3599; + break; + case ZT_RADPAR_DEEMP: + if (stack.p.data) + wc->radmode[chan->chanpos - 1] |= RADMODE_DEEMP; + else + wc->radmode[chan->chanpos - 1] &= ~RADMODE_DEEMP; + wc->rxgain[chan->chanpos - 1] = 1199; + break; + case ZT_RADPAR_PREEMP: + if (stack.p.data) + wc->radmode[chan->chanpos - 1] |= RADMODE_PREEMP; + else + wc->radmode[chan->chanpos - 1] &= ~RADMODE_PREEMP; + wc->txgain[chan->chanpos - 1] = 3599; + break; + default: + return -EINVAL; + } + qrv_dosetup(chan,wc); + return 0; + default: + return -ENOTTY; + } + return 0; +} + +static int wctdm_open(struct zt_chan *chan) +{ + struct wctdm *wc = chan->pvt; + if (!(wc->cardflag & (1 << (chan->chanpos - 1)))) + return -ENODEV; + if (wc->dead) + return -ENODEV; + wc->usecount++; +#ifndef LINUX26 + MOD_INC_USE_COUNT; +#else + try_module_get(THIS_MODULE); +#endif + return 0; +} + +static int wctdm_watchdog(struct zt_span *span, int event) +{ + printk("TDM: Restarting DMA\n"); + wctdm_restart_dma(span->pvt); + return 0; +} + +static int wctdm_close(struct zt_chan *chan) +{ + struct wctdm *wc = chan->pvt; + int x; + signed char reg; + wc->usecount--; +#ifndef LINUX26 + MOD_DEC_USE_COUNT; +#else + module_put(THIS_MODULE); +#endif + for (x=0;xcards;x++) { + if (wc->modtype[x] == MOD_TYPE_FXS) + wc->mods[x].fxs.idletxhookstate = 1; + if (wc->modtype[x] == MOD_TYPE_QRV) + { + int qrvcard = x & 0xfc; + + wc->qrvhook[x] = 0; + wc->qrvhook[x + 2] = 0xff; + wc->debouncetime[x] = QRV_DEBOUNCETIME; + wc->qrvdebtime[x] = 0; + wc->radmode[x] = 0; + wc->txgain[x] = 3599; + wc->rxgain[x] = 1199; + reg = 0; + if (!wc->qrvhook[qrvcard]) reg |= 1; + if (!wc->qrvhook[qrvcard + 1]) reg |= 0x10; + wc->sethook[qrvcard] = CMD_WR(3, reg); + qrv_dosetup(chan,wc); + } + } + /* If we're dead, release us now */ + if (!wc->usecount && wc->dead) + wctdm_release(wc); + return 0; +} + +static int wctdm_hooksig(struct zt_chan *chan, zt_txsig_t txsig) +{ + struct wctdm *wc = chan->pvt; + int reg=0,qrvcard; + if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_QRV) { + qrvcard = (chan->chanpos - 1) & 0xfc; + switch(txsig) { + case ZT_TXSIG_START: + case ZT_TXSIG_OFFHOOK: + wc->qrvhook[chan->chanpos - 1] = 1; + break; + case ZT_TXSIG_ONHOOK: + wc->qrvhook[chan->chanpos - 1] = 0; + break; + default: + printk("wctdm24xxp: Can't set tx state to %d\n", txsig); + } + reg = 0; + if (!wc->qrvhook[qrvcard]) reg |= 1; + if (!wc->qrvhook[qrvcard + 1]) reg |= 0x10; + wc->sethook[qrvcard] = CMD_WR(3, reg); + /* wctdm_setreg(wc, qrvcard, 3, reg); */ + } else if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { + switch(txsig) { + case ZT_TXSIG_START: + case ZT_TXSIG_OFFHOOK: + wc->mods[chan->chanpos - 1].fxo.offhook = 1; + wc->sethook[chan->chanpos - 1] = CMD_WR(5, 0x9); + /* wctdm_setreg(wc, chan->chanpos - 1, 5, 0x9); */ + break; + case ZT_TXSIG_ONHOOK: + wc->mods[chan->chanpos - 1].fxo.offhook = 0; + wc->sethook[chan->chanpos - 1] = CMD_WR(5, 0x8); + /* wctdm_setreg(wc, chan->chanpos - 1, 5, 0x8); */ + break; + default: + printk("wctdm24xxp: Can't set tx state to %d\n", txsig); + } + } else { + switch(txsig) { + case ZT_TXSIG_ONHOOK: + switch(chan->sig) { + case ZT_SIG_EM: + case ZT_SIG_FXOKS: + case ZT_SIG_FXOLS: + wc->mods[chan->chanpos - 1].fxs.lasttxhook = wc->mods[chan->chanpos - 1].fxs.idletxhookstate; + break; + case ZT_SIG_FXOGS: + wc->mods[chan->chanpos - 1].fxs.lasttxhook = 3; + break; + } + break; + case ZT_TXSIG_OFFHOOK: + switch(chan->sig) { + case ZT_SIG_EM: + wc->mods[chan->chanpos - 1].fxs.lasttxhook = 5; + break; + default: + wc->mods[chan->chanpos - 1].fxs.lasttxhook = wc->mods[chan->chanpos - 1].fxs.idletxhookstate; + break; + } + break; + case ZT_TXSIG_START: + wc->mods[chan->chanpos - 1].fxs.lasttxhook = 4; + break; + case ZT_TXSIG_KEWL: + wc->mods[chan->chanpos - 1].fxs.lasttxhook = 0; + break; + default: + printk("wctdm24xxp: Can't set tx state to %d\n", txsig); + } + if (debug & DEBUG_CARD) + printk("Setting FXS hook state to %d (%02x)\n", txsig, reg); + + + wc->sethook[chan->chanpos - 1] = CMD_WR(64, wc->mods[chan->chanpos - 1].fxs.lasttxhook); + /* wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mods[chan->chanpos - 1].fxs.lasttxhook); */ + } + return 0; +} + +static void wctdm_dacs_connect(struct wctdm *wc, int srccard, int dstcard) +{ + + if (wc->dacssrc[dstcard] > - 1) { + printk("wctdm_dacs_connect: Can't have double sourcing yet!\n"); + return; + } + if (!((wc->modtype[srccard] == MOD_TYPE_FXS)||(wc->modtype[srccard] == MOD_TYPE_FXO))){ + printk("wctdm_dacs_connect: Unsupported modtype for card %d\n", srccard); + return; + } + if (!((wc->modtype[dstcard] == MOD_TYPE_FXS)||(wc->modtype[dstcard] == MOD_TYPE_FXO))){ + printk("wctdm_dacs_connect: Unsupported modtype for card %d\n", dstcard); + return; + } + if (debug) + printk("connect %d => %d\n", srccard, dstcard); + wc->dacssrc[dstcard] = srccard; + + /* make srccard transmit to srccard+24 on the TDM bus */ + if (wc->modtype[srccard] == MOD_TYPE_FXS) { + /* proslic */ + wctdm_setreg(wc, srccard, PCM_XMIT_START_COUNT_LSB, ((srccard+24) * 8) & 0xff); + wctdm_setreg(wc, srccard, PCM_XMIT_START_COUNT_MSB, ((srccard+24) * 8) >> 8); + } else if(wc->modtype[srccard] == MOD_TYPE_FXO) { + /* daa */ + wctdm_setreg(wc, srccard, 34, ((srccard+24) * 8) & 0xff); /* TX */ + wctdm_setreg(wc, srccard, 35, ((srccard+24) * 8) >> 8); /* TX */ + } + + /* have dstcard receive from srccard+24 on the TDM bus */ + if (wc->modtype[dstcard] == MOD_TYPE_FXS) { + /* proslic */ + wctdm_setreg(wc, dstcard, PCM_RCV_START_COUNT_LSB, ((srccard+24) * 8) & 0xff); + wctdm_setreg(wc, dstcard, PCM_RCV_START_COUNT_MSB, ((srccard+24) * 8) >> 8); + } else if(wc->modtype[dstcard] == MOD_TYPE_FXO) { + /* daa */ + wctdm_setreg(wc, dstcard, 36, ((srccard+24) * 8) & 0xff); /* RX */ + wctdm_setreg(wc, dstcard, 37, ((srccard+24) * 8) >> 8); /* RX */ + } + +} + +static void wctdm_dacs_disconnect(struct wctdm *wc, int card) +{ + if (wc->dacssrc[card] > -1) { + if (debug) + printk("wctdm_dacs_disconnect: restoring TX for %d and RX for %d\n",wc->dacssrc[card], card); + + /* restore TX (source card) */ + if(wc->modtype[wc->dacssrc[card]] == MOD_TYPE_FXS){ + wctdm_setreg(wc, wc->dacssrc[card], PCM_XMIT_START_COUNT_LSB, (wc->dacssrc[card] * 8) & 0xff); + wctdm_setreg(wc, wc->dacssrc[card], PCM_XMIT_START_COUNT_MSB, (wc->dacssrc[card] * 8) >> 8); + } else if(wc->modtype[wc->dacssrc[card]] == MOD_TYPE_FXO){ + wctdm_setreg(wc, card, 34, (card * 8) & 0xff); + wctdm_setreg(wc, card, 35, (card * 8) >> 8); + } else { + printk("WARNING: wctdm_dacs_disconnect() called on unsupported modtype\n"); + } + + /* restore RX (this card) */ + if(wc->modtype[card] == MOD_TYPE_FXS){ + wctdm_setreg(wc, card, PCM_RCV_START_COUNT_LSB, (card * 8) & 0xff); + wctdm_setreg(wc, card, PCM_RCV_START_COUNT_MSB, (card * 8) >> 8); + } else if(wc->modtype[card] == MOD_TYPE_FXO){ + wctdm_setreg(wc, card, 36, (card * 8) & 0xff); + wctdm_setreg(wc, card, 37, (card * 8) >> 8); + } else { + printk("WARNING: wctdm_dacs_disconnect() called on unsupported modtype\n"); + } + + wc->dacssrc[card] = -1; + } +} + +static int wctdm_dacs(struct zt_chan *dst, struct zt_chan *src) +{ + struct wctdm *wc; + + if(!nativebridge) + return 0; /* should this return -1 since unsuccessful? */ + + wc = dst->pvt; + + if(src) { + wctdm_dacs_connect(wc, src->chanpos - 1, dst->chanpos - 1); + if (debug) + printk("dacs connecct: %d -> %d!\n\n", src->chanpos, dst->chanpos); + } else { + wctdm_dacs_disconnect(wc, dst->chanpos - 1); + if (debug) + printk("dacs disconnect: %d!\n", dst->chanpos); + } + return 0; +} + +static int wctdm_initialize(struct wctdm *wc) +{ + int x; + + /* Zapata stuff */ + sprintf(wc->span.name, "WCTDM/%d", wc->pos); + sprintf(wc->span.desc, "%s Board %d", wc->variety, wc->pos + 1); + if (alawoverride) { + printk("ALAW override parameter detected. Device will be operating in ALAW\n"); + wc->span.deflaw = ZT_LAW_ALAW; + } else + wc->span.deflaw = ZT_LAW_MULAW; + for (x=0;xcards;x++) { + sprintf(wc->chans[x].name, "WCTDM/%d/%d", wc->pos, x); + wc->chans[x].sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_SF | ZT_SIG_EM | ZT_SIG_CLEAR; + wc->chans[x].sigcap |= ZT_SIG_FXSKS | ZT_SIG_FXSLS | ZT_SIG_SF | ZT_SIG_CLEAR; + wc->chans[x].chanpos = x+1; + wc->chans[x].pvt = wc; + } + wc->span.chans = wc->chans; + wc->span.channels = wc->type; + wc->span.hooksig = wctdm_hooksig; + wc->span.open = wctdm_open; + wc->span.close = wctdm_close; + wc->span.flags = ZT_FLAG_RBS; + wc->span.ioctl = wctdm_ioctl; + wc->span.watchdog = wctdm_watchdog; + wc->span.dacs= wctdm_dacs; +#ifdef VPM_SUPPORT + wc->span.echocan = wctdm_echocan; +#endif + init_waitqueue_head(&wc->span.maintq); + + wc->span.pvt = wc; + if (zt_register(&wc->span, 0)) { + printk("Unable to register span with zaptel\n"); + return -1; + } + return 0; +} + +static void wctdm_post_initialize(struct wctdm *wc) +{ + int x; + /* Finalize signalling */ + for (x=0;xcards;x++) { + if (wc->cardflag & (1 << x)) { + if (wc->modtype[x] == MOD_TYPE_FXO) + wc->chans[x].sigcap = ZT_SIG_FXSKS | ZT_SIG_FXSLS | ZT_SIG_SF | ZT_SIG_CLEAR; + else if (wc->modtype[x] == MOD_TYPE_FXS) + wc->chans[x].sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_SF | ZT_SIG_EM | ZT_SIG_CLEAR; + else if (wc->modtype[x] == MOD_TYPE_QRV) + wc->chans[x].sigcap = ZT_SIG_SF | ZT_SIG_EM | ZT_SIG_CLEAR; + } + } +} + +static int wctdm_hardware_init(struct wctdm *wc) +{ + /* Hardware stuff */ + unsigned int reg; + unsigned long newjiffies; + + /* Initialize descriptors */ + wctdm_init_descriptors(wc); + + /* Enable I/O Access */ + pci_read_config_dword(wc->dev, 0x0004, ®); + reg |= 0x00000007; + pci_write_config_dword(wc->dev, 0x0004, reg); + printk("PCI Config reg is %08x\n", reg); + + wctdm_setctl(wc, 0x0000, 0xfff88001); + + newjiffies = jiffies + HZ/10; + while(((reg = wctdm_getctl(wc,0x0000)) & 0x00000001) && (newjiffies > jiffies)); + printk("%s: New Reg: %08x!\n", wc->variety, reg); + + + /* Configure watchdogs, access, etc */ + wctdm_setctl(wc, 0x0030, 0x00080048); + wctdm_setctl(wc, 0x0078, 0x00000013 /* | (1 << 28) */); + +#if 0 + /* XXX Enable loopback XXX */ + reg = wctdm_getctl(wc, 0x0030); + wctdm_setctl(wc, 0x0030, reg | 0x00000400); + +#else + reg = wctdm_getctl(wc, 0x00fc); + wctdm_setctl(wc, 0x00fc, (reg & ~0x7) | 0x7); + wctdm_setsdi(wc, 0x00, 0x0100); + wctdm_setsdi(wc, 0x16, 0x2100); + printk("Detected REG0: %08x\n", wctdm_getsdi(wc, 0x00)); + printk("Detected REG1: %08x\n", wctdm_getsdi(wc, 0x01)); + printk("Detected REG2: %08x\n", wctdm_getsdi(wc, 0x02)); + + reg = wctdm_getctl(wc, 0x00fc); + printk("(pre) Reg fc is %08x\n", reg); + + wctdm_setctl(wc, 0x00fc, (reg & ~0x7) | 0x4); + wctdm_setsdi(wc, 0x00, 0x0100); + wctdm_setsdi(wc, 0x16, 0x2100); + reg = wctdm_getctl(wc, 0x00fc); + printk("(post) Reg fc is %08x\n", reg); + printk("Detected REG2: %08x\n", wctdm_getsdi(wc, 0x02)); +#endif + printk("wctdm24xxp: reg is %08x\n", wctdm_getctl(wc, 0x0088)); + + return 0; +} + +static void wctdm_setintmask(struct wctdm *wc, unsigned int intmask) +{ + wc->intmask = intmask; + wctdm_setctl(wc, 0x0038, intmask); +} + +static void wctdm_enable_interrupts(struct wctdm *wc) +{ + /* Enable interrupts */ + wctdm_setintmask(wc, 0x00010041); +} + +static void wctdm_restart_dma(struct wctdm *wc) +{ +} + +static void wctdm_start_dma(struct wctdm *wc) +{ + unsigned int reg; + wmb(); + wctdm_setctl(wc, 0x0020, wc->descripdma); + wctdm_setctl(wc, 0x0018, wc->descripdma + (16 * ERING_SIZE)); + /* Start receiver/transmitter */ + reg = wctdm_getctl(wc, 0x0030); + wctdm_setctl(wc, 0x0030, reg | 0x00002002); + wctdm_setctl(wc, 0x0008, 0x00000000); + wctdm_setctl(wc, 0x0010, 0x00000000); + reg = wctdm_getctl(wc, 0x0028); + wctdm_setctl(wc, 0x0028, reg); + +} + +static void wctdm_stop_dma(struct wctdm *wc) +{ + /* Disable interrupts and reset */ + unsigned int reg; + /* Disable interrupts */ + wctdm_setintmask(wc, 0x00000000); + wctdm_setctl(wc, 0x0084, 0x00000000); + wctdm_setctl(wc, 0x0048, 0x00000000); + /* Reset the part to be on the safe side */ + reg = wctdm_getctl(wc, 0x0000); + reg |= 0x00000001; + wctdm_setctl(wc, 0x0000, reg); +} + +static void wctdm_disable_interrupts(struct wctdm *wc) +{ + /* Disable interrupts */ + wctdm_setintmask(wc, 0x00000000); + wctdm_setctl(wc, 0x0084, 0x00000000); +} + +#ifdef VPM_SUPPORT +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +static void vpm150m_bh(void *data) +{ + struct vpm150m *vpm150m = data; +#else +static void vpm150m_bh(struct work_struct *data) +{ + struct vpm150m *vpm150m = container_of(data, struct vpm150m, work); +#endif + struct wctdm *wc = vpm150m->wc; + int i; + + for (i = 0; i < wc->type; i++) { + int enable = -1; + if (test_bit(i, &vpm150m->desireddtmfmutestate)) { + if (!test_bit(i, &vpm150m->curdtmfmutestate)) { + enable = 1; + } + } else { + if (test_bit(i, &vpm150m->curdtmfmutestate)) { + enable = 0; + } + } + if (enable > -1) { + unsigned int start = wc->intcount; + GPAK_AlgControlStat_t pstatus; + int res; + + if (enable) { + res = gpakAlgControl(vpm150m->dspid, i, EnableDTMFMuteA, &pstatus); + if (debug & DEBUG_ECHOCAN) + printk("DTMF mute enable took %d ms\n", wc->intcount - start); + } else { + res = gpakAlgControl(vpm150m->dspid, i, DisableDTMFMuteA, &pstatus); + if (debug & DEBUG_ECHOCAN) + printk("DTMF mute disable took %d ms\n", wc->intcount - start); + } + if (!res) + change_bit(i, &vpm150m->curdtmfmutestate); + } + } + + if (test_bit(VPM150M_DTMFDETECT, &vpm150m->control)) { + unsigned short channel; + GpakAsyncEventCode_t eventcode; + GpakAsyncEventData_t eventdata; + gpakReadEventFIFOMessageStat_t res; +#if 1 + unsigned int start = wc->intcount; +#endif + + do { + res = gpakReadEventFIFOMessage(vpm150m->dspid, &channel, &eventcode, &eventdata); + +#if 1 + if (debug & DEBUG_ECHOCAN) + printk("ReadEventFIFOMessage took %d ms\n", wc->intcount - start); +#endif + + if (res == RefInvalidEvent || res == RefDspCommFailure) { + printk("Uh oh (%d)\n", res); + continue; + } + + if (eventcode == EventToneDetect) { + GpakToneCodes_t tone = eventdata.toneEvent.ToneCode; + int duration = eventdata.toneEvent.ToneDuration; + char zaptone = vpm150mtone_to_zaptone(tone); + + if (debug & DEBUG_ECHOCAN) + printk("Channel %d: Detected DTMF tone %d of duration %d!!!\n", channel + 1, tone, duration); + + if (test_bit(channel, &wc->dtmfmask) && (eventdata.toneEvent.ToneDuration > 0)) { + struct zt_chan *chan = &wc->chans[channel]; + + if ((tone != EndofMFDigit) && (zaptone != 0)) { + vpm150m->curtone[channel] = tone; + + if (test_bit(channel, &vpm150m->curdtmfmutestate)) { + unsigned long flags; + int y; + + /* Mute the audio data buffers */ + spin_lock_irqsave(&chan->lock, flags); + for (y = 0; y < chan->numbufs; y++) { + if ((chan->inreadbuf > -1) && (chan->readidx[y])) + memset(chan->readbuf[chan->inreadbuf], ZT_XLAW(0, chan), chan->readidx[y]); + } + spin_unlock_irqrestore(&chan->lock, flags); + } + if (!test_bit(channel, &wc->dtmfactive)) { + if (debug & DEBUG_ECHOCAN) + printk("Queuing DTMFDOWN %c\n", zaptone); + set_bit(channel, &wc->dtmfactive); + zt_qevent_lock(chan, (ZT_EVENT_DTMFDOWN | zaptone)); + } + } else if ((tone == EndofMFDigit) && test_bit(channel, &wc->dtmfactive)) { + if (debug & DEBUG_ECHOCAN) + printk("Queuing DTMFUP %c\n", vpm150mtone_to_zaptone(vpm150m->curtone[channel])); + zt_qevent_lock(chan, (ZT_EVENT_DTMFUP | vpm150mtone_to_zaptone(vpm150m->curtone[channel]))); + clear_bit(channel, &wc->dtmfactive); + } + } + } + } while ((res != RefNoEventAvail) && (res != RefInvalidEvent) && (res != RefDspCommFailure)); + } + + for (i = 0; i < wc->type; i++) { + int enable = -1; + if (test_bit(i, &vpm150m->desiredecstate)) { + if (!test_bit(i, &vpm150m->curecstate)) { + enable = 1; + } + } else { + if (test_bit(i, &vpm150m->curecstate)) { + enable = 0; + } + } + if (enable > -1) { + unsigned int start = wc->intcount; + GPAK_AlgControlStat_t pstatus; + int res; + + if (enable) { + res = gpakAlgControl(vpm150m->dspid, i, EnableEcanA, &pstatus); + if (debug & DEBUG_ECHOCAN) + printk("Echocan enable took %d ms\n", wc->intcount - start); + } else { + res = gpakAlgControl(vpm150m->dspid, i, BypassEcanA, &pstatus); + if (debug & DEBUG_ECHOCAN) + printk("Echocan disable took %d ms\n", wc->intcount - start); + } + if (!res) + change_bit(i, &vpm150m->curecstate); + } + } + + return; +} + +static int vpm150m_config_hw(struct wctdm *wc) +{ + struct vpm150m *vpm150m = wc->vpm150m; + gpakConfigPortStatus_t configportstatus; + GpakPortConfig_t portconfig; + GPAK_PortConfigStat_t pstatus; + GpakChannelConfig_t chanconfig; + GPAK_ChannelConfigStat_t cstatus; + GPAK_AlgControlStat_t algstatus; + + int res, i; + + memset(&portconfig, 0, sizeof(GpakPortConfig_t)); + + /* First Serial Port config */ + portconfig.SlotsSelect1 = SlotCfgNone; + portconfig.FirstBlockNum1 = 0; + portconfig.FirstSlotMask1 = 0x0000; + portconfig.SecBlockNum1 = 1; + portconfig.SecSlotMask1 = 0x0000; + portconfig.SerialWordSize1 = SerWordSize8; + portconfig.CompandingMode1 = cmpNone; + portconfig.TxFrameSyncPolarity1 = FrameSyncActHigh; + portconfig.RxFrameSyncPolarity1 = FrameSyncActHigh; + portconfig.TxClockPolarity1 = SerClockActHigh; + portconfig.RxClockPolarity1 = SerClockActHigh; + portconfig.TxDataDelay1 = DataDelay0; + portconfig.RxDataDelay1 = DataDelay0; + portconfig.DxDelay1 = Disabled; + portconfig.ThirdSlotMask1 = 0x0000; + portconfig.FouthSlotMask1 = 0x0000; + portconfig.FifthSlotMask1 = 0x0000; + portconfig.SixthSlotMask1 = 0x0000; + portconfig.SevenSlotMask1 = 0x0000; + portconfig.EightSlotMask1 = 0x0000; + + /* Second Serial Port config */ + portconfig.SlotsSelect2 = SlotCfg2Groups; + portconfig.FirstBlockNum2 = 0; + portconfig.FirstSlotMask2 = 0xffff; + portconfig.SecBlockNum2 = 1; + portconfig.SecSlotMask2 = 0xffff; + portconfig.SerialWordSize2 = SerWordSize8; + portconfig.CompandingMode2 = cmpNone; + portconfig.TxFrameSyncPolarity2 = FrameSyncActHigh; + portconfig.RxFrameSyncPolarity2 = FrameSyncActHigh; + portconfig.TxClockPolarity2 = SerClockActHigh; + portconfig.RxClockPolarity2 = SerClockActHigh; + portconfig.TxDataDelay2 = DataDelay0; + portconfig.RxDataDelay2 = DataDelay0; + portconfig.DxDelay2 = Disabled; + portconfig.ThirdSlotMask2 = 0x0000; + portconfig.FouthSlotMask2 = 0x0000; + portconfig.FifthSlotMask2 = 0x0000; + portconfig.SixthSlotMask2 = 0x0000; + portconfig.SevenSlotMask2 = 0x0000; + portconfig.EightSlotMask2 = 0x0000; + + /* Third Serial Port Config */ + portconfig.SlotsSelect3 = SlotCfg2Groups; + portconfig.FirstBlockNum3 = 0; + portconfig.FirstSlotMask3 = 0xffff; + portconfig.SecBlockNum3 = 1; + portconfig.SecSlotMask3 = 0xffff; + portconfig.SerialWordSize3 = SerWordSize8; + portconfig.CompandingMode3 = cmpNone; + portconfig.TxFrameSyncPolarity3 = FrameSyncActHigh; + portconfig.RxFrameSyncPolarity3 = FrameSyncActHigh; + portconfig.TxClockPolarity3 = SerClockActHigh; + portconfig.RxClockPolarity3 = SerClockActHigh; + portconfig.TxDataDelay3 = DataDelay0; + portconfig.RxDataDelay3 = DataDelay0; + portconfig.DxDelay3 = Disabled; + portconfig.ThirdSlotMask3 = 0x0000; + portconfig.FouthSlotMask3 = 0x0000; + portconfig.FifthSlotMask3 = 0x0000; + portconfig.SixthSlotMask3 = 0x0000; + portconfig.SevenSlotMask3 = 0x0000; + portconfig.EightSlotMask3 = 0x0000; + + if ((configportstatus = gpakConfigurePorts(vpm150m->dspid, &portconfig, &pstatus))) { + printk("Configuration of ports failed (%d)!\n", configportstatus); + return -1; + } else { + if (debug & DEBUG_ECHOCAN) + printk("Configured McBSP ports successfully\n"); + } + + if ((res = gpakPingDsp(vpm150m->dspid, &vpm150m->version))) { + printk("Error pinging DSP (%d)\n", res); + return -1; + } + + for (i = 0; i < wc->type; i++) { + /* Let's configure a channel */ + chanconfig.PcmInPortA = 3; + chanconfig.PcmInSlotA = i; + chanconfig.PcmOutPortA = SerialPortNull; + chanconfig.PcmOutSlotA = i; + chanconfig.PcmInPortB = 2; + chanconfig.PcmInSlotB = i; + chanconfig.PcmOutPortB = 3; + chanconfig.PcmOutSlotB = i; + if (vpmdtmfsupport) { + chanconfig.ToneTypesA = DTMF_tone; + chanconfig.MuteToneA = Enabled; + chanconfig.FaxCngDetA = Enabled; + } else { + chanconfig.ToneTypesA = Null_tone; + chanconfig.MuteToneA = Disabled; + chanconfig.FaxCngDetA = Disabled; + } + chanconfig.ToneTypesB = Null_tone; + chanconfig.EcanEnableA = Enabled; + chanconfig.EcanEnableB = Disabled; + chanconfig.MuteToneB = Disabled; + chanconfig.FaxCngDetB = Disabled; + + if (alawoverride) + chanconfig.SoftwareCompand = cmpPCMA; + else + chanconfig.SoftwareCompand = cmpPCMU; + + chanconfig.FrameRate = rate2ms; + + chanconfig.EcanParametersA.EcanTapLength = 1024; + chanconfig.EcanParametersA.EcanNlpType = 1; + chanconfig.EcanParametersA.EcanAdaptEnable = 1; + chanconfig.EcanParametersA.EcanG165DetEnable = 0; + chanconfig.EcanParametersA.EcanDblTalkThresh = 6; + chanconfig.EcanParametersA.EcanNlpThreshold = 24; + chanconfig.EcanParametersA.EcanNlpConv = 0; + chanconfig.EcanParametersA.EcanNlpUnConv = 0; + chanconfig.EcanParametersA.EcanNlpMaxSuppress = 0; + chanconfig.EcanParametersA.EcanCngThreshold = 43; + chanconfig.EcanParametersA.EcanAdaptLimit = 50; + chanconfig.EcanParametersA.EcanCrossCorrLimit = 15; + chanconfig.EcanParametersA.EcanNumFirSegments = 3; + chanconfig.EcanParametersA.EcanFirSegmentLen = 64; + + chanconfig.EcanParametersB.EcanTapLength = 1024; + chanconfig.EcanParametersB.EcanNlpType = 1; + chanconfig.EcanParametersB.EcanAdaptEnable = 1; + chanconfig.EcanParametersB.EcanG165DetEnable = 0; + chanconfig.EcanParametersB.EcanDblTalkThresh = 6; + chanconfig.EcanParametersB.EcanNlpThreshold = 24; + chanconfig.EcanParametersB.EcanNlpConv = 0; + chanconfig.EcanParametersB.EcanNlpUnConv = 0; + chanconfig.EcanParametersB.EcanNlpMaxSuppress = 0; + chanconfig.EcanParametersB.EcanCngThreshold = 43; + chanconfig.EcanParametersB.EcanAdaptLimit = 50; + chanconfig.EcanParametersB.EcanCrossCorrLimit = 15; + chanconfig.EcanParametersB.EcanNumFirSegments = 3; + chanconfig.EcanParametersB.EcanFirSegmentLen = 64; + + if ((res = gpakConfigureChannel(vpm150m->dspid, i, tdmToTdm, &chanconfig, &cstatus))) { + printk("Unable to configure channel (%d)\n", res); + if (res == 1) { + printk("Reason %d\n", cstatus); + } + + return -1; + } + + if ((res = gpakAlgControl(vpm150m->dspid, i, BypassEcanA, &algstatus))) { + printk("Unable to disable echo can on channel %d (reason %d:%d)\n", i + 1, res, algstatus); + return -1; + } + + if (vpmdtmfsupport) { + if ((res = gpakAlgControl(vpm150m->dspid, i, DisableDTMFMuteA, &algstatus))) { + printk("Unable to disable dtmf muting on channel %d (reason %d:%d)\n", i + 1, res, algstatus); + return -1; + } + } + } + + if ((res = gpakPingDsp(vpm150m->dspid, &vpm150m->version))) { + printk("Error pinging DSP (%d)\n", res); + return -1; + } + + vpm150m->wq = create_singlethread_workqueue("wctdm24xxp"); + vpm150m->wc = wc; + + if (!vpm150m->wq) { + printk("Unable to create work queue!\n"); + return -1; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + INIT_WORK(&vpm150m->work, vpm150m_bh, vpm150m); +#else + INIT_WORK(&vpm150m->work, vpm150m_bh); +#endif + + /* Turn on DTMF detection */ + if (vpmdtmfsupport) + set_bit(VPM150M_DTMFDETECT, &vpm150m->control); + + set_bit(VPM150M_ACTIVE, &vpm150m->control); + + return 0; +} + +static void wctdm_vpm150m_init(struct wctdm *wc) +{ + unsigned short i; + struct vpm150m *vpm150m; + unsigned short reg; + struct wctdm_firmware fw; + gpakDownloadStatus_t downloadstatus; + gpakPingDspStat_t pingstatus; + unsigned long flags; + + if (!vpmsupport) { + printk("VPM: Support Disabled\n"); + wc->vpm150m = NULL; + return; + } + + vpm150m = kmalloc(sizeof(struct vpm150m), GFP_KERNEL); + + if (!vpm150m) { + printk("Unable to allocate VPM150M!\n"); + return; + } + + memset(vpm150m, 0, sizeof(struct vpm150m)); + + /* Init our vpm150m struct */ + sema_init(&vpm150m->sem, 1); + vpm150m->curpage = 0x80; + + for (i = 0; i < WC_MAX_IFACES; i++) { + if (ifaces[i] == wc) + vpm150m->dspid = i; + } + + printk("Checking for VPMADT032...\n"); + + if (debug & DEBUG_ECHOCAN) + printk("Setting VPMADT032 DSP ID to %d\n", vpm150m->dspid); + + spin_lock_irqsave(&wc->reglock, flags); + wc->vpm150m = vpm150m; + spin_unlock_irqrestore(&wc->reglock, flags); + + for (i = 0; i < 10; i++) + schluffen(&wc->regq); + + if (debug & DEBUG_ECHOCAN) + printk("VPMADT032 Testing page access: "); + for (i = 0; i < 0xf; i++) { + int x; + for (x = 0; x < 3; x++) { + wctdm_vpm150m_setpage(wc, i); + reg = wctdm_vpm150m_getpage(wc); + if (reg != i) { + if (debug & DEBUG_ECHOCAN) + printk("Failed: Sent %x != %x VPMADT032 Failed HI page test\n", i, reg); + goto failed_exit; + } + } + } + if (debug & DEBUG_ECHOCAN) + printk("Passed\n"); + + /* Set us up to page 0 */ + wctdm_vpm150m_setpage(wc, 0); + if (debug & DEBUG_ECHOCAN) + printk("VPMADT032 now doing address test: "); + for (i = 0; i < 16; i++) { + int x; + for (x = 0; x < 2; x++) { + wctdm_vpm150m_setreg(wc, 1, 0x1000, &i); + wctdm_vpm150m_getreg(wc, 1, 0x1000, ®); + if (reg != i) { + printk("VPMADT032 Failed address test\n"); + goto failed_exit; + } + + } + } + if (debug & DEBUG_ECHOCAN) + printk("Passed\n"); + +#if 0 + /* Load the firmware */ + set_bit(VPM150M_SPIRESET, &vpm150m->control); + + /* Wait for it to boot */ + msleep(7000); + + pingstatus = gpakPingDsp(vpm150m->dspid, &version); + + if (pingstatus || (version != 0x106)) { +#endif + fw.offset = 0; + if (request_firmware(&fw.fw, vpm150m_firmware, &wc->dev->dev)) { + printk("Unable to locate VPMADT032.bin firmware!\n"); + } + + set_bit(VPM150M_HPIRESET, &vpm150m->control); + + while (test_bit(VPM150M_HPIRESET, &vpm150m->control)) + schluffen(&wc->regq); + + printk("VPMADT032 Loading firwmare... "); + downloadstatus = gpakDownloadDsp(vpm150m->dspid, &fw); + + release_firmware(fw.fw); + + if (downloadstatus != 0) { + printk("Unable to download firmware to VPMADT032 with cause %d\n", downloadstatus); + goto failed_exit; + } else { + printk("Success\n"); + } + + set_bit(VPM150M_SWRESET, &vpm150m->control); + + while (test_bit(VPM150M_SWRESET, &vpm150m->control)) + schluffen(&wc->regq); + + msleep(700); +#if 0 + } +#endif + + pingstatus = gpakPingDsp(vpm150m->dspid, &vpm150m->version); + + if (!pingstatus) { + if (debug & DEBUG_ECHOCAN) + printk("Version of DSP is %x\n", vpm150m->version); + } else { + printk("VPMADT032 Failed! Unable to ping the DSP (%d)!\n", pingstatus); + goto failed_exit; + } + + if (vpm150m_config_hw(wc)) { + goto failed_exit; + } + + return; + +failed_exit: + spin_lock_irqsave(&wc->reglock, flags); + wc->vpm150m = NULL; + spin_unlock_irqrestore(&wc->reglock, flags); + kfree(vpm150m); + + return; +} + +static void wctdm_vpm_set_dtmf_threshold(struct wctdm *wc, unsigned int threshold) +{ + unsigned int x; + + for (x = 0; x < 4; x++) { + wctdm_vpm_out(wc, x, 0xC4, (threshold >> 8) & 0xFF); + wctdm_vpm_out(wc, x, 0xC5, (threshold & 0xFF)); + } + printk("VPM: DTMF threshold set to %d\n", threshold); +} + +static void wctdm_vpm_init(struct wctdm *wc) +{ + unsigned char reg; + unsigned int mask; + unsigned int ver; + unsigned char vpmver=0; + unsigned int i, x, y; + + if (!vpmsupport) { + printk("VPM: Support Disabled\n"); + wc->vpm = 0; + return; + } + + for (x=0;xvpm = 0; + return; + } + + if (!x) { + vpmver = wctdm_vpm_in(wc, x, 0x1a6) & 0xf; + printk("VPM Revision: %02x\n", vpmver); + } + + + /* Setup GPIO's */ + for (y=0;y<4;y++) { + wctdm_vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */ + if (y == 3) + wctdm_vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */ + else + wctdm_vpm_out(wc, x, 0x1ac + y, 0xff); /* GPIO dir */ + wctdm_vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */ + } + + /* Setup TDM path - sets fsync and tdm_clk as inputs */ + reg = wctdm_vpm_in(wc, x, 0x1a3); /* misc_con */ + wctdm_vpm_out(wc, x, 0x1a3, reg & ~2); + + /* Setup Echo length (256 taps) */ + wctdm_vpm_out(wc, x, 0x022, 0); + + /* Setup timeslots */ + if (vpmver == 0x01) { + wctdm_vpm_out(wc, x, 0x02f, 0x00); + wctdm_vpm_out(wc, x, 0x023, 0xff); + mask = 0x11111111 << x; + } else { + wctdm_vpm_out(wc, x, 0x02f, 0x20 | (x << 3)); + wctdm_vpm_out(wc, x, 0x023, 0x3f); + mask = 0x0000003f; + } + + /* Setup the tdm channel masks for all chips*/ + for (i = 0; i < 4; i++) + wctdm_vpm_out(wc, x, 0x33 - i, (mask >> (i << 3)) & 0xff); + + /* Setup convergence rate */ + reg = wctdm_vpm_in(wc,x,0x20); + reg &= 0xE0; + if (alawoverride) { + if (!x) + printk("VPM: A-law mode\n"); + reg |= 0x01; + } else { + if (!x) + printk("VPM: U-law mode\n"); + reg &= ~0x01; + } + wctdm_vpm_out(wc,x,0x20,(reg | 0x20)); + + /* Initialize echo cans */ + for (i = 0 ; i < MAX_TDM_CHAN; i++) { + if (mask & (0x00000001 << i)) + wctdm_vpm_out(wc,x,i,0x00); + } + + for (i=0;i<30;i++) + schluffen(&wc->regq); + + /* Put in bypass mode */ + for (i = 0 ; i < MAX_TDM_CHAN ; i++) { + if (mask & (0x00000001 << i)) { + wctdm_vpm_out(wc,x,i,0x01); + } + } + + /* Enable bypass */ + for (i = 0 ; i < MAX_TDM_CHAN ; i++) { + if (mask & (0x00000001 << i)) + wctdm_vpm_out(wc,x,0x78 + i,0x01); + } + + /* Enable DTMF detectors (always DTMF detect all spans) */ + for (i = 0; i < 6; i++) { + if (vpmver == 0x01) + wctdm_vpm_out(wc, x, 0x98 + i, 0x40 | (i << 2) | x); + else + wctdm_vpm_out(wc, x, 0x98 + i, 0x40 | i); + } + + for (i = 0xB8; i < 0xC0; i++) + wctdm_vpm_out(wc, x, i, 0xFF); + for (i = 0xC0; i < 0xC4; i++) + wctdm_vpm_out(wc, x, i, 0xff); + + } + /* set DTMF detection threshold */ + wctdm_vpm_set_dtmf_threshold(wc, dtmfthreshold); + + if (vpmver == 0x01) + wc->vpm = 2; + else + wc->vpm = 1; +} + +#endif + +static void wctdm_locate_modules(struct wctdm *wc) +{ + int x; + unsigned long flags; + printk("Resetting the modules...\n"); + /* Initialize control register */ + wc->ctlreg = 0x00; + /* Set Reset */ + wctdm_setctl(wc, 0x0048, 0x00000000); + for (x=0;x<10;x++) + schluffen(&wc->regq); + printk("During Resetting the modules...\n"); + /* Clear reset */ + wctdm_setctl(wc, 0x0048, 0x00010000); + for (x=0;x<10;x++) + schluffen(&wc->regq); + printk("After resetting the modules...\n"); + /* Switch to caring only about receive interrupts */ + wctdm_setintmask(wc, 0x00010040); + + /* Make sure all units go into daisy chain mode */ + spin_lock_irqsave(&wc->reglock, flags); + wc->span.irqmisses = 0; + for (x=0;xcards;x++) + wc->modtype[x] = MOD_TYPE_FXSINIT; +#ifdef VPM_SUPPORT + wc->vpm = -1; + for (x = wc->cards; x < wc->cards+NUM_EC; x++) + wc->modtype[x] = MOD_TYPE_VPM; +#endif + spin_unlock_irqrestore(&wc->reglock, flags); + /* Wait just a bit */ + for (x=0;x<10;x++) + schluffen(&wc->regq); + spin_lock_irqsave(&wc->reglock, flags); + for (x=0;xcards;x++) + wc->modtype[x] = MOD_TYPE_FXS; + spin_unlock_irqrestore(&wc->reglock, flags); + +#if 0 + /* XXX */ + cmddesc = 0; +#endif + /* Now that all the cards have been reset, we can stop checking them all if there aren't as many */ + spin_lock_irqsave(&wc->reglock, flags); + wc->cards = wc->type; + spin_unlock_irqrestore(&wc->reglock, flags); + + /* Reset modules */ + for (x=0;xcards;x++) { + int sane=0,ret=0,readi=0; +retry: + /* Init with Auto Calibration */ + if (!(ret = wctdm_init_proslic(wc, x, 0, 0, sane))) { + wc->cardflag |= (1 << x); + if (debug & DEBUG_CARD) { + readi = wctdm_getreg(wc,x,LOOP_I_LIMIT); + printk("Proslic module %d loop current is %dmA\n",x, + ((readi*3)+20)); + } + printk("Port %d: Installed -- AUTO FXS/DPO\n", x + 1); + } else { + if(ret!=-2) { + sane=1; + /* Init with Manual Calibration */ + if (!wctdm_init_proslic(wc, x, 0, 1, sane)) { + wc->cardflag |= (1 << x); + if (debug & DEBUG_CARD) { + readi = wctdm_getreg(wc,x,LOOP_I_LIMIT); + printk("Proslic module %d loop current is %dmA\n",x, + ((readi*3)+20)); + } + printk("Port %d: Installed -- MANUAL FXS\n",x + 1); + } else { + printk("Port %d: FAILED FXS (%s)\n", x + 1, fxshonormode ? fxo_modes[_opermode].name : "FCC"); + } + } else if (!(ret = wctdm_init_voicedaa(wc, x, 0, 0, sane))) { + wc->cardflag |= (1 << x); + printk("Port %d: Installed -- AUTO FXO (%s mode)\n",x + 1, fxo_modes[_opermode].name); + } else if (!wctdm_init_qrvdri(wc,x)) { + wc->cardflag |= 1 << x; + printk("Port %d: Installed -- QRV DRI card\n",x + 1); + } else { + if ((wc->type == 8) && ((x & 0x3) == 1) && !wc->altcs[x]) { + spin_lock_irqsave(&wc->reglock, flags); + wc->modtype[x] = MOD_TYPE_FXSINIT; + wc->altcs[x] = 2; + spin_unlock_irqrestore(&wc->reglock, flags); + schluffen(&wc->regq); + spin_lock_irqsave(&wc->reglock, flags); + wc->modtype[x] = MOD_TYPE_FXS; + spin_unlock_irqrestore(&wc->reglock, flags); + if (debug & DEBUG_CARD) + printk("Trying port %d with alternate chip select\n", x + 1); + goto retry; + } else { + printk("Port %d: Not installed\n", x + 1); + wc->modtype[x] = MOD_TYPE_NONE; + wc->cardflag |= (1 << x); + } + } + } + } +#ifdef VPM_SUPPORT + wctdm_vpm_init(wc); + if (wc->vpm) { + printk("VPM: Present and operational (Rev %c)\n", 'A' + wc->vpm - 1); + wc->ctlreg |= 0x10; + } else { + spin_lock_irqsave(&wc->reglock, flags); + for (x = wc->cards; x < wc->cards+NUM_EC; x++) + wc->modtype[x] = MOD_TYPE_NONE; + spin_unlock_irqrestore(&wc->reglock, flags); + wctdm_vpm150m_init(wc); + if (wc->vpm150m) { + printk("VPMADT032: Present and operational (Firmware version %x)\n", wc->vpm150m->version); + wc->ctlreg |= 0x10; + } + } +#endif +} + +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; + + if (pci_enable_device(pdev)) { + res = -EIO; + } else { + wc = kmalloc(sizeof(struct wctdm), GFP_KERNEL); + if (wc) { + spin_lock(&ifacelock); + for (x = 0; x < WC_MAX_IFACES; x++) + if (!ifaces[x]) break; + + ifaces[x] = wc; + spin_unlock(&ifacelock); + + memset(wc, 0, sizeof(struct wctdm)); + spin_lock_init(&wc->reglock); + wc->curcard = -1; + wc->cards = NUM_CARDS; + wc->iobase = pci_resource_start(pdev, 0); + wc->type = d->ports; + wc->dev = pdev; + wc->pos = x; + wc->variety = d->name; + for (y=0;yflags[y] = d->flags; + wc->dacssrc[y] = -1; + } + /* Keep track of whether we need to free the region */ + if (request_region(wc->iobase, 0xff, "wctdm24xxp")) + wc->freeregion = 1; + + /* Allocate enough memory for two zt chunks, receive and transmit. Each sample uses + 32 bits. Allocate an extra set just for control too */ + wc->writechunk = pci_alloc_consistent(pdev, PCI_WINDOW_SIZE, &wc->writedma); + if (!wc->writechunk) { + printk("wctdm: Unable to allocate DMA-able memory\n"); + if (wc->freeregion) + release_region(wc->iobase, 0xff); + return -ENOMEM; + } + + wc->readchunk = wc->writechunk + SFRAME_SIZE / 2; /* in doublewords */ + wc->readdma = wc->writedma + SFRAME_SIZE * 2; /* in bytes */ + + wc->descripchunk = wc->readchunk + SFRAME_SIZE / 2; /* in doublewords */ + wc->descripdma = wc->readdma + SFRAME_SIZE * 2; /* in bytes */ + + /* Initialize Write/Buffers to all blank data */ + memset((void *)wc->writechunk,0x00, SFRAME_SIZE * 2); + memset((void *)wc->readchunk, 0x00, SFRAME_SIZE * 2); + + init_waitqueue_head(&wc->regq); + + if (wctdm_initialize(wc)) { + printk("%s: Unable to register span with zaptel\n", wc->variety); + /* Set Reset Low */ + wctdm_stop_dma(wc); + /* Free Resources */ + free_irq(pdev->irq, wc); + if (wc->freeregion) + release_region(wc->iobase, 0xff); + pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *)wc->writechunk, wc->writedma); + kfree(wc); + return -EIO; + } + + /* 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, SA_SHIRQ, wc->variety, wc)) { + printk("wctdm24xxp: Unable to request IRQ %d\n", pdev->irq); + if (wc->freeregion) + release_region(wc->iobase, 0xff); + pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *)wc->writechunk, wc->writedma); + pci_set_drvdata(pdev, NULL); + kfree(wc); + return -EIO; + } + + + if (wctdm_hardware_init(wc)) { + /* Set Reset Low */ + wctdm_stop_dma(wc); + /* Free Resources */ + free_irq(pdev->irq, wc); + if (wc->freeregion) + release_region(wc->iobase, 0xff); + pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *)wc->writechunk, wc->writedma); + pci_set_drvdata(pdev, NULL); + zt_unregister(&wc->span); + kfree(wc); + return -EIO; + + } + + + /* Enable interrupts */ + wctdm_enable_interrupts(wc); + + /* Start DMA */ + wctdm_start_dma(wc); + + /* Now track down what modules are installed */ + wctdm_locate_modules(wc); + + /* Final initialization */ + wctdm_post_initialize(wc); + + printk("Found a Wildcard TDM: %s (%d modules)\n", wc->variety, wc->type); + res = 0; + } else + res = -ENOMEM; + } + return res; +} + +static void wctdm_release(struct wctdm *wc) +{ + int i; + + zt_unregister(&wc->span); + + if (wc->freeregion) + release_region(wc->iobase, 0xff); + + spin_lock(&ifacelock); + + for (i = 0; i < WC_MAX_IFACES; i++) + if (ifaces[i] == wc) + break; + + ifaces[i] = NULL; + + spin_unlock(&ifacelock); + + kfree(wc); + printk("Freed a Wildcard\n"); +} + +static void __devexit wctdm_remove_one(struct pci_dev *pdev) +{ + unsigned long flags; + struct wctdm *wc = pci_get_drvdata(pdev); + struct vpm150m *vpm150m = wc->vpm150m; + + if (wc) { + if (vpm150m) { + clear_bit(VPM150M_DTMFDETECT, &vpm150m->control); + clear_bit(VPM150M_ACTIVE, &vpm150m->control); + flush_workqueue(vpm150m->wq); + destroy_workqueue(vpm150m->wq); + + spin_lock_irqsave(&wc->reglock, flags); + wc->vpm150m = NULL; + vpm150m->wc = NULL; + spin_unlock_irqrestore(&wc->reglock, flags); + + kfree(wc->vpm150m); + } + /* Stop any DMA */ + wctdm_stop_dma(wc); + + /* In case hardware is still there */ + wctdm_disable_interrupts(wc); + + /* Immediately free resources */ + pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *)wc->writechunk, wc->writedma); + free_irq(pdev->irq, wc); + + /* Release span, possibly delayed */ + if (!wc->usecount) + wctdm_release(wc); + else + wc->dead = 1; + } +} + +static struct pci_device_id wctdm_pci_tbl[] = { + { 0xd161, 0x2400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wctdm2400 }, + { 0xd161, 0x0800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wctdm800 }, + { 0xd161, 0x8002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wcaex800 }, + { 0xd161, 0x8003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wcaex2400 }, + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, wctdm_pci_tbl); + +static struct pci_driver wctdm_driver = { + name: "wctdm24xxp", + probe: wctdm_init_one, +#ifdef LINUX26 + remove: __devexit_p(wctdm_remove_one), +#else + remove: wctdm_remove_one, +#endif + 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("Invalid/unknown operating mode '%s' specified. Please choose one of:\n", opermode); + for (x=0;x"); +#if defined(MODULE_ALIAS) +MODULE_ALIAS("wctdm8xxp"); +#endif +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +module_init(wctdm_init); +module_exit(wctdm_cleanup); diff --git a/wctdm24xxp/gpakErrs.h b/wctdm24xxp/gpakErrs.h new file mode 100644 index 0000000..73d3666 --- /dev/null +++ b/wctdm24xxp/gpakErrs.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2002 - 2004, Adaptive Digital Technologies, Inc. + * + * File Name: GpakErrs.h + * + * Description: + * This file contains DSP reply status codes used by G.PAK API functions to + * indicate specific errors. + * + * Version: 1.0 + * + * Revision History: + * 10/17/01 - Initial release. + * 07/03/02 - Updates for conferencing. + * 06/15/04 - Tone type updates. + * + */ + +#ifndef _GPAKERRS_H /* prevent multiple inclusion */ +#define _GPAKERRS_H + +/* Configure Serial Ports reply status codes. */ +typedef enum +{ + Pc_Success = 0, /* serial ports configured successfully */ + Pc_ChannelsActive = 1, /* unable to configure while channels active */ + Pc_TooManySlots1 = 2, /* too many slots selected for port 1 */ + Pc_InvalidBlockCombo1 = 3, /* invalid combination of blocks for port 1 */ + Pc_NoSlots1 = 4, /* no slots selected for port 1 */ + Pc_InvalidSlots1 = 5, /* invalid slot (> max) selected for port 1 */ + Pc_TooManySlots2 = 6, /* too many slots selected for port 2 */ + Pc_InvalidBlockCombo2 = 7, /* invalid combination of blocks for port 2 */ + Pc_NoSlots2 = 8, /* no slots selected for port 2 */ + Pc_InvalidSlots2 = 9, /* invalid slot (> max) selected for port 2 */ + Pc_TooManySlots3 = 10, /* too many slots selected for port 3 */ + Pc_InvalidBlockCombo3 = 11, /* invalid combination of blocks for port 3 */ + Pc_NoSlots3 = 12, /* no slots selected for port 3 */ + Pc_InvalidSlots3 = 13 /* invalid slot (> max) selected for port 3 */ +} GPAK_PortConfigStat_t; + +/* Configure Channel reply status codes. */ +typedef enum +{ + Cc_Success = 0, /* channel configured successfully */ + Cc_InvalidChannelType = 1, /* invalid Channel Type */ + Cc_InvalidChannel = 2, /* invalid Channel A Id */ + Cc_ChannelActiveA = 3, /* Channel A is currently active */ + Cc_InvalidInputPortA = 4, /* invalid Input A Port */ + Cc_InvalidInputSlotA = 5, /* invalid Input A Slot */ + Cc_BusyInputSlotA = 6, /* busy Input A Slot */ + Cc_InvalidOutputPortA = 7, /* invalid Output A Port */ + Cc_InvalidOutputSlotA = 8, /* invalid Output A Slot */ + Cc_BusyOutputSlotA = 9, /* busy Output A Slot */ + Cc_InvalidInputPortB = 10, /* invalid Input B Port */ + Cc_InvalidInputSlotB = 11, /* invalid Input B Slot */ + Cc_BusyInputSlotB = 12, /* busy Input B Slot */ + Cc_InvalidPktInCodingA = 13, /* invalid Packet In A Coding */ + Cc_InvalidPktOutCodingA = 14, /* invalid Packet Out A Coding */ + Cc_InvalidPktInSizeA = 15, /* invalid Packet In A Frame Size */ + Cc_InvalidPktOutSizeA = 16, /* invalid Packet Out A Frame Size */ + + Cc_ChanTypeNotConfigured = 21, /* channel type was not configured */ + Cc_InsuffECResources = 22, /* insufficient ecan resources avail. */ + Cc_InsuffTDMResources = 23, /* insufficient tdm block resources avail. */ + + Cc_InsuffPktBufResources = 25, /* insufficient pkt buffer resources avail. */ + Cc_InsuffPcmBufResources = 26, /* insufficient pcm buffer resources avail. */ + + Cc_BadPcmEcNlpType = 30, /* invalid EC Nlp type */ + Cc_BadPcmEcTapLength = 31, /* invalid EC tap length */ + Cc_BadPcmEcDblTalkThresh = 32, /* invalid EC double-talk threshold */ + Cc_BadPcmEcNlpThreshold = 33, /* invalid EC Nlp threshold */ + Cc_BadPcmEcCngThreshold = 34, /* invalid EC Cng threshold */ + Cc_BadPcmEcAdaptLimit = 35, /* invalid EC Adapt Limit */ + Cc_BadPcmEcCrossCorrLim = 36, /* invalid EC Cross Correlation Limit */ + Cc_BadPcmEcNumFirSegs = 37, /* invalid EC Number of FirSegments */ + Cc_BadPcmEcFirSegLen = 38, /* invalid EC Fir Segment Length */ + + /*Cc_InvalidNumEcsEnabled = 48, */ /* more than 1 Ec enabled on channel */ + Cc_InvalidFrameRate = 49, /* invalid gpak frame rate */ + Cc_InvalidSoftCompand = 50, /* invalid softCompanding type */ + + Cc_InvalidMuteToneA = 51, /* invalid MuteToneA set, no detector */ + Cc_InvalidMuteToneB = 52, /* invalid MuteToneB set, no detector */ + Cc_InsuffFaxCngDetResources = 53 /* insufficient tdm block resources avail. */ + +} GPAK_ChannelConfigStat_t; + +/* Tear Down Channel reply status codes. */ +typedef enum +{ + Td_Success = 0, /* channel torn down successfully */ + Td_InvalidChannel = 1, /* invalid Channel Id */ + Td_ChannelNotActive = 2 /* channel is not active */ +} GPAK_TearDownChanStat_t; + + +typedef enum +{ + Ac_Success = 0, /* algorithm control is successfull */ + Ac_InvalidChannel = 1, /* invalid channel identifier */ + Ac_InvalidCode = 2, /* invalid algorithm control code */ + Ac_ECNotEnabled = 3, /* echo canceller was not allocated */ + Ac_InvalidSoftComp = 4, /* invalid softcompanding, 'cause serial port not in companding mode */ + Ac_InvalidDTMFMuteA = 5, /* A side invalid Mute, since no dtmf detector */ + Ac_InvalidDTMFMuteB = 6, /* B side invalid Mute, since no dtmf detector */ + Ac_InvalidFaxCngA = 7, /* A side FAXCNG detector not available */ + Ac_InvalidFaxCngB = 8, /* B side FAXCNG detector not available */ + Ac_InvalidSysConfig = 9 /* No new system parameters (DTMF config) wrriten yet */ +} GPAK_AlgControlStat_t; + +/* Write System Parameters reply status codes. */ +typedef enum +{ + Sp_Success = 0, /* System Parameters written successfully */ + Sp_BadTwistThresh = 29 /* invalid twist threshold */ + +} GPAK_SysParmsStat_t; + +#endif /* prevent multiple inclusion */ + + + + + + + + + + + + + + + + + + + diff --git a/wctdm24xxp/gpakenum.h b/wctdm24xxp/gpakenum.h new file mode 100644 index 0000000..b278159 --- /dev/null +++ b/wctdm24xxp/gpakenum.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2005, Adaptive Digital Technologies, Inc. + * + * File Name: gpakenum.h + * + * Description: + * This file contains common enumerations related to G.PAK application + * software. + * + * Version: 1.0 + * + * Revision History: + * 06/15/05 - Initial release. + * + */ + +#ifndef _GPAKENUM_H /* prevent multiple inclusion */ +#define _GPAKENUM_H + +/* G.PAK Serial Port Word Size */ +typedef enum +{ + SerWordSize8 = 0, // 8-bit seial word + SerWordSize16 = 1 // 16-bit serial word +} GpakSerWordSize_t; + +/* G.PAK Serial Port FrameSync Polarity */ +typedef enum +{ + FrameSyncActLow = 0, // active low frame sync signal + FrameSyncActHigh = 1 // active high frame sync signal +} GpakSerFrameSyncPol_t; + +/* G.PAK Serial Port Clock Polarity */ +typedef enum +{ + SerClockActLow = 0, // active low serial clock + SerClockActHigh = 1 // active high serial clock +} GpakSerClockPol_t; + +/* G.PAK Serial Port Data Delay */ +typedef enum +{ + DataDelay0 = 0, // no data delay + DataDelay1 = 1, // 1-bit data delay + DataDelay2 = 2 // 2-bit data delay +} GpakSerDataDelay_t; + +/* G.PAK Serial Port Ids. */ +typedef enum +{ + SerialPortNull = 0, // null serial port + SerialPort1 = 1, // first PCM serial stream port (McBSP0) + SerialPort2 = 2, // second PCM serial stream port (McBSP1) + SerialPort3 = 3 // third PCM serial stream port (McBSP2) +} GpakSerialPort_t; + +/* G.PAK serial port Slot Configuration selection codes. */ +typedef enum +{ + SlotCfgNone = 0, // no time slots used + SlotCfg2Groups = 2, // 2 groups of 16 time slots used, 32 Channels system + SlotCfg8Groups = 8 // 8-partition mode for 128-channel system +} GpakSlotCfg_t; + +/* G.PAK serial port Companding Mode codes. */ +typedef enum +{ + cmpPCMU=0, // u-Law + cmpPCMA=1, // A-Law + cmpNone=2 // none +} GpakCompandModes; + +/* G.PAK Active/Inactive selection codes. */ +typedef enum +{ + Disabled=0, // Inactive + Enabled=1 // Active +} GpakActivation; + +/* G.PAK Channel Type codes. */ +typedef enum +{ + inactive=0, // channel inactive + tdmToTdm=1 // tdmToTdm +} GpakChanType; + +/* G.PAK Algorithm control commands */ +typedef enum +{ + EnableEcanA = 0, // Enable A side echo canceller + BypassEcanA = 1, // Bypass A side echo canceller + ResetEcanA = 2, // Reset A side echo canceller + EnableEcanB = 3, // Enable B side echo canceller + BypassEcanB = 4, // Bypass B side echo canceller + ResetEcanB = 5, // Reset B side echo canceller + + EnableMuLawSwCompanding = 6,// Enable Mu-law Software companding + EnableALawSwCompanding = 7, // Enable Mu-law Software companding + BypassSwCompanding = 8, // Bypass Software companding + EnableDTMFMuteA = 9, // Mute A side Dtmf digit after tone detected + DisableDTMFMuteA = 10, // Do not mute A side Dtmf digit once tone detected + EnableDTMFMuteB = 11, // Mute B side Dtmf digit after tone detected + DisableDTMFMuteB = 12, // Do not mute B side Dtmf digit once tone detected + EnableFaxCngDetectA = 13, // Enable A side Fax CNG detector, channel must be configed already + DisableFaxCngDetectA = 14, // Disable A side Fax CNG detector, channel must be configed already + EnableFaxCngDetectB = 15, // Enable B side Fax CNG detector, channel must be configed already + DisableFaxCngDetectB = 16 // Disable B side Fax CNG detector, channel must be configed already +} GpakAlgCtrl_t; + +/* G.PAK Tone types. */ +typedef enum +{ + Null_tone = 0, // no tone detection + DTMF_tone = 1 // DTMF tone +} GpakToneTypes; + +/* G.PAK direction. */ +typedef enum +{ + TDMAToB = 0, // A to B + TDMBToA = 1 // B to A +} GpakTdmDirection; + + +typedef enum +{ + rate1ms=0, + rate2ms=1, + rate10ms=2 +} GpakRate_t; + +/* G.PAK Asynchronous Event Codes */ +typedef enum +{ + EventToneDetect = 0, // Tone detection event + EventDSPDebug = 7 // DSP debug data event +} GpakAsyncEventCode_t; + +/* G.PAK MF Tone Code Indices */ +typedef enum +{ + DtmfDigit1 = 0, // DTMF Digit 1 + DtmfDigit2 = 1, // DTMF Digit 2 + DtmfDigit3 = 2, // DTMF Digit 3 + DtmfDigitA = 3, // DTMF Digit A + DtmfDigit4 = 4, // DTMF Digit 4 + DtmfDigit5 = 5, // DTMF Digit 5 + DtmfDigit6 = 6, // DTMF Digit 6 + DtmfDigitB = 7, // DTMF Digit B + DtmfDigit7 = 8, // DTMF Digit 7 + DtmfDigit8 = 9, // DTMF Digit 8 + DtmfDigit9 = 10, // DTMF Digit 9 + DtmfDigitC = 11, // DTMF Digit C + DtmfDigitSt = 12, // DTMF Digit * + DtmfDigit0 = 13, // DTMF Digit 0 + DtmfDigitPnd = 14, // DTMF Digit # + DtmfDigitD = 15, // DTMF Digit D + + FaxCngDigit = 90, // Fax Calling Tone (1100 Hz) + + EndofMFDigit = 100, // End of MF digit + EndofCngDigit = 101 // End of Cng Digit +} GpakToneCodes_t; + +/* GPIO control code*/ +typedef enum +{ + GPIO_READ = 0, + GPIO_WRITE = 1, + GPIO_DIR = 2 +} GpakGPIOCotrol_t; + + +#endif // end multiple inclusion diff --git a/wctdm24xxp/wctdm24xxp.h b/wctdm24xxp/wctdm24xxp.h new file mode 100644 index 0000000..1b32103 --- /dev/null +++ b/wctdm24xxp/wctdm24xxp.h @@ -0,0 +1,237 @@ +#ifndef _WCTDM24XXP_H +#define _WCTDM24XXP_H + +#include "../zaptel.h" +#include + +#define NUM_FXO_REGS 60 + +#define WC_MAX_IFACES 128 + +#define RING_DEBOUNCE 128 /* Ringer Debounce (in ms) */ +#define DEFAULT_BATT_DEBOUNCE 64 /* Battery debounce (in ms) */ +#define POLARITY_DEBOUNCE 64 /* Polarity debounce (in ms) */ +#define DEFAULT_BATT_THRESH 3 /* Anything under this is "no battery" */ + +#define OHT_TIMER 6000 /* How long after RING to retain OHT */ + +#define FLAG_3215 (1 << 0) + +#define EFRAME_SIZE 108 +#define ERING_SIZE 16 /* Maximum ring size */ +#define EFRAME_GAP 20 +#define SFRAME_SIZE ((EFRAME_SIZE * ZT_CHUNKSIZE) + (EFRAME_GAP * (ZT_CHUNKSIZE - 1))) + +#define MAX_ALARMS 10 + +#define MOD_TYPE_NONE 0 +#define MOD_TYPE_FXS 1 +#define MOD_TYPE_FXO 2 +#define MOD_TYPE_FXSINIT 3 +#define MOD_TYPE_VPM 4 +#define MOD_TYPE_QRV 5 +#define MOD_TYPE_VPM150M 6 + +#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 SDI_CLK (0x00010000) +#define SDI_DOUT (0x00020000) +#define SDI_DREAD (0x00040000) +#define SDI_DIN (0x00080000) + +#define PCI_WINDOW_SIZE ((2 * 2 * 2 * SFRAME_SIZE) + (2 * ERING_SIZE * 4)) + +#define __CMD_RD (1 << 20) /* Read Operation */ +#define __CMD_WR (1 << 21) /* Write Operation */ +#define __CMD_FIN (1 << 22) /* Has finished receive */ +#define __CMD_TX (1 << 23) /* Has been transmitted */ + +#define CMD_WR(a,b) (((a) << 8) | (b) | __CMD_WR) +#define CMD_RD(a) (((a) << 8) | __CMD_RD) + +#define CMD_BYTE(card,bit,altcs) (((((card) & 0x3) * 3 + (bit)) * 7) \ + + ((card) >> 2) + (altcs) + ((altcs) ? -21 : 0)) +#define NUM_CARDS 24 +#define NUM_EC 4 +#define NUM_SLOTS 6 +#define MAX_TDM_CHAN 31 + +#define NUM_CAL_REGS 12 + +#define USER_COMMANDS 8 +#define ISR_COMMANDS 2 +#define QRV_DEBOUNCETIME 20 + +#define MAX_COMMANDS (USER_COMMANDS + ISR_COMMANDS) + +#define __VPM150M_RWPAGE (1 << 4) +#define __VPM150M_RD (1 << 3) +#define __VPM150M_WR (1 << 2) +#define __VPM150M_FIN (1 << 1) +#define __VPM150M_TX (1 << 0) + +#define VPM150M_HPI_CONTROL 0x00 +#define VPM150M_HPI_ADDRESS 0x02 +#define VPM150M_HPI_DATA 0x03 + +#define VPM150M_MAX_COMMANDS 8 + +/* Some Bit ops for different operations */ +#define VPM150M_SPIRESET 0 +#define VPM150M_HPIRESET 1 +#define VPM150M_SWRESET 2 +#define VPM150M_DTMFDETECT 3 +#define VPM150M_ACTIVE 4 + +#define VPM150M_MAX_DATA 3 + +#define VPM_SUPPORT + +#ifdef VPM_SUPPORT + +/* Define to get more attention-grabbing but slightly more CPU using echocan status */ +#define FANCY_ECHOCAN + +#endif + +struct vpm150m_cmd { + unsigned int addr; + unsigned char datalen; + unsigned char desc; + unsigned char txident; + unsigned short data[VPM150M_MAX_DATA]; +}; + +struct vpm150m { + struct workqueue_struct *wq; + struct work_struct work; + struct wctdm *wc; + + int dspid; + struct semaphore sem; + unsigned long control; + unsigned char curpage; + unsigned short version; + unsigned long curecstate; + unsigned long desiredecstate; + unsigned long curdtmfmutestate; + unsigned long desireddtmfmutestate; + struct vpm150m_cmd cmdq[MAX_COMMANDS]; + unsigned char curtone[24]; +}; + +struct calregs { + unsigned char vals[NUM_CAL_REGS]; +}; + +struct cmdq { + unsigned int cmds[MAX_COMMANDS]; + unsigned char isrshadow[ISR_COMMANDS]; +}; + +struct wctdm { + struct pci_dev *dev; + char *variety; + struct zt_span span; + unsigned char ios; + unsigned int sdi; + int usecount; + unsigned int intcount; + unsigned int rxints; + unsigned int txints; + unsigned int intmask; + unsigned char txident; + unsigned char rxident; + int dead; + int pos; + int flags[NUM_CARDS]; + int freeregion; + int alt; + int rdbl; + int tdbl; + int curcard; + unsigned char ctlreg; + int cards; + int cardflag; /* Bit-map of present cards */ + int altcs[NUM_CARDS + NUM_EC]; + char qrvhook[NUM_CARDS]; + unsigned short qrvdebtime[NUM_CARDS]; + int radmode[NUM_CARDS]; +#define RADMODE_INVERTCOR 1 +#define RADMODE_IGNORECOR 2 +#define RADMODE_EXTTONE 4 +#define RADMODE_EXTINVERT 8 +#define RADMODE_IGNORECT 16 +#define RADMODE_PREEMP 32 +#define RADMODE_DEEMP 64 + unsigned short debouncetime[NUM_CARDS]; + signed short rxgain[NUM_CARDS]; + signed short txgain[NUM_CARDS]; + spinlock_t reglock; + wait_queue_head_t regq; + /* FXO Stuff */ + union { + struct { + int wasringing; + int ringdebounce; + int offhook; + int battdebounce; + int nobatttimer; + int battery; + int lastpol; + int polarity; + int polaritydebounce; + } fxo; + struct { + int oldrxhook; + int debouncehook; + int lastrxhook; + int debounce; + int ohttimer; + int idletxhookstate; /* IDLE changing hook state */ + int lasttxhook; + int palarms; + struct calregs calregs; + } fxs; + } mods[NUM_CARDS]; + struct cmdq cmdq[NUM_CARDS + NUM_EC]; + /* Receive hook state and debouncing */ + int modtype[NUM_CARDS + NUM_EC]; + /* Set hook */ + int sethook[NUM_CARDS + NUM_EC]; + int dacssrc[NUM_CARDS]; + int type; + +#ifdef VPM_SUPPORT + int vpm; + unsigned long dtmfactive; + unsigned long dtmfmask; + unsigned long dtmfmutemask; + short dtmfenergy[NUM_CARDS]; + short dtmfdigit[NUM_CARDS]; + + struct vpm150m *vpm150m; +#ifdef FANCY_ECHOCAN + int echocanpos; + int blinktimer; +#endif +#endif + unsigned long iobase; + dma_addr_t readdma; + dma_addr_t writedma; + dma_addr_t descripdma; + volatile unsigned int *writechunk; /* Double-word aligned write memory */ + volatile unsigned int *readchunk; /* Double-word aligned read memory */ + volatile unsigned int *descripchunk; /* Descriptors */ + struct zt_chan chans[NUM_CARDS]; +}; + + +int schluffen(wait_queue_head_t *q); + +extern spinlock_t ifacelock; +extern struct wctdm *ifaces[WC_MAX_IFACES]; + +#endif -- cgit v1.2.3