diff options
Diffstat (limited to 'dahdi-linux-2.10.1-openvox-2.patch')
-rw-r--r-- | dahdi-linux-2.10.1-openvox-2.patch | 5851 |
1 files changed, 5851 insertions, 0 deletions
diff --git a/dahdi-linux-2.10.1-openvox-2.patch b/dahdi-linux-2.10.1-openvox-2.patch new file mode 100644 index 000000000000..3be86e27c9a4 --- /dev/null +++ b/dahdi-linux-2.10.1-openvox-2.patch @@ -0,0 +1,5851 @@ +--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/base.h 1970-01-01 01:00:00.000000000 +0100 ++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/base.h 2015-02-10 14:19:03.000000000 +0100 +@@ -0,0 +1,321 @@ ++/* ++ * OpenVox A24xx FXS/FXO Interface Driver for Zapata Telephony interface ++ * ++ * Written by MiaoLin<miaolin@openvox.cn> ++ * $Id: base.h 360 2011-04-06 06:11:45Z yangshugang $ ++ ++ * Copyright (C) 2005-2010 OpenVox Communication Co. Ltd, ++ * ++ * 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. ++ * ++ */ ++ ++ ++#ifndef _OPVXA2410P_H ++#define _OPVXA2410P_H ++ ++#include <dahdi/kernel.h> ++#include <linux/firmware.h> ++ ++#ifndef SLIC_LF_OPEN ++/* Proslic Linefeed options for register 64 - Linefeed Control */ ++#define SLIC_LF_OPEN 0x0 ++#define SLIC_LF_ACTIVE_FWD 0x1 ++#define SLIC_LF_OHTRAN_FWD 0x2 /* Forward On Hook Transfer */ ++#define SLIC_LF_TIP_OPEN 0x3 ++#define SLIC_LF_RINGING 0x4 ++#define SLIC_LF_ACTIVE_REV 0x5 ++#define SLIC_LF_OHTRAN_REV 0x6 /* Reverse On Hook Transfer */ ++#define SLIC_LF_RING_OPEN 0x7 ++ ++#define SLIC_LF_SETMASK 0x7 ++#define SLIC_LF_OPPENDING 0x10 ++ ++/* Mask used to reverse the linefeed mode between forward and ++ * reverse polarity. */ ++#define SLIC_LF_REVMASK 0x4 ++#endif ++ ++struct ec; ++ ++#define VPM_SUPPORT ++ ++#define CARDS_PER_MODULE 4 ++#define MOD_TYPE_FXS 0 ++#define MOD_TYPE_FXO 1 ++ ++#define NUM_FXO_REGS 60 ++#define WC_MAX_IFACES 128 ++#define DEFAULT_RING_DEBOUNCE 64 /* Ringer Debounce (64 ms) */ ++ ++/* the constants below control the 'debounce' periods enforced by the ++ check_hook routines; these routines are called once every 4 interrupts ++ (the interrupt cycles around the four modules), so the periods are ++ specified in _4 millisecond_ increments ++*/ ++#define DEFAULT_BATT_DEBOUNCE 4 /* Battery debounce (64 ms) */ ++#define POLARITY_DEBOUNCE 64 /* Polarity debounce (64 ms) */ ++#define DEFAULT_BATT_THRESH 3 /* Anything under this is "no battery" */ ++ ++#define OHT_TIMER 6000 /* How long after RING to retain OHT */ ++ ++#define NEON_MWI_RNGY_PULSEWIDTH 0x3e8 /*=> period of 250 mS */ ++ ++#define FLAG_3215 (1 << 0) ++ ++#define MAX_NUM_CARDS 24 ++ ++enum cid_hook_state { ++ CID_STATE_IDLE = 0, ++ CID_STATE_RING_DELAY, ++ CID_STATE_RING_ON, ++ CID_STATE_RING_OFF, ++ CID_STATE_WAIT_RING_FINISH ++}; ++ ++/* if you want to record the last 8 sec voice before the driver unload, uncomment it and rebuild. */ ++/* #define TEST_LOG_INCOME_VOICE */ ++#define voc_buffer_size (8000*8) ++#define MAX_ALARMS 10 ++#define MINPEGTIME 10 * 8 /* 30 ms peak to peak gets us no more than 100 Hz */ ++#define PEGTIME 50 * 8 /* 50ms peak to peak gets us rings of 10 Hz or more */ ++#define PEGCOUNT 5 /* 5 cycles of pegging means RING */ ++ ++#define NUM_CAL_REGS 12 ++ ++ ++#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) ++ ++ ++struct calregs { ++ unsigned char vals[NUM_CAL_REGS]; ++}; ++ ++enum proslic_power_warn { ++ PROSLIC_POWER_UNKNOWN = 0, ++ PROSLIC_POWER_ON, ++ PROSLIC_POWER_WARNED, ++}; ++ ++enum battery_state { ++ BATTERY_UNKNOWN = 0, ++ BATTERY_PRESENT, ++ BATTERY_LOST, ++}; ++ ++struct a24xx_dev { ++ struct pci_dev *dev; ++ char *variety; ++ struct dahdi_device *ddev; ++ unsigned char ios; ++ int usecount; ++ unsigned int intcount; ++ int dead; ++ int pos; ++ int flags[MAX_NUM_CARDS]; ++ int freeregion; ++ int alt; ++ int curcard; ++ int cardflag; /* Bit-map of present cards */ ++ enum proslic_power_warn proslic_power; ++ spinlock_t lock; ++ ++ union { ++ struct fxo { ++#ifdef AUDIO_RINGCHECK ++ unsigned int pegtimer; ++ int pegcount; ++ int peg; ++ int ring; ++#else ++ int wasringing; ++ int lastrdtx; ++#endif ++ int ringdebounce; ++ int offhook; ++ unsigned int battdebounce; ++ unsigned int battalarm; ++ enum battery_state battery; ++ int lastpol; ++ int polarity; ++ int polaritydebounce; ++ } fxo; ++ struct fxs { ++ int oldrxhook; ++ int debouncehook; ++ int lastrxhook; ++ int debounce; ++ int ohttimer; ++ int idletxhookstate; /* IDLE changing hook state */ ++ int lasttxhook; ++ int palarms; ++ struct calregs calregs; ++ struct dahdi_vmwi_info vmwisetting; ++ int vmwi_active_messages; ++ u32 vmwi_lrev:1; /*MWI Line Reversal*/ ++ u32 vmwi_hvdc:1; /*MWI High Voltage DC Idle line*/ ++ u32 vmwi_hvac:1; /*MWI Neon High Voltage AC Idle line*/ ++ u32 neonringing:1;/*Ring Generator is set for NEON*/ ++ int reversepolarity; /* polarity reversal */ ++ spinlock_t lasttxhooklock; ++ } fxs; ++ } mod[MAX_NUM_CARDS]; ++ ++ /* Receive hook state and debouncing */ ++ int modtype[MAX_NUM_CARDS]; ++ unsigned char reg0shadow[MAX_NUM_CARDS]; ++ unsigned char reg1shadow[MAX_NUM_CARDS]; ++ ++ unsigned long mem_region; /* 32 bit Region allocated to tiger320 */ ++ unsigned long mem_len; /* Length of 32 bit region */ ++ volatile unsigned long mem32; /* Virtual representation of 32 bit memory area */ ++ ++ dma_addr_t readdma; ++ dma_addr_t writedma; ++ volatile unsigned char *writechunk; /* Double-word aligned write memory */ ++ volatile unsigned char *readchunk; /* Double-word aligned read memory */ ++ ++#ifdef TEST_LOG_INCOME_VOICE ++ char * voc_buf[MAX_NUM_CARDS]; ++ int voc_ptr[MAX_NUM_CARDS]; ++#endif ++ unsigned short ledstate; ++ unsigned int fwversion; ++ int max_cards; ++ char *card_name; ++ unsigned int master; ++ ++ char *cid_history_buf[MAX_NUM_CARDS]; ++ int cid_history_ptr[MAX_NUM_CARDS]; ++ int cid_history_clone_cnt[MAX_NUM_CARDS]; ++ enum cid_hook_state cid_state[MAX_NUM_CARDS]; ++ int cid_ring_on_time[MAX_NUM_CARDS]; ++ ++#ifdef VPM_SUPPORT ++ struct ec *vpm_ec; ++ int vpm; ++ ++ unsigned long dtmfactive; ++ unsigned long dtmfmask; ++ unsigned long dtmfmutemask; ++ ++#endif ++ ++}; ++ ++struct a24xx_desc { ++ char *name; ++ int flags; ++}; ++ ++struct a24xx { ++ struct a24xx_dev dev; ++ struct dahdi_span span; ++ struct dahdi_chan _chans[MAX_NUM_CARDS]; ++ struct dahdi_chan *chans[MAX_NUM_CARDS]; ++ struct dahdi_echocan_state *ec[32]; /* Echcan state for each channel */ ++ unsigned int index; ++}; ++ ++/* ++ * from bin ++ */ ++extern void __opvx_a24xx_setcreg(unsigned long mem32, unsigned int offset, unsigned int reg, unsigned int val); ++extern unsigned int __opvx_a24xx_getcreg(unsigned long mem32, unsigned int offset, unsigned char reg); ++extern unsigned char __opvx_a24xx_read_8bits(unsigned long mem32); ++ ++extern void __opvx_a24xx_reset_modules(unsigned long mem32, void (*func)(int), int data); ++extern void __opvx_a24xx_reset_modules_v2(unsigned long mem32, void (*func)(int), int data); ++extern void __opvx_a24xx_setcard(unsigned long mem32, int card); ++extern void __opvx_a24xx_reset_spi(void *wc_dev, int card, void (*func)(void*, int)); ++extern void __opvx_a24xx_write_8bits(unsigned long mem32, unsigned char bits); ++ ++extern void __opvx_a24xx_set_master(unsigned long mem32,unsigned int master); ++extern unsigned int __opvx_a24xx_get_master(unsigned long mem32); ++extern void __opvx_a24xx_set_irq_frq(unsigned long mem32,unsigned int frq); ++extern unsigned int __opvx_a24xx_get_version(unsigned long mem32); ++extern unsigned int __opvx_a24xx_get_irqstatus(unsigned long mem32); ++extern void __opvx_a24xx_set_irqstatus(unsigned long mem32, unsigned int value); ++extern void __opvx_a24xx_clear_irqs(unsigned long mem32); ++extern void __opvx_a24xx_enable_interrupts(unsigned long mem32); ++extern void __opvx_a24xx_disable_interrupts(unsigned long mem32); ++extern unsigned int __opvx_a24xx_get_irqcnt_lo(unsigned long mem32); ++ ++extern void __opvx_a24xx_restart_dma(unsigned long mem32); ++extern void __opvx_a24xx_start_dma(unsigned long mem32, unsigned int data); ++extern void __opvx_a24xx_stop_dma(unsigned long mem32); ++extern void __opvx_a24xx_reset_tdm(unsigned long mem32); ++ ++extern void __opvx_a24xx_spi_setreg(void *wc_dev, unsigned long mem32, int card, int modtype, unsigned char reg, unsigned char value, void (*func)(void*, int)); ++extern unsigned char __opvx_a24xx_spi_getreg(void *wc_dev, unsigned long mem32, int card, int modtype, unsigned char reg, void (*func)(void*, int)); ++ ++extern unsigned int __opvx_a24xx_oct_in(unsigned long mem32, unsigned int addr); ++extern unsigned int __opvx_a24xx_oct_in_v2(unsigned long mem32, unsigned int addr); ++extern void __opvx_a24xx_oct_out(unsigned long mem32, unsigned int addr, unsigned int value); ++extern void __opvx_a24xx_oct_out_v2(unsigned long mem32, unsigned int addr, unsigned int value); ++extern int __opvx_a24xx_check_vpm(unsigned long mem32); ++extern int __opvx_a24xx_check_vpm_v2(unsigned long mem32); ++extern void __opvx_a24xx_vpm_setpresent(unsigned long mem32); ++extern void __opvx_a24xx_vpm_setpresent_v2(unsigned long mem32); ++ ++extern void __opvx_a24xx_transmit(unsigned long mem32, volatile unsigned char *writechunk, volatile unsigned char **txbuf,unsigned int irq_frq , unsigned int order); ++extern void __opvx_a24xx_receive(unsigned long mem32, volatile unsigned char *readchunk, volatile unsigned char **rxbuf,unsigned int irq_frq , unsigned int order); ++ ++extern void __opvx_a24xx_set_chunk(void *readchunk, void *writechunk,unsigned int frq); ++ ++/* ++ * from a24xx.c ++ */ ++extern void __a24xx_wait_just_a_bit(int foo); ++extern void __a24xx_spi_setreg(struct a24xx_dev *wc_dev, int card, unsigned char reg, unsigned char value); ++extern unsigned char __a24xx_spi_getreg(struct a24xx_dev *wc_dev, int card, unsigned char reg); ++extern void a24xx_spi_setreg(struct a24xx_dev *wc_dev, int card, unsigned char reg, unsigned char value); ++extern unsigned char a24xx_spi_getreg(struct a24xx_dev *wc_dev, int card, unsigned char reg); ++extern void a24xx_reset_spi(struct a24xx_dev *wc_dev, int card); ++extern void __a24xx_setcard(void *wc_dev, int card); ++extern void oct_set_reg(void *data, unsigned int reg, unsigned int val); ++extern unsigned int oct_get_reg(void *data, unsigned int reg); ++extern void __a24xx_vpm_setpresent(struct a24xx_dev *wc_dev); ++extern int __a24xx_proslic_setreg_indirect(struct a24xx_dev *wc_dev, int card, unsigned char address, unsigned short data); ++extern int __a24xx_proslic_getreg_indirect(struct a24xx_dev *wc_dev, int card, unsigned char address); ++extern int __a24xx_malloc_chunk(struct a24xx_dev *wc_dev,unsigned int frq); ++ ++/* ++ * from si321x.c ++ */ ++extern int si321x_init_ring_generator_mode(struct a24xx_dev *wc_dev, int card); ++extern int si321x_set_ring_generator_mode(struct a24xx_dev *wc_dev, int card, int mode); ++extern int si321x_init_proslic(struct a24xx_dev *wc_dev, int card, int fast, int manual, int sane); ++extern int si321x_init_proslic_all(struct a24xx_dev *wc_dev, int fxs_flag,int fast, int manual, int sane,int *blk_flag); ++extern int si321x_proslic_setreg_indirect(struct a24xx_dev *wc_dev, int card, unsigned char address, unsigned short data); ++extern int si321x_proslic_getreg_indirect(struct a24xx_dev *wc_dev, int card, unsigned char address); ++extern void si321x_proslic_recheck_sanity(struct a24xx_dev *wc_dev, int card); ++ ++/* ++ * from si3050.c ++ */ ++int si3050_set_hwgain(struct a24xx_dev *wc_dev, int card, __s32 gain, __u32 tx); ++int si3050_init_voicedaa(struct a24xx_dev *wc_dev, int card, int fast, int manual, int sane); ++ ++#endif ++ +--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/busydetect.c 1970-01-01 01:00:00.000000000 +0100 ++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/busydetect.c 2015-02-10 14:19:03.000000000 +0100 +@@ -0,0 +1,876 @@ ++/* ++ * OpenVox FXO Detect Busy Voice Driver for DAHDI Telephony interface ++ * ++ * Written by kevin.chen ++ ++ * Copyright (C) 2012-2013 OpenVox Communication Co. Ltd, ++ * ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++/* Rev history ++ * ++ * Rev 0.10 remove structure country_busytone ++ * add GEN_BUSYTONE_ONTIME and GEN_BUSYTONE_OFFTIME ++ * Rev 0.20 fix bug ++ * after frequency is set to a valid value, cannot reset the zero value ++ * Rev 0.30 support new kernel version 3.10.0 ++ * ++ */ ++ ++#include <linux/proc_fs.h> ++#include "busydetect.h" ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) ++#include <linux/seq_file.h> ++#endif ++ ++static const char *module_name = "opvxdsp"; ++static int ref_count = 0; ++ ++#define MAX_TONE_NUM 3 ++#define TONE_FREQ_NUM 2 ++ ++#define GEN_BUSYTONE_ONTIME 500 ++#define GEN_BUSYTONE_OFFTIME 500 ++ ++#define TONE_THRESHOLD 20548 ++#define SILENT_THRESHOLD 2560 ++#define ENERGY_SCALE (FRAME_SHORT_SIZE >> 1) ++ ++struct freq_state { ++ int freq; ++ /* Calculate the sampling data*/ ++ short prev2; ++ short prev; ++ short fac; ++}; ++ ++struct tone { ++ int busycount; ++ int threshold; ++ int ontime; ++ int offtime; ++ struct freq_state fs[TONE_FREQ_NUM]; ++ struct proc_dir_entry *subentry; ++}; ++ ++struct silent_detect { ++ int detect_tx; ++ int detect_rx; ++ /* Mute timeout */ ++ int length; ++ int threshold; ++ struct proc_dir_entry *subentry; ++ ++ /* Statistics mute sample */ ++ u32 tx_samples; ++ u32 rx_samples; ++}; ++ ++struct param { ++ struct tone tone[MAX_TONE_NUM]; ++ struct silent_detect sd; ++}; ++ ++struct detect_state { ++ int index; ++ u32 length; ++}; ++ ++struct dsp_info { ++ /* The read data cache */ ++ short rdata[FRAME_SHORT_SIZE]; ++ int rlen; ++ /* The write data cache */ ++ short wdata[FRAME_SHORT_SIZE]; ++ int wlen; ++ ++ struct param param; ++ struct proc_dir_entry *entry; ++ ++ u32 energy; ++ int count; ++ int detected_tone; ++ struct detect_state state[3]; ++ ++ /* The busy tone appear */ ++ int appear_busy; ++ ++ /* Calculate parameters for generate waveform */ ++ /* Read and write two direction */ ++ int phase[2][TONE_FREQ_NUM]; ++ int is_offtime[2]; ++ int offset[2]; ++}; ++ ++struct detect_info { ++ struct dsp_info dsp[TOTAL_CARDS]; ++ ++ /* Variable for generater waveform */ ++ u32 phase_rate[TONE_FREQ_NUM]; ++ short gain[TONE_FREQ_NUM]; ++ int ontime; ++ int offtime; ++}; ++ ++static struct detect_info *di = NULL; ++ ++static DECLARE_BITMAP(fxo_cardflag, TOTAL_CARDS); ++static DECLARE_BITMAP(fxs_cardflag, TOTAL_CARDS); ++ ++static struct proc_dir_entry *opvxdsp_entry; ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Kevin.chen"); ++MODULE_DESCRIPTION("DAHDI detect busy voice"); ++ ++static int detect_is_close(int channo) ++{ ++ int i; ++ struct param *param = &di->dsp[channo - 1].param; ++ ++ for (i = 0; i < MAX_TONE_NUM; i++) { ++ if (param->tone[i].busycount > 0) { ++ return 0; ++ } ++ } ++ ++ if (param->sd.length > 0 && (param->sd.detect_tx || param->sd.detect_rx)) { ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static void reset_dsp_detect_param(struct dsp_info *dsp) ++{ ++ int i; ++ ++ dsp->param.sd.tx_samples = 0; ++ dsp->param.sd.rx_samples = 0; ++ ++ dsp->detected_tone = -1; ++ dsp->count = 0; ++ for (i = 0; i < 3; i++) { ++ dsp->state[i].index = -1; ++ dsp->state[i].length = 0; ++ } ++} ++ ++static void reset_dsp_generate_param(struct dsp_info *dsp) ++{ ++ int i, j; ++ ++ if (dsp->appear_busy) { ++ dsp->appear_busy = 0; ++ ++ for (i = 0; i < 2; i++) { ++ dsp->is_offtime[i] = 0; ++ dsp->offset[i] = 0; ++ for (j = 0; j < TONE_FREQ_NUM; j++) { ++ dsp->phase[i][j] = 0; ++ } ++ } ++ } ++} ++ ++static void update_detect(struct dsp_info *dsp, short s) ++{ ++ int i, j; ++ short tmp; ++ struct freq_state *fs; ++ ++ for (i = 0; i < MAX_TONE_NUM; i++) { ++ if (dsp->param.tone[i].busycount <= 0) { ++ continue; ++ } ++ ++ for (j = 0; j < TONE_FREQ_NUM; j++) { ++ fs = &dsp->param.tone[i].fs[j]; ++ tmp = fs->prev2; ++ fs->prev2 = fs->prev; ++ fs->prev = (((int)fs->fac * fs->prev2) >> 14) - tmp + (s >> 7); ++ } ++ } ++} ++ ++static u32 detect_result(struct freq_state *fs) ++{ ++ u32 val; ++ ++ val = fs->prev * fs->prev + fs->prev2 * fs->prev2 - ((fs->fac * fs->prev) >> 14) * fs->prev2; ++ /* Reset */ ++ fs->prev = fs->prev2 = 0; ++ ++ return val; ++} ++ ++static int busy_detect(struct dsp_info *dsp, int len) ++{ ++ int i, j; ++ u32 power, max_power = 0; ++ int index = -1; ++ ++ for (i = 0; i < MAX_TONE_NUM; i++) { ++ power = 0; ++ for (j = 0; j < TONE_FREQ_NUM; j++) { ++ power += detect_result(&dsp->param.tone[i].fs[j]); ++ } ++ ++ if (dsp->param.tone[i].busycount > 0 && ++ dsp->energy > dsp->param.tone[i].threshold && ++ power > ENERGY_SCALE * dsp->energy) { ++ if (power > max_power) { ++ max_power = power; ++ index = i; ++ } ++ } ++ } ++ ++ if (index != -1 && dsp->detected_tone != index) { ++ dsp->detected_tone = index; ++ dsp->count = 0; ++ for (i = 0; i < 3; i++) { ++ dsp->state[i].index = -1; ++ dsp->state[i].length = 0; ++ } ++ } ++ ++ if (dsp->state[2].index != index) { ++ dsp->state[2].index = index; ++ dsp->state[1].length += len; ++ } else { ++ if (dsp->state[1].index != index) { ++ if (dsp->detected_tone >= 0) { ++ if (dsp->state[0].index == dsp->detected_tone && dsp->state[1].index == -1) { ++ if ((dsp->state[0].length >= dsp->param.tone[dsp->detected_tone].ontime * 8 - FRAME_SHORT_SIZE * 2) && \ ++ (dsp->state[0].length <= dsp->param.tone[dsp->detected_tone].ontime * 8 + FRAME_SHORT_SIZE * 2) && \ ++ (dsp->state[1].length >= dsp->param.tone[dsp->detected_tone].offtime * 8 - FRAME_SHORT_SIZE * 2)) { ++ dsp->count++; ++ if (dsp->count >= dsp->param.tone[dsp->detected_tone].busycount) { ++ return 1; ++ } ++ } ++ } ++ } ++ memmove(&dsp->state[0], &dsp->state[1], 2 * sizeof(dsp->state[0])); ++ dsp->state[1].index = index; ++ dsp->state[1].length = len; ++ } else { ++ dsp->state[1].length += len; ++ } ++ } ++ ++ return 0; ++} ++ ++static int silent_detect(struct dsp_info *dsp, int len, int is_write) ++{ ++ int res = 0; ++ ++ if (dsp->param.sd.length <= 0) { ++ return res; ++ } ++ ++ if (dsp->energy < dsp->param.sd.threshold) { ++ if (is_write) { ++ dsp->param.sd.tx_samples += len; ++ if (dsp->param.sd.tx_samples >= dsp->param.sd.length * SAMPLE_PER_SEC) { ++ res = 1; ++ } ++ } else { ++ dsp->param.sd.rx_samples += len; ++ if (dsp->param.sd.rx_samples >= dsp->param.sd.length * SAMPLE_PER_SEC) { ++ res = 1; ++ } ++ } ++ } else { ++ dsp->param.sd.tx_samples = 0; ++ dsp->param.sd.rx_samples = 0; ++ } ++ ++ return res; ++} ++ ++static short calc_amp(u32 *acc, u32 rate, short scale) ++{ ++ u32 phase, step; ++ short amp; ++ ++ phase = *acc; ++ phase >>= 23; ++ step = phase & (SIN_DIVISION - 1); ++ if ((phase & SIN_DIVISION)) { ++ step = SIN_DIVISION - step; ++ } ++ ++ amp = sin_table[step]; ++ if ((phase & (2 * SIN_DIVISION))) { ++ amp = -amp; ++ } ++ ++ *acc += rate; ++ return (short)(((u32)amp * scale) >> 15); ++} ++ ++static short generater_amp(struct dsp_info *dsp, int is_write) ++{ ++ int i; ++ short amp = 0; ++ int index = (is_write == 0) ? 0 : 1; ++ ++ dsp->offset[index]++; ++ if (!dsp->is_offtime[index]) { ++ for (i = 0; i < TONE_FREQ_NUM; i++) { ++ if (di->phase_rate[i] > 0) { ++ amp += calc_amp(&dsp->phase[index][i], di->phase_rate[i], di->gain[i]); ++ } ++ } ++ if (dsp->offset[index] >= di->ontime * 8) { ++ dsp->offset[index] = 0; ++ dsp->is_offtime[index] = 1; ++ } ++ } else { ++ if (dsp->offset[index] >= di->offtime * 8) { ++ dsp->offset[index] = 0; ++ dsp->is_offtime[index] = 0; ++ } ++ } ++ ++ return amp; ++} ++ ++static void analysis_dsp(struct dsp_info *dsp, short s[], int len, int is_write) ++{ ++ int i, res = 0; ++ u16 temp; ++ ++ for (i = 0; i < len; i++) { ++ temp = (s[i] < 0 ? -s[i] : s[i]) >> 7; ++ dsp->energy += temp * temp; ++ if (!is_write) { ++ update_detect(dsp, s[i]); ++ } ++ } ++ ++ if (is_write) { ++ ++ if (dsp->param.sd.detect_tx) { ++ res = silent_detect(dsp, len, 1); ++ } ++ } else { ++ res = busy_detect(dsp, len); ++ ++ /* Read only support the silent detect */ ++ if (!res && dsp->param.sd.detect_rx) { ++ res = silent_detect(dsp, len, 0); ++ } ++ } ++ ++ dsp->energy = 0; ++ ++ if (res) { ++ dsp->appear_busy = 1; ++ reset_dsp_detect_param(dsp); ++ } ++} ++ ++void parser_busy_silent_process(struct a24xx *wc, int is_write) ++{ ++ int i, j; ++ struct a24xx_dev *wc_dev = &wc->dev; ++ struct dahdi_chan *chan; ++ struct dsp_info *dsp; ++ ++ if (!di) { ++ /* Initialize this module failed */ ++ return; ++ } ++ ++ for (i = 0; i < wc_dev->max_cards; i++) { ++ chan = wc->chans[i]; ++ dsp = &di->dsp[chan->channo - 1]; ++ if (chan->channo > TOTAL_CARDS) { ++ continue; ++ } ++ if ((wc_dev->modtype[i] != MOD_TYPE_FXO) || ++ (wc_dev->modtype[i] == MOD_TYPE_FXO && !wc_dev->mod[i].fxo.offhook)) { ++ reset_dsp_generate_param(dsp); ++ continue; ++ } ++ ++ if (detect_is_close(chan->channo)) { ++ /* The busy tone and silence detection has been closed */ ++ continue; ++ } ++ ++ if (is_write) { ++ if (dsp->appear_busy) { ++ for (j = 0; j < DAHDI_CHUNKSIZE; j++) { ++ chan->writechunk[j] = DAHDI_LIN2X(generater_amp(dsp, 1), chan); ++ } ++ } else { ++ for (j = 0; j < DAHDI_CHUNKSIZE; j++) { ++ dsp->wdata[dsp->wlen++] = DAHDI_XLAW(chan->writechunk[j], chan); ++ } ++ if (dsp->wlen == FRAME_SHORT_SIZE) { ++ dsp->wlen = 0; ++ analysis_dsp(dsp, dsp->wdata, FRAME_SHORT_SIZE, 1); ++ } ++ } ++ } else { ++ if (dsp->appear_busy) { ++ for (j = 0; j < DAHDI_CHUNKSIZE; j++) { ++ chan->readchunk[j] = DAHDI_LIN2X(generater_amp(dsp, 0), chan); ++ } ++ } else { ++ for (j = 0; j < DAHDI_CHUNKSIZE; j++) { ++ dsp->rdata[dsp->rlen++] = DAHDI_XLAW(chan->readchunk[j], chan); ++ } ++ if (dsp->rlen == FRAME_SHORT_SIZE) { ++ dsp->rlen = 0; ++ analysis_dsp(dsp, dsp->rdata, FRAME_SHORT_SIZE, 0); ++ } ++ } ++ } ++ } ++} ++ ++static short get_fac(int freq) ++{ ++ if (freq < MIN_INDEX_FREQ) { ++ freq = MIN_INDEX_FREQ; ++ } else if (freq > MAX_INDEX_FREQ) { ++ freq = MAX_INDEX_FREQ; ++ } ++ ++ return fac_table[(freq - MIN_INDEX_FREQ) / STEP_FREQ]; ++} ++ ++static u32 get_phase_rate(int freq) ++{ ++ return (freq * 65536 / SAMPLE_PER_SEC) * 65536; ++} ++ ++static short get_gain(int level) ++{ ++ if (level < MIN_INDEX_LEVEL) { ++ level = MIN_INDEX_LEVEL; ++ } else if (level > MAX_INDEX_LEVEL) { ++ level = MAX_INDEX_LEVEL; ++ } ++ ++ return gain_table[(level - MIN_INDEX_LEVEL) / STEP_LEVEL]; ++} ++ ++static void config_proc_param(struct param *param) ++{ ++ int i, j; ++ ++ for (i = 0; i < MAX_TONE_NUM; i++) { ++ param->tone[i].busycount = 0; ++ param->tone[i].threshold = TONE_THRESHOLD; ++ param->tone[i].ontime = 0; ++ param->tone[i].offtime = 0; ++ for (j = 0; j < TONE_FREQ_NUM; j++) { ++ param->tone[i].fs[j].freq = 0; ++ param->tone[i].fs[j].fac = 0; ++ } ++ } ++ param->sd.detect_tx = 0; ++ param->sd.detect_rx = 0; ++ param->sd.length = 30; ++ param->sd.threshold = SILENT_THRESHOLD; ++} ++ ++static void init_detect_info(struct detect_info *di, const char *opermode) ++{ ++ int i, j; ++ ++ di->ontime = GEN_BUSYTONE_ONTIME; ++ di->offtime = GEN_BUSYTONE_OFFTIME; ++ di->phase_rate[0] = get_phase_rate(450); ++ di->gain[0] = get_gain(-12); ++ ++ /* Set default parameters */ ++ for (i = 0; i < TOTAL_CARDS; i++) { ++ config_proc_param(&di->dsp[i].param); ++ ++ di->dsp[i].detected_tone = -1; ++ for (j = 0; j < 3; j++) { ++ di->dsp[i].state[j].index = -1; ++ } ++ } ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) ++static int write_param_proc(struct file *filp, const char __user *buf, ++ unsigned long count, void *data) ++#else ++static ssize_t write_param_proc(struct file *file, const char __user *buf, ++ size_t count, loff_t *pos) ++#endif ++{ ++ char temp[24]; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) ++ int *param = (int *)data; ++#else ++ int *param = PDE_DATA(file_inode(file)); ++#endif ++ int value; ++ int len; ++ ++ len = count > (sizeof(temp) - 1) ? (sizeof(temp) - 1) : count; ++ ++ if (copy_from_user(temp, buf, len)) { ++ return -EFAULT; ++ } ++ ++ temp[len] = '\0'; ++ ++ value = simple_strtoul(temp, NULL, 10); ++ if (value >= 0) { ++ *param = value; ++ } ++ ++ return count; ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) ++static int read_param_proc(char *buf, char **start, off_t off, int count, ++ int *eof, void *data) ++{ ++ int res; ++ int *param = (int *)data; ++ ++ if (off > 0) { ++ /* We have finished to read, return 0 */ ++ res = 0; ++ } else { ++ res = sprintf(buf, "%d", *param); ++ } ++ ++ return res; ++} ++ ++static void create_param_proc(const char *name, struct proc_dir_entry *base, void *data) ++{ ++ struct proc_dir_entry *entry; ++ ++ entry = create_proc_entry(name, 0644, base); ++ if (entry) { ++ entry->data = data; ++ entry->read_proc = read_param_proc; ++ entry->write_proc = write_param_proc; ++ } ++} ++#else ++static int param_proc_show(struct seq_file *m, void *v) ++{ ++ int *param = (int *)m->private; ++ ++ seq_printf(m, "%d", *param); ++ return 0; ++} ++ ++static int open_param_proc(struct inode *inode, struct file *file) ++{ ++ return single_open(file, param_proc_show, PDE_DATA(inode)); ++} ++ ++static struct file_operations proc_param_fops = { ++ .open = open_param_proc, ++ .read = seq_read, ++ .write = write_param_proc, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static void create_param_proc(const char *name, struct proc_dir_entry *base, void *data) ++{ ++ proc_create_data(name, 0644, base, &proc_param_fops, data); ++} ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) ++static int write_param_freq_proc(struct file *filp, const char __user *buf, ++ unsigned long count, void *data) ++#else ++static ssize_t write_param_freq_proc(struct file *file, const char __user *buf, ++ size_t count, loff_t *pos) ++#endif ++{ ++ char temp[24]; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) ++ struct freq_state *fs = (struct freq_state *)data; ++#else ++ struct freq_state *fs = PDE_DATA(file_inode(file)); ++#endif ++ int value; ++ int len; ++ ++ len = count > (sizeof(temp) - 1) ? (sizeof(temp) - 1) : count; ++ ++ if (copy_from_user(temp, buf, len)) { ++ return -EFAULT; ++ } ++ ++ temp[len] = '\0'; ++ ++ value = simple_strtoul(temp, NULL, 10); ++ if (!value || (value >= MIN_INDEX_FREQ && value <= MAX_INDEX_FREQ)) { ++ fs->freq = value; ++ if (fs->freq) { ++ fs->fac = get_fac(fs->freq); ++ } else { ++ fs->fac = 0; ++ } ++ } ++ ++ return count; ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) ++static int read_param_freq_proc(char *buf, char **start, off_t off, int count, ++ int *eof, void *data) ++{ ++ int res; ++ struct freq_state *fs = (struct freq_state *)data; ++ ++ if (off > 0) { ++ /* We have finished to read, return 0 */ ++ res = 0; ++ } else { ++ res = sprintf(buf, "%d", fs->freq); ++ } ++ ++ return res; ++} ++ ++static void create_param_freq_proc(const char *name, struct proc_dir_entry *base, void *data) ++{ ++ struct proc_dir_entry *entry; ++ ++ entry = create_proc_entry(name, 0644, base); ++ if (entry) { ++ entry->data = data; ++ entry->read_proc = read_param_freq_proc; ++ entry->write_proc = write_param_freq_proc; ++ } ++} ++#else ++static int param_freq_proc_show(struct seq_file *m, void *v) ++{ ++ struct freq_state *fs = (struct freq_state *)m->private; ++ ++ seq_printf(m, "%d", fs->freq); ++ return 0; ++} ++ ++static int open_param_freq_proc(struct inode *inode, struct file *file) ++{ ++ return single_open(file, param_freq_proc_show, PDE_DATA(inode)); ++} ++ ++static struct file_operations proc_param_freq_fops = { ++ .open = open_param_freq_proc, ++ .read = seq_read, ++ .write = write_param_freq_proc, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static void create_param_freq_proc(const char *name, struct proc_dir_entry *base, void *data) ++{ ++ proc_create_data(name, 0644, base, &proc_param_freq_fops, data); ++} ++#endif ++ ++/* ++ * \brief parameter ++ * flag: DECLARE_BITMAP structure definition, TOTAL_CARDS length ++ * is_clean: 0 is to create, 1 is to remove ++ */ ++static void rebuild_recur_proc(unsigned long *flag, int is_clean) ++{ ++ int i, j, k; ++ char temp[24]; ++ struct param *param; ++ struct proc_dir_entry *entry, *subentry; ++ ++ if (!opvxdsp_entry) { ++ return; ++ } ++ ++ if (is_clean) { ++ for (i = 0; i < TOTAL_CARDS; i++) { ++ if (test_bit(i, flag)) { ++ entry = di->dsp[i].entry; ++ if (entry) { ++ param = &di->dsp[i].param; ++ for (j = 0; j < MAX_TONE_NUM; j++) { ++ subentry = param->tone[j].subentry; ++ if (subentry) { ++ remove_proc_entry("busycount", subentry); ++ remove_proc_entry("threshold", subentry); ++ remove_proc_entry("ontime", subentry); ++ remove_proc_entry("offtime", subentry); ++ for (k = 0; k < TONE_FREQ_NUM; k++) { ++ sprintf(temp, "frequency%d", k + 1); ++ remove_proc_entry(temp, subentry); ++ } ++ ++ sprintf(temp, "tone%d", j + 1); ++ remove_proc_entry(temp, entry); ++ } ++ } ++ subentry = param->sd.subentry; ++ if (subentry) { ++ remove_proc_entry("detect_tx", subentry); ++ remove_proc_entry("detect_rx", subentry); ++ remove_proc_entry("length", subentry); ++ remove_proc_entry("threshold", subentry); ++ remove_proc_entry("silent_detect", entry); ++ } ++ ++ sprintf(temp, "%d", i + 1); ++ remove_proc_entry(temp, opvxdsp_entry); ++ } ++ } ++ } ++ } else { ++ for (i = 0; i < TOTAL_CARDS; i++) { ++ if (test_bit(i, flag)) { ++ sprintf(temp, "%d", i + 1); ++ entry = proc_mkdir(temp, opvxdsp_entry); ++ di->dsp[i].entry = entry; ++ if (entry) { ++ param = &di->dsp[i].param; ++ for (j = 0; j < MAX_TONE_NUM; j++) { ++ sprintf(temp, "tone%d", j + 1); ++ subentry = proc_mkdir(temp, entry); ++ param->tone[j].subentry = subentry; ++ if (subentry) { ++ create_param_proc("busycount", subentry, ¶m->tone[j].busycount); ++ create_param_proc("threshold", subentry, ¶m->tone[j].threshold); ++ create_param_proc("ontime", subentry, ¶m->tone[j].ontime); ++ create_param_proc("offtime", subentry, ¶m->tone[j].offtime); ++ for (k = 0; k < TONE_FREQ_NUM; k++) { ++ sprintf(temp, "frequency%d", k + 1); ++ create_param_freq_proc(temp, subentry, ¶m->tone[j].fs[k]); ++ } ++ } ++ } ++ subentry = proc_mkdir("silent_detect", entry); ++ param->sd.subentry = subentry; ++ if (subentry) { ++ create_param_proc("detect_tx", subentry, ¶m->sd.detect_tx); ++ create_param_proc("detect_rx", subentry, ¶m->sd.detect_rx); ++ create_param_proc("length", subentry, ¶m->sd.length); ++ create_param_proc("threshold", subentry, ¶m->sd.threshold); ++ } ++ } ++ } ++ } ++ } ++} ++ ++static void set_chan_cards_bit(struct a24xx *wc) ++{ ++ int i, channo; ++ DECLARE_BITMAP(tempflag, TOTAL_CARDS); ++ struct a24xx_dev *wc_dev = &wc->dev; ++ ++ bitmap_zero(tempflag, TOTAL_CARDS); ++ ++ for (i = 0; i < wc_dev->max_cards; i++) { ++ channo = wc->chans[i]->channo; ++ if (channo <= TOTAL_CARDS) { ++ if (wc_dev->modtype[i] == MOD_TYPE_FXO) { ++ set_bit(channo - 1, fxo_cardflag); ++ set_bit(channo - 1, tempflag); ++ } ++ } ++ } ++ ++ rebuild_recur_proc(tempflag, 0); ++} ++ ++static void clear_chan_cards_bit(struct a24xx *wc) ++{ ++ int i, channo; ++ DECLARE_BITMAP(tempflag, TOTAL_CARDS); ++ struct a24xx_dev *wc_dev = &wc->dev; ++ ++ bitmap_zero(tempflag, TOTAL_CARDS); ++ ++ for (i = 0; i < wc_dev->max_cards; i++) { ++ channo = wc->chans[i]->channo; ++ if (channo <= TOTAL_CARDS) { ++ if (wc_dev->modtype[i] == MOD_TYPE_FXO) { ++ clear_bit(channo - 1, fxo_cardflag); ++ set_bit(channo - 1, tempflag); ++ } ++ } ++ } ++ ++ rebuild_recur_proc(tempflag, 1); ++} ++ ++int init_busydetect(struct a24xx *wc, const char *opermode) ++{ ++ int res = 0; ++ ++ if (!ref_count++) { ++ bitmap_zero(fxo_cardflag, TOTAL_CARDS); ++ bitmap_zero(fxs_cardflag, TOTAL_CARDS); ++ ++ di = kzalloc(sizeof(*di), GFP_KERNEL); ++ if (!di) { ++ printk(KERN_ERR "Not enough memory, A2410P not support the busy tone and silence detection\n"); ++ res = -ENOMEM; ++ goto out; ++ } ++ ++ init_detect_info(di, opermode); ++ ++ opvxdsp_entry = proc_mkdir(module_name, NULL); ++ ++ printk(KERN_INFO "A2410P start the busy tone and silence detection\n"); ++ } ++ ++ set_chan_cards_bit(wc); ++out: ++ return res; ++} ++ ++void destroy_busydetect(struct a24xx *wc) ++{ ++ if (ref_count) { ++ clear_chan_cards_bit(wc); ++ ++ if (!--ref_count) { ++ if (di) { ++ remove_proc_entry(module_name, NULL); ++ kfree(di); ++ printk(KERN_INFO "A2410P stop the busy tone and silence detection\n"); ++ } ++ } ++ } ++} +--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/busydetect.h 1970-01-01 01:00:00.000000000 +0100 ++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/busydetect.h 2015-02-10 14:19:03.000000000 +0100 +@@ -0,0 +1,313 @@ ++/* ++ * OpenVox FXO Detect Busy Voice Driver for DAHDI Telephony interface ++ * ++ * Written by kevin.chen ++ ++ * Copyright (C) 2012-2013 OpenVox Communication Co. Ltd, ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef _BUSYDETECT_H ++#define _BUSYDETECT_H ++ ++#include "base.h" ++ ++#define FRAME_SHORT_SIZE (DAHDI_CHUNKSIZE * 20) ++#define SAMPLE_PER_SEC 8000 ++ ++#define TOTAL_CARDS 120 ++ ++#define SIN_DIVISION 128 ++static const short sin_table[] = ++{ ++ 201, ++ 603, ++ 1005, ++ 1407, ++ 1809, ++ 2210, ++ 2611, ++ 3012, ++ 3412, ++ 3812, ++ 4211, ++ 4609, ++ 5007, ++ 5404, ++ 5800, ++ 6195, ++ 6590, ++ 6983, ++ 7376, ++ 7767, ++ 8157, ++ 8546, ++ 8933, ++ 9319, ++ 9704, ++ 10088, ++ 10469, ++ 10850, ++ 11228, ++ 11605, ++ 11980, ++ 12354, ++ 12725, ++ 13095, ++ 13463, ++ 13828, ++ 14192, ++ 14553, ++ 14912, ++ 15269, ++ 15624, ++ 15976, ++ 16326, ++ 16673, ++ 17018, ++ 17361, ++ 17700, ++ 18037, ++ 18372, ++ 18703, ++ 19032, ++ 19358, ++ 19681, ++ 20001, ++ 20318, ++ 20632, ++ 20943, ++ 21251, ++ 21555, ++ 21856, ++ 22154, ++ 22449, ++ 22740, ++ 23028, ++ 23312, ++ 23593, ++ 23870, ++ 24144, ++ 24414, ++ 24680, ++ 24943, ++ 25202, ++ 25457, ++ 25708, ++ 25956, ++ 26199, ++ 26439, ++ 26674, ++ 26906, ++ 27133, ++ 27357, ++ 27576, ++ 27791, ++ 28002, ++ 28209, ++ 28411, ++ 28610, ++ 28803, ++ 28993, ++ 29178, ++ 29359, ++ 29535, ++ 29707, ++ 29875, ++ 30038, ++ 30196, ++ 30350, ++ 30499, ++ 30644, ++ 30784, ++ 30920, ++ 31050, ++ 31177, ++ 31298, ++ 31415, ++ 31527, ++ 31634, ++ 31737, ++ 31834, ++ 31927, ++ 32015, ++ 32099, ++ 32177, ++ 32251, ++ 32319, ++ 32383, ++ 32442, ++ 32496, ++ 32546, ++ 32590, ++ 32629, ++ 32664, ++ 32693, ++ 32718, ++ 32738, ++ 32753, ++ 32762, ++ 32767, ++ 32767 ++}; ++ ++/* Level index range -30 ~ 0, step 1 */ ++#define MIN_INDEX_LEVEL -30 ++#define MAX_INDEX_LEVEL 0 ++#define STEP_LEVEL 1 ++static const short gain_table[] = { ++ 722, ++ 810, ++ 909, ++ 1020, ++ 1144, ++ 1284, ++ 1440, ++ 1616, ++ 1813, ++ 2034, ++ 2283, ++ 2561, ++ 2874, ++ 3224, ++ 3618, ++ 4059, ++ 4554, ++ 5110, ++ 5734, ++ 6433, ++ 7218, ++ 8099, ++ 9087, ++ 10196, ++ 11440, ++ 12836, ++ 14402, ++ 16160, ++ 18132, ++ 20344, ++ 22826, ++}; ++ ++/* Frequency index range 300 ~ 700, step 5 */ ++#define MIN_INDEX_FREQ 300 ++#define MAX_INDEX_FREQ 700 ++#define STEP_FREQ 5 ++static const short fac_table[] = { ++ 31861, ++ 31830, ++ 31800, ++ 31768, ++ 31737, ++ 31704, ++ 31672, ++ 31638, ++ 31605, ++ 31570, ++ 31536, ++ 31501, ++ 31465, ++ 31429, ++ 31392, ++ 31355, ++ 31318, ++ 31279, ++ 31241, ++ 31202, ++ 31162, ++ 31122, ++ 31082, ++ 31041, ++ 30999, ++ 30958, ++ 30915, ++ 30872, ++ 30829, ++ 30785, ++ 30741, ++ 30696, ++ 30651, ++ 30605, ++ 30559, ++ 30512, ++ 30465, ++ 30417, ++ 30369, ++ 30321, ++ 30272, ++ 30222, ++ 30172, ++ 30122, ++ 30071, ++ 30020, ++ 29968, ++ 29916, ++ 29863, ++ 29810, ++ 29756, ++ 29702, ++ 29648, ++ 29593, ++ 29537, ++ 29481, ++ 29425, ++ 29368, ++ 29311, ++ 29253, ++ 29195, ++ 29136, ++ 29077, ++ 29017, ++ 28957, ++ 28897, ++ 28836, ++ 28775, ++ 28713, ++ 28651, ++ 28588, ++ 28525, ++ 28462, ++ 28398, ++ 28333, ++ 28268, ++ 28203, ++ 28137, ++ 28071, ++ 28005, ++ 27938, ++}; ++ ++/* busydetect.c */ ++void parser_busy_silent_process(struct a24xx *wc, int is_write); ++ ++int init_busydetect(struct a24xx *wc, const char *opermode); ++void destroy_busydetect(struct a24xx *wc); ++ ++/* callerid.c */ ++void parser_callerid_process(struct a24xx *wc, int cidbuflen, int cidtimeout); ++void set_cidstart_desc_from_chan_num(int spanno, int channo, int cid_state); ++void set_signal_unknown_from_chan_num(int spanno, int channo); ++int is_callerid_disable(int spanno, int channo); ++char is_ring_delay_operation(int spanno, int channo); ++void reset_parser_variable_from_chan_num(int spanno, int channo); ++ ++int init_callerid(struct a24xx *wc); ++void destroy_callerid(struct a24xx *wc); ++ ++#endif /* _BUSYDETECT_H */ +--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/callerid.c 1970-01-01 01:00:00.000000000 +0100 ++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/callerid.c 2015-02-10 14:19:03.000000000 +0100 +@@ -0,0 +1,1429 @@ ++/* ++ * OpenVox Calling Identity Delivery Analysis Driver for DAHDI Telephony interface ++ * ++ * Written by kevin.chen ++ ++ * Copyright (C) 2012-2013 OpenVox Communication Co. Ltd, ++ * ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++/* Rev history ++ * ++ * Rev 0.10 add ringdly_flag variable, deal with special caller id case. ++ * Rev 0.30 support new kernel version 3.10.0 ++ * ++ */ ++ ++#include <linux/proc_fs.h> ++#include <linux/ctype.h> ++#include <linux/kmod.h> ++#include "base.h" ++#include "busydetect.h" /* use sin_table[] */ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) ++#include <linux/seq_file.h> ++#endif ++ ++static int cid_debug = 0; ++module_param(cid_debug, int, 0600); ++ ++/* Before using this directory, the directory must had been created */ ++static const char *module_name = "opvxdsp"; ++ ++#define MAX_CID_LEN 32 ++#define DTMF_BLOCK_SIZE 102 ++#define MAX_DTMF_DIGITS 64 ++ ++#define DTMF_TIMEOUT_SAMPLES (300 * DAHDI_CHUNKSIZE) /* default 300 msec */ ++ ++/* Support signals for caller id */ ++enum { ++ /* Signal type for caller id fsk */ ++ CALLERID_BELL202_OR_V23, ++ CALLERID_V23JP, ++ MAX_FSK_NUM, ++ /* Dtmf signal */ ++ CALLERID_DTMF, ++}; ++ ++/* type for caller id start */ ++enum { ++ CIDSTART_RING = 1, ++ CIDSTART_POLARITY, ++ CIDSTART_POLARITY_IN, ++ CIDSTART_DTMF, ++ MAX_CIDSTART, ++}; ++ ++/* Unknown caller id signal */ ++#define UNKNOWN_CID_SIGNAL -1 ++ ++static const char *signal_desc[] = { ++ [CALLERID_BELL202_OR_V23] = "bell or v23", ++ [CALLERID_V23JP] = "v23_jp", ++ [CALLERID_DTMF] = "dtmf", ++}; ++static const char *cidstart_desc[] = { ++ [CIDSTART_RING] = "ring", ++ [CIDSTART_POLARITY] = "polarity", ++ [CIDSTART_POLARITY_IN] = "polarity_in", ++ [CIDSTART_DTMF] = "dtmf", ++}; ++static const char *unknown_desc = "unknown"; ++ ++static const int dtmf_fac[] = {27978, 26955, 25700, 24217, 19072, 16324, 13084, 9314}; ++static const char dtmf_positions[] = "123A" "456B" "789C" "*0#D"; ++ ++typedef struct freq_state { ++ short prev2; ++ short prev; ++ short fac; ++}freq_state_t; ++ ++typedef struct callerid_dtmf { ++ freq_state_t row[4]; ++ freq_state_t col[4]; ++ ++ int cur_sample; ++ u32 energy; ++ u8 digit; ++ u8 last_hit; ++ ++ int timeout; ++ ++ /* Analytical results */ ++ char digits[MAX_DTMF_DIGITS + 1]; ++ int num; ++}__attribute__((packed))callerid_dtmf_t; ++ ++typedef struct complex { ++ int re; ++ int im; ++}complex_t; ++ ++typedef struct callerid_fsk { ++ int baud_rate; ++ int baud_frac; ++ int sig_present; ++ int last_bit; ++ ++ int phase_rate[2]; ++ u32 phase_acc[2]; ++ ++ complex_t window[2][24]; ++ complex_t dot[2]; ++ int dot_pos; ++ int span; ++ ++ short last_amp; ++ int power; ++ ++ /* Analytical results */ ++ u8 name[MAX_CID_LEN]; ++ u8 number[MAX_CID_LEN]; ++ ++ int bit_pos; ++ u8 msg[256]; ++ int msg_len; ++ int continuous_one; ++ int val; ++}__attribute__((packed))callerid_fsk_t; ++ ++typedef struct gen_wave { ++ int bit_no; ++ int bit_pos; ++ int byte_no; ++ ++ int occupied_len; ++ int premark_len; ++ int postmark_len; ++ ++ int msg_len; ++ u8 msg[256]; ++ ++ int baud_frac; ++ int baud_rate; ++ ++ int scaling; ++ int phase_rates[2]; ++ u32 phase_acc; ++}__attribute__((packed))gen_wave_t; ++ ++typedef struct callerid { ++ /* Initial current signal unknown */ ++ int cur_sig; ++ /* Generate the signal */ ++ int appear; ++ ++ /* DTMF signal analysis */ ++ callerid_dtmf_t dtmf; ++ /* Variety of fsk signal analysis */ ++ callerid_fsk_t fsk[MAX_FSK_NUM]; ++ ++ /* Structuration for generator waveform */ ++ gen_wave_t gw; ++}__attribute__((packed))callerid_t; ++ ++struct param { ++ /* The pointer of signal description from the last detection */ ++ const char *last_signal; ++ /* The pointer of signal description from the current detection */ ++ const char *detect_signal; ++ /* Signal the start of caller id */ ++ const char *cidstart; ++ /* Close the current channel caller id support */ ++ u8 disable; ++}; ++ ++struct detect_info { ++ int channo; ++ callerid_t cid; ++ ++ /* Initialized to zero do not find any cidstart signal */ ++ char cidstart_type; ++ ++ /* ++ * Sometimes time interval is shorter between signal of caller id end and the next ++ * bell before, so unable to provide a complete signal to asterisk ++ * Set flag to postpone ring tones appears ++ */ ++ char ringdly_flag; ++ ++ struct param param; ++ struct proc_dir_entry *entry; ++ ++ struct list_head list; ++}__attribute__((packed)); ++ ++#define MAX_LIST_SPAN 20 ++static struct list_head di_list[MAX_LIST_SPAN]; ++ ++static void reset_parser_variable_result(callerid_t *cid); ++ ++static const u16 crc16_table[] = { ++ 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF, ++ 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7, ++ 0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E, ++ 0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876, ++ 0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD, ++ 0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5, ++ 0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C, ++ 0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974, ++ 0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB, ++ 0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3, ++ 0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A, ++ 0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72, ++ 0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9, ++ 0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1, ++ 0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738, ++ 0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70, ++ 0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7, ++ 0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF, ++ 0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036, ++ 0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E, ++ 0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5, ++ 0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD, ++ 0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134, ++ 0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C, ++ 0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3, ++ 0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB, ++ 0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232, ++ 0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A, ++ 0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1, ++ 0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9, ++ 0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330, ++ 0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78 ++}; ++ ++static u16 check_crc16(const u8 *buf, int len) ++{ ++ int i; ++ u16 crc = 0; ++ ++ for (i = 0; i < len; i++) { ++ crc = (crc >> 8) ^ crc16_table[(crc ^ buf[i]) & 0xff]; ++ } ++ return crc; ++} ++ ++static int get_phase_rate(int freq) ++{ ++ return (freq * 65536 / SAMPLE_PER_SEC) * 65536; ++} ++ ++static short calc_amp(u32 acc) ++{ ++ u32 phase, step; ++ short amp; ++ ++ phase = acc; ++ phase >>= 23; ++ step = phase & (SIN_DIVISION - 1); ++ if ((phase & SIN_DIVISION)) { ++ step = SIN_DIVISION - step; ++ } ++ ++ amp = sin_table[step]; ++ if ((phase & (2 * SIN_DIVISION))) { ++ amp = -amp; ++ } ++ return amp; ++} ++ ++static void fsk_put_msg(callerid_fsk_t *fsk, const u8 msg[], int len, int sig_type) ++{ ++ int i, pos; ++ int res; ++ ++ memset(fsk->name, 0, sizeof(fsk->name)); ++ memset(fsk->number, 0, sizeof(fsk->number)); ++ if (sig_type == CALLERID_V23JP) { ++ pos = 7; ++ for (i = 0; i < msg[6]; i++) { ++ if (msg[i + pos] == 0x02) { ++ i++; ++ res = (msg[i + pos] <= MAX_CID_LEN) ? msg[i + pos] : MAX_CID_LEN; ++ memcpy(fsk->number, msg + i + pos + 1, res); ++ i += msg[i + pos] + 1; ++ } else { ++ i++; ++ i += msg[i + pos] + 1; ++ } ++ } ++ } else { ++ if (msg[0] == 0x80 || msg[0] == 0x82) { ++ /* MDMF */ ++ pos = 2; ++ for (i = 0; i < msg[1];) { ++ switch (msg[i + pos]) { ++ case 2: ++ case 4: ++ i++; ++ res = (msg[i + pos] <= MAX_CID_LEN) ? msg[i + pos] : MAX_CID_LEN; ++ memcpy(fsk->number, msg + i + pos + 1, res); ++ i += msg[i + pos] + 1; ++ break; ++ case 7: ++ case 8: ++ i++; ++ res = (msg[i + pos] <= MAX_CID_LEN) ? msg[i + pos] : MAX_CID_LEN; ++ memcpy(fsk->name, msg + i + pos + 1, res); ++ i += msg[i + pos] + 1; ++ break; ++ default: ++ i++; ++ i += msg[i + pos] + 1; ++ break; ++ } ++ } ++ } else if (msg[0] == 0x04) { ++ /* SDMF */ ++ if (msg[1] > 8) { ++ memcpy(fsk->number, msg + 10, (msg[1] - 8) <= MAX_CID_LEN ? (msg[1] - 8) : MAX_CID_LEN); ++ } ++ } ++ } ++} ++ ++static void fsk_put_bit(callerid_fsk_t *fsk, int bit, int sig_type) ++{ ++ int i, sum; ++ ++ if (fsk->bit_pos == 0) { ++ if (!bit) { ++ /* Start bit */ ++ fsk->bit_pos++; ++ if (fsk->continuous_one > 10) { ++ fsk->msg_len = 0; ++ } ++ fsk->continuous_one = 0; ++ } else { ++ fsk->continuous_one++; ++ } ++ } else if (fsk->bit_pos <= 8) { ++ fsk->val >>= 1; ++ if (bit) { ++ fsk->val |= 0x80; ++ } ++ fsk->bit_pos++; ++ } else { ++ /* Stop bit */ ++ if (bit && fsk->msg_len < 256) { ++ if (sig_type == CALLERID_V23JP) { ++ if (fsk->msg_len == 0) { ++ if (fsk->val == 0x90) { ++ fsk->msg[fsk->msg_len++] = (u8)fsk->val; ++ } ++ } else { ++ fsk->msg[fsk->msg_len++] = (u8)fsk->val; ++ } ++ if (fsk->msg_len >= 11 && fsk->msg_len == ((fsk->msg[6] & 0x7f) + 11)) { ++ if (check_crc16(fsk->msg + 2, fsk->msg_len - 2) == 0) { ++ for (i = 0; i < fsk->msg_len - 2; i++) { ++ fsk->msg[i] &= 0x7f; ++ } ++ fsk_put_msg(fsk, fsk->msg, fsk->msg_len - 2, sig_type); ++ } ++ fsk->msg_len = 0; ++ } ++ } else { ++ fsk->msg[fsk->msg_len++] = (u8)fsk->val; ++ if (fsk->msg_len >= 3 && fsk->msg_len == (fsk->msg[1] + 3)) { ++ sum = 0; ++ for (i = 0; i < fsk->msg_len - 1; i++) { ++ sum += fsk->msg[i]; ++ } ++ if (256 - (sum & 0xff) == fsk->msg[i]) { ++ fsk_put_msg(fsk, fsk->msg, fsk->msg_len - 1, sig_type); ++ } ++ fsk->msg_len = 0; ++ } ++ } ++ } /* if (bit && fsk->msg_len < 256) */ ++ ++ fsk->bit_pos = 0; ++ fsk->val = 0; ++ } ++} ++ ++static void analysis_fsk(callerid_fsk_t *fsk, short amp[], int len, int sig_type) ++{ ++ int i, j; ++ int dot_pos; ++ int hit_bit; ++ short x; ++ int dot; ++ int sum[2]; ++ complex_t ph; ++ ++ dot_pos = fsk->dot_pos; ++ ++ for (i = 0; i < len; i++) { ++ for (j = 0; j < 2; j++) { ++ fsk->dot[j].re -= fsk->window[j][dot_pos].re; ++ fsk->dot[j].im -= fsk->window[j][dot_pos].im; ++ ++ ph.re = calc_amp(fsk->phase_acc[j] + (1 << 30)); ++ ph.im = calc_amp(fsk->phase_acc[j]); ++ fsk->phase_acc[j] += fsk->phase_rate[j]; ++ fsk->window[j][dot_pos].re = (ph.re * amp[i]) >> 3; ++ fsk->window[j][dot_pos].im = (ph.im * amp[i]) >> 3; ++ ++ fsk->dot[j].re += fsk->window[j][dot_pos].re; ++ fsk->dot[j].im += fsk->window[j][dot_pos].im; ++ ++ dot = fsk->dot[j].re >> 15; ++ sum[j] = dot * dot; ++ dot = fsk->dot[j].im >> 15; ++ sum[j] += dot * dot; ++ } ++ ++ x = (amp[i] >> 1) - fsk->last_amp; ++ fsk->power += ((x * x - fsk->power) >> 4); ++ fsk->last_amp = amp[i] >> 1; ++ if (fsk->sig_present) { ++ /* calc result 36380=pow(10.0,(level-14.7)/10.0)*32767.0*32767.0, level=-30 */ ++ if (fsk->power < 36380) { ++ fsk->sig_present = 0; ++ fsk->baud_frac = 0; ++ continue; ++ } ++ } else { ++ /* calc result 115046=pow(10.0,(level-9.7)/10.0)*32767.0*32767.0, level=-30 */ ++ if (fsk->power < 115046) { ++ fsk->baud_frac = 0; ++ continue; ++ } ++ ++ fsk->sig_present = 1; ++ fsk->baud_frac = 0; ++ fsk->last_bit = 0; ++ } ++ ++ hit_bit = (sum[0] < sum[1]); ++ if (fsk->last_bit != hit_bit) { ++ fsk->last_bit = hit_bit; ++ fsk->baud_frac = (SAMPLE_PER_SEC >> 1); ++ } ++ ++ fsk->baud_frac += fsk->baud_rate; ++ if (fsk->baud_frac >= SAMPLE_PER_SEC) { ++ fsk->baud_frac -= SAMPLE_PER_SEC; ++ fsk_put_bit(fsk, hit_bit, sig_type); ++ } ++ ++ if (++dot_pos >= fsk->span) { ++ dot_pos = 0; ++ } ++ } ++ ++ fsk->dot_pos = dot_pos; ++} ++ ++static inline u32 detect_result(freq_state_t *fs) ++{ ++ u32 val; ++ ++ val = fs->prev * fs->prev + fs->prev2 * fs->prev2 - ((fs->fac * fs->prev) >> 14) * fs->prev2; ++ fs->prev = fs->prev2 = 0; ++ ++ return val; ++} ++ ++static inline void update_detect(freq_state_t *fs, short s) ++{ ++ short tmp; ++ ++ tmp = fs->prev2; ++ fs->prev2 = fs->prev; ++ fs->prev = (((int)fs->fac * fs->prev2) >> 14) - tmp + (s >> 7); ++} ++ ++static void get_dtmf_number(callerid_dtmf_t *dtmf, u8 number[]) ++{ ++ int i; ++ int code; ++ char *p = dtmf->digits; ++ ++ if (dtmf->num < 2) { ++ return; ++ } ++ ++ if (p[0] == 'B') { ++ code = simple_strtoul(p + 1, NULL, 10); ++ if (code == 0) { ++ /* Unknown caller id number */ ++ number[0] = 'O'; ++ } else if (code == 10) { ++ /* Private caller id number */ ++ number[0] = 'P'; ++ } ++ } else if (p[0] == 'D' && p[2] == '#') { ++ if (p[1] == '1') { ++ /* Private caller id number */ ++ number[0] = 'P'; ++ } else if (p[1] == '2' || p[2] == '3') { ++ /* Unknown caller id number */ ++ number[0] = 'O'; ++ } ++ } else if (p[0] == 'D' || p[0] == 'A') { ++ for (i = 1; i < dtmf->num; i++) { ++ if (p[i] == 'C' || p[i] == '#') { ++ break; ++ } ++ if (isdigit(p[i]) && i <= MAX_CID_LEN) { ++ number[i - 1] = p[i]; ++ } ++ } ++ } else if (isdigit(p[0])) { ++ for (i = 0; i < dtmf->num; i++) { ++ if (isdigit(p[i]) && i < MAX_CID_LEN) { ++ number[i] = p[i]; ++ } else { ++ break; ++ } ++ } ++ } else { ++ /* Unknown caller id number */ ++ number[0] = 'O'; ++ } ++} ++ ++static void analysis_dtmf(callerid_dtmf_t *dtmf, short s[], int len) ++{ ++ int i, j, k; ++ int limit; ++ short temp; ++ int row_energy[4]; ++ int col_energy[4]; ++ int best_row, best_col; ++ u8 hit; ++ ++ for (i = 0; i < len; i = limit) { ++ if (len - i >= DTMF_BLOCK_SIZE - dtmf->cur_sample) { ++ limit = i + DTMF_BLOCK_SIZE - dtmf->cur_sample; ++ } else { ++ limit = len; ++ } ++ ++ for (j = i; j < limit; j++) { ++ temp = (s[j] < 0 ? -s[j] : s[j]) >> 7; ++ dtmf->energy += temp * temp; ++ for (k = 0; k < 4; k++) { ++ update_detect(&dtmf->row[k], s[j]); ++ update_detect(&dtmf->col[k], s[j]); ++ } ++ } ++ ++ dtmf->cur_sample += (limit - i); ++ if (dtmf->cur_sample < DTMF_BLOCK_SIZE) { ++ break; ++ } ++ ++ row_energy[0] = detect_result(&dtmf->row[0]); ++ col_energy[0] = detect_result(&dtmf->col[0]); ++ best_row = 0; ++ best_col = 0; ++ for (j = 1; j < 4; j++) { ++ row_energy[j] = detect_result(&dtmf->row[j]); ++ if (row_energy[j] > row_energy[best_row]) { ++ best_row = j; ++ } ++ col_energy[j] = detect_result(&dtmf->col[j]); ++ if (col_energy[j] > col_energy[best_col]) { ++ best_col = j; ++ } ++ } ++ ++ hit = 0; ++ if (row_energy[best_row] >= 10438 && ++ col_energy[best_col] >= 10438 && ++ col_energy[best_col] < row_energy[best_row] * 2 && ++ col_energy[best_col] * 6 > row_energy[best_row]) { ++ for (j = 0; j < 4; j++) { ++ if ((j != best_row && row_energy[j] * 6 > row_energy[best_row]) || ++ (j != best_col && col_energy[j] * 6 > col_energy[best_col])) { ++ break; ++ } ++ } ++ if (j >= 4 && ((row_energy[best_row] + col_energy[best_col]) > 42 * dtmf->energy)) { ++ hit = dtmf_positions[(best_row << 2) + best_col]; ++ } ++ } ++ ++ if (hit) { ++ dtmf->timeout = 0; ++ } else { ++ dtmf->timeout += DTMF_BLOCK_SIZE; ++ } ++ ++ if (hit != dtmf->digit) { ++ if (dtmf->last_hit != dtmf->digit) { ++ hit = (hit && hit == dtmf->last_hit) ? hit : 0; ++ if (hit) { ++ if (dtmf->num < MAX_DTMF_DIGITS) { ++ dtmf->digits[dtmf->num++] = (char)hit; ++ dtmf->digits[dtmf->num] = '\0'; ++ } ++ } ++ dtmf->digit = hit; ++ } ++ } ++ dtmf->last_hit = hit; ++ dtmf->energy = 0; ++ dtmf->cur_sample = 0; ++ } ++} ++ ++static const u16 mon_yday[2][13] = { ++ /* Normal year */ ++ {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, ++ /* Leap year */ ++ {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}, ++}; ++ ++static void get_systime_val(int *mon, int *mday, int *hour, int *min) ++{ ++ struct timeval tv; ++ const u16 *ip; ++ int days, rem, y; ++ int yg; ++ ++ do_gettimeofday(&tv); ++ ++#define SECS_PER_HOUR (60 * 60) ++#define SECS_PER_DAY (SECS_PER_HOUR * 24) ++ days = tv.tv_sec / SECS_PER_DAY; ++ rem = tv.tv_sec % SECS_PER_DAY; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29) ++ extern struct timezone sys_tz; ++#endif ++ rem += (-sys_tz.tz_minuteswest * 60); ++ while (rem < 0) { ++ rem += SECS_PER_DAY; ++ days--; ++ } ++ while (rem > SECS_PER_DAY) { ++ rem -= SECS_PER_DAY; ++ days++; ++ } ++ ++ *hour = rem / SECS_PER_HOUR; ++ rem %= SECS_PER_HOUR; ++ *min = rem / 60; ++ ++#define IS_LEAP(y) (((y) % 4 == 0) && ((y) % 100 != 0 || (y) % 400 == 0)) ++#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) ++#define LEAP_BETWEEN(y) (DIV(y, 4) - DIV(y, 100) + DIV(y, 400)) ++ y = 1970; ++ while (days < 0 || days >= (IS_LEAP(y) ? 366 : 365)) { ++ yg = y + DIV(days, 365); ++ days -= ((yg - y) * 365 + LEAP_BETWEEN(yg - 1) - LEAP_BETWEEN(y - 1)); ++ y = yg; ++ } ++ ++ ip = mon_yday[IS_LEAP(y)]; ++ for (y = 11; days < ip[y]; y--) { ++ continue; ++ } ++ days -= ip[y]; ++ ++ *mon = y + 1; ++ *mday = days + 1; ++} ++ ++static int build_msg(u8 *msg, const char *number, const char *name) ++{ ++ int i, res; ++ int len; ++ int mon, mday, hour, min; ++ int sum = 0; ++ u8 *ptr = msg + 2; ++ ++ get_systime_val(&mon, &mday, &hour, &min); ++ res = sprintf(ptr, "\001\010%02d%02d%02d%02d", mon, mday, hour, min); ++ ptr += res; ++ ++ if (strlen(number)) { ++ len = strlen(number); ++ res = sprintf(ptr, "\002%c", len); ++ ptr += res; ++ for (i = 0; i < len; i++) { ++ ptr[i] = number[i]; ++ } ++ ptr[i] = '\0'; ++ ptr += len; ++ } else { ++ res = sprintf(ptr, "\004\001O"); ++ ptr += res; ++ } ++ ++ if (strlen(name)) { ++ len = strlen(name); ++ res = sprintf(ptr, "\007%c", len); ++ ptr += res; ++ for (i = 0; i < len; i++) { ++ ptr[i] = name[i]; ++ } ++ ptr[i] = '\0'; ++ ptr += len; ++ } else { ++ res = sprintf(ptr, "\010\001O"); ++ ptr += res; ++ } ++ ++ msg[0] = 0x80; ++ msg[1] = ptr - msg - 2; ++ ++ for (i = 0; i < ptr - msg; i++) { ++ sum += msg[i]; ++ } ++ msg[ptr - msg] = 256 - (sum & 0xff); ++ ptr++; ++ ++ return (ptr - msg); ++} ++ ++static int fsk_get_bit(gen_wave_t *gw) ++{ ++ int bit; ++ ++ if (gw->bit_no < gw->occupied_len) { ++ bit = gw->bit_no & 1; ++ gw->bit_no++; ++ } else if (gw->bit_no < gw->occupied_len + gw->premark_len) { ++ bit = 1; ++ gw->bit_no++; ++ } else if (gw->bit_no == gw->occupied_len + gw->premark_len) { ++ if (gw->bit_pos == 0) { ++ /* Start bit */ ++ bit = 0; ++ gw->bit_pos++; ++ } else if (gw->bit_pos <= 8) { ++ bit = (gw->msg[gw->byte_no] >> (gw->bit_pos - 1)) & 1; ++ gw->bit_pos++; ++ } else { ++ /* Stop bit */ ++ bit = 1; ++ gw->bit_pos = 0; ++ if (++gw->byte_no >= gw->msg_len) { ++ gw->bit_no++; ++ } ++ } ++ } else if (gw->bit_no <= gw->occupied_len + gw->premark_len + gw->postmark_len) { ++ bit = 1; ++ gw->bit_no++; ++ } else { ++ /* Completion */ ++ bit = -1; ++ } ++ ++ return bit; ++} ++ ++static short generater_amp(u32 *phase_acc, int phase_rate, int scale) ++{ ++ short amp; ++ ++ amp = (short)(((int)calc_amp(*phase_acc) * scale) >> 15); ++ *phase_acc += phase_rate; ++ return amp; ++} ++ ++static int fsk_gen(gen_wave_t *gw, short amp[], int len) ++{ ++ int i = 0; ++ int bit = 0; ++ static int cur_bit = 1; ++ int cur_phase_rate; ++ ++ cur_phase_rate = gw->phase_rates[cur_bit]; ++ while (i < len) { ++ if ((gw->baud_frac += gw->baud_rate) >= SAMPLE_PER_SEC) { ++ gw->baud_frac -= SAMPLE_PER_SEC; ++ bit = fsk_get_bit(gw); ++ if (bit == -1) { ++ /* Completion */ ++ cur_bit = 1; ++ break; ++ } ++ cur_bit = bit & 1; ++ cur_phase_rate = gw->phase_rates[cur_bit]; ++ } ++ amp[i++] = generater_amp(&gw->phase_acc, cur_phase_rate, gw->scaling); ++ } ++ ++ return bit; ++} ++ ++static void analysis_all_signal(callerid_t *cid, short s[], int len) ++{ ++ int i; ++ ++ analysis_dtmf(&cid->dtmf, s, len); ++ for (i = 0; i < MAX_FSK_NUM; i++) { ++ analysis_fsk(&cid->fsk[i], s, len, i); ++ } ++} ++ ++static void analysis_current_signal(callerid_t *cid, short s[], int len) ++{ ++ switch (cid->cur_sig) { ++ case CALLERID_BELL202_OR_V23: ++ case CALLERID_V23JP: ++ analysis_fsk(&cid->fsk[cid->cur_sig], s, len, cid->cur_sig); ++ break; ++ case CALLERID_DTMF: ++ analysis_dtmf(&cid->dtmf, s, len); ++ break; ++ default: /* UNKNOWN_CID_SIGNAL */ ++ /* signal of caller id is unknown, re-check */ ++ analysis_all_signal(cid, s, len); ++ break; ++ } ++} ++ ++static struct detect_info *get_detect_info_from_chan_num(int spanno, int channo) ++{ ++ struct detect_info *di; ++ ++ if (spanno <= 0 || spanno > MAX_LIST_SPAN) { ++ return NULL; ++ } ++ ++ list_for_each_entry(di, &di_list[spanno - 1], list) { ++ if (di->channo == channo) { ++ /* Find the corresponding matching */ ++ return di; ++ } ++ } ++ ++ return NULL; ++} ++ ++static void set_signal_desc(struct detect_info *di) ++{ ++ di->param.last_signal = di->param.detect_signal; ++ ++ switch (di->cid.cur_sig) { ++ case CALLERID_BELL202_OR_V23: ++ case CALLERID_V23JP: ++ case CALLERID_DTMF: ++ di->param.detect_signal = signal_desc[di->cid.cur_sig]; ++ break; ++ default: /* UNKNOWN_CID_SIGNAL */ ++ di->param.detect_signal = unknown_desc; ++ break; ++ } ++} ++ ++void set_signal_unknown_from_chan_num(int spanno, int channo) ++{ ++ struct detect_info *di; ++ ++ di = get_detect_info_from_chan_num(spanno, channo); ++ if (di) { ++ /* Default standard does not analyze the current signal */ ++ di->cid.cur_sig = UNKNOWN_CID_SIGNAL; ++ set_signal_desc (di); ++ /* set the ring delay flag */ ++ if (di->cid.dtmf.num > 1) { ++ di->ringdly_flag = 1; ++ } else { ++ di->ringdly_flag = di->cid.appear > 0 ? 1 : 0; ++ } ++ } ++} ++ ++char is_ring_delay_operation(int spanno, int channo) ++{ ++ struct detect_info *di; ++ ++ di = get_detect_info_from_chan_num(spanno, channo); ++ if (di) { ++ return di->ringdly_flag; ++ } else { ++ return 0; ++ } ++} ++ ++static void clear_cidstart_type(struct detect_info *di) ++{ ++ di->cidstart_type = 0; ++} ++ ++static void set_cidstart_desc_force(struct detect_info *di, int type) ++{ ++ if (type >= CIDSTART_RING && type < MAX_CIDSTART) { ++ di->cidstart_type = type; ++ di->param.cidstart = cidstart_desc[type]; ++ } ++} ++ ++static void set_cidstart_desc(struct detect_info *di, int type) ++{ ++ if (!di->cidstart_type) { ++ /* Cidstart signal has not yet appeared */ ++ if (type >= CIDSTART_RING && type < MAX_CIDSTART) { ++ di->cidstart_type = type; ++ di->param.cidstart = cidstart_desc[type]; ++ } ++ } ++} ++ ++void set_cidstart_desc_from_chan_num(int spanno, int channo, int cid_state) ++{ ++ int type; ++ struct detect_info *di; ++ ++ if(cid_state == CID_STATE_IDLE) { ++ type = CIDSTART_POLARITY; ++ } else { ++ type = CIDSTART_POLARITY_IN; ++ } ++ ++ di = get_detect_info_from_chan_num(spanno, channo); ++ if (di) { ++ set_cidstart_desc(di, type); ++ } ++} ++ ++int is_callerid_disable(int spanno, int channo) ++{ ++ int res = 0; ++ struct detect_info *di; ++ ++ di = get_detect_info_from_chan_num(spanno, channo); ++ if (di) { ++ res = di->param.disable; ++ } ++ ++ return res; ++} ++ ++static void print_cidinfo(u8 *number, u8 *name) ++{ ++ struct timeval tv; ++ int mon, mday, hour, min; ++ ++ do_gettimeofday(&tv); ++ get_systime_val(&mon, &mday, &hour, &min); ++ printk(KERN_INFO "cid infor: %02d-%02d %02d:%02d number=%s, name=%s\n", mon, mday, hour, min, number, name); ++} ++ ++static void check_callerid_signal(struct detect_info *di, int timeout) ++{ ++ int i, res = 0; ++ callerid_fsk_t *fsk; ++ u8 name[MAX_CID_LEN + 1]; ++ u8 number[MAX_CID_LEN + 1]; ++ callerid_t *cid = &di->cid; ++ ++ if (cid->appear) { ++ /* Last time we have found signal of caller id, directly to exit */ ++ return; ++ } ++ memset(name, 0, sizeof(name)); ++ memset(number, 0, sizeof(number)); ++ if (cid->dtmf.num > 1) { ++ if (cid->dtmf.timeout >= timeout) { ++ get_dtmf_number(&cid->dtmf, number); ++ cid->cur_sig = CALLERID_DTMF; ++ set_cidstart_desc(di, CIDSTART_DTMF); ++ res = 1; ++ } ++ } else { ++ for (i = 0; i < MAX_FSK_NUM; i++) { ++ fsk = &cid->fsk[i]; ++ if (strlen(fsk->number)) { ++ memcpy(number, fsk->number, MAX_CID_LEN); ++ memcpy(name, fsk->name, MAX_CID_LEN); ++ if (cid->dtmf.num == 1) { ++ set_cidstart_desc_force(di, CIDSTART_DTMF); ++ } ++ cid->cur_sig = i; ++ res = 1; ++ break; ++ } ++ } ++ } ++ ++ if (res) { ++ if (strlen(number)) { ++ cid->gw.msg_len = build_msg(cid->gw.msg, number, name); ++ cid->appear = 1; ++ set_signal_desc(di); ++ if (cid_debug) { ++ print_cidinfo(number, name); ++ } ++ } ++ } ++} ++ ++void reset_parser_variable_from_chan_num(int spanno, int channo) ++{ ++ struct detect_info *di; ++ ++ di = get_detect_info_from_chan_num(spanno, channo); ++ if (di) { ++ reset_parser_variable_result(&di->cid); ++ } ++} ++ ++static int generate_callerid(callerid_t *cid, short s[], int len) ++{ ++ if (fsk_gen(&cid->gw, s, len) == -1) { ++ /* Signal of caller id stop transmission */ ++ reset_parser_variable_result(cid); ++ return 0; ++ } else { ++ return 1; ++ } ++} ++ ++void parser_callerid_process(struct a24xx *wc, int cidbuflen, int cidtimeout) ++{ ++ int i, j; ++ /* Analytical work to ensure that within the ring tones */ ++#define LENGTH_PER_PTR (10 * DAHDI_CHUNKSIZE) ++ short data[LENGTH_PER_PTR]; ++ struct a24xx_dev *wc_dev = &wc->dev; ++ struct dahdi_chan *chan; ++ struct detect_info *di; ++ ++ for (i = 0; i < wc_dev->max_cards; i++) { ++ if (wc_dev->modtype[i] == MOD_TYPE_FXO && !wc_dev->mod[i].fxo.offhook) { ++ chan = wc->chans[i]; ++ if (wc_dev->cid_state[i] == CID_STATE_IDLE || ++ wc_dev->cid_state[i] == CID_STATE_RING_DELAY) { ++ /* We need copy data to the caller id voice buffer */ ++ memcpy(wc_dev->cid_history_buf[i] + wc_dev->cid_history_ptr[i], chan->readchunk, DAHDI_CHUNKSIZE); ++ wc_dev->cid_history_ptr[i] = (wc_dev->cid_history_ptr[i] + DAHDI_CHUNKSIZE)%(cidbuflen * DAHDI_MAX_CHUNKSIZE); ++ di = get_detect_info_from_chan_num(wc->span.spanno, chan->channo); ++ if (di && !di->param.disable) { ++ /* Empty data to prevent interference */ ++ memset(chan->readchunk, DAHDI_LIN2X(0, chan), DAHDI_CHUNKSIZE); ++ } ++ } else if (wc_dev->cid_state[i] == CID_STATE_RING_ON) { ++ di = get_detect_info_from_chan_num(wc->span.spanno, chan->channo); ++ if (di) { ++ if (wc_dev->cid_history_clone_cnt[i] > 0) { ++ for (j = 0; j < LENGTH_PER_PTR; j++) { ++ data[j] = DAHDI_XLAW(*(u8 *)(wc_dev->cid_history_buf[i] + wc_dev->cid_history_ptr[i] + j), chan); ++ } ++ analysis_current_signal(&di->cid, data, LENGTH_PER_PTR); ++ wc_dev->cid_history_clone_cnt[i] -= (LENGTH_PER_PTR / DAHDI_CHUNKSIZE); ++ if (wc_dev->cid_history_clone_cnt[i] < 0) { ++ wc_dev->cid_history_clone_cnt[i] = 0; ++ } ++ wc_dev->cid_history_ptr[i] = (wc_dev->cid_history_ptr[i] + LENGTH_PER_PTR)%(cidbuflen * DAHDI_MAX_CHUNKSIZE); ++ } else if (wc_dev->cid_history_clone_cnt[i] == 0) { ++ check_callerid_signal(di, 0); ++ wc_dev->cid_history_clone_cnt[i] = -1; ++ } ++ } ++ } else if (wc_dev->cid_state[i] == CID_STATE_RING_OFF) { ++ di = get_detect_info_from_chan_num(wc->span.spanno, chan->channo); ++ if (di) { ++ if (di->cid.appear) { ++ set_cidstart_desc(di, CIDSTART_RING); ++ if (generate_callerid(&di->cid, data, DAHDI_CHUNKSIZE)) { ++ if (!di->param.disable) { ++ /* Create effective caller id signal */ ++ for (j = 0; j < DAHDI_CHUNKSIZE; j++) { ++ chan->readchunk[j] = DAHDI_LIN2X(data[j], chan); ++ } ++ } ++ } else { ++ clear_cidstart_type(di); ++ wc_dev->cid_state[i] = CID_STATE_WAIT_RING_FINISH; ++ wc_dev->cid_history_clone_cnt[i] = cidtimeout; ++ } ++ } else { ++ for (j = 0; j < DAHDI_CHUNKSIZE; j++) { ++ data[j] = DAHDI_XLAW(chan->readchunk[j], chan); ++ if (!di->param.disable) { ++ /* Empty data to prevent repeated parsing */ ++ chan->readchunk[j] = DAHDI_LIN2X(0, chan); ++ } ++ } ++ analysis_current_signal(&di->cid, data, DAHDI_CHUNKSIZE); ++ check_callerid_signal(di, DTMF_TIMEOUT_SAMPLES); ++ } /* if (di->cid.appear) */ ++ } ++ } else if (wc_dev->cid_state[i] == CID_STATE_WAIT_RING_FINISH) { ++ if (wc_dev->cid_history_clone_cnt[i] > 0) { ++ wc_dev->cid_history_clone_cnt[i]--; ++ } else { ++ wc_dev->cid_state[i] = CID_STATE_IDLE; ++ } ++ } ++ } ++ } ++} ++ ++static void reset_parser_variable_result(callerid_t *cid) ++{ ++ int i; ++ callerid_dtmf_t *dtmf; ++ callerid_fsk_t *fsk; ++ gen_wave_t *gw; ++ ++ /* Generate the signal reset */ ++ cid->appear = 0; ++ ++ /* Reset the dtmf analytical variables and results */ ++ dtmf = &cid->dtmf; ++ memset(dtmf, 0, sizeof(*dtmf)); ++ for (i = 0; i < 4; i++) { ++ dtmf->row[i].fac = dtmf_fac[i]; ++ dtmf->col[i].fac = dtmf_fac[4 + i]; ++ } ++ ++ /* Reset the variety of fsk analytical variables and results */ ++ for (i = 0; i < MAX_FSK_NUM; i++) { ++ fsk = &cid->fsk[i]; ++ memset(fsk, 0, sizeof(*fsk)); ++ fsk->baud_rate = 1200; ++ fsk->span = SAMPLE_PER_SEC / fsk->baud_rate; ++ if (i == CALLERID_BELL202_OR_V23) { ++ fsk->phase_rate[0] = get_phase_rate(2200); ++ fsk->phase_rate[1] = get_phase_rate(1200); ++ } else { ++ fsk->phase_rate[0] = get_phase_rate(2100); ++ fsk->phase_rate[1] = get_phase_rate(1300); ++ } ++ } ++ ++ /* Reset the waveform analytical variables and results */ ++ fsk = &cid->fsk[CALLERID_BELL202_OR_V23]; ++ gw = &cid->gw; ++ memset(gw, 0, sizeof(*gw)); ++ gw->occupied_len = 300; ++ gw->premark_len = 180; ++ gw->postmark_len = 60; ++ gw->scaling = 2873; /* pow(10.0,(level-3.14)/20.0)*32767.0, level=-14 */ ++ gw->baud_rate = fsk->baud_rate; ++ gw->phase_rates[0] = fsk->phase_rate[0]; ++ gw->phase_rates[1] = fsk->phase_rate[1]; ++} ++ ++static void init_callerid_info(struct detect_info *di, int channo) ++{ ++ /* Save channel number */ ++ di->channo = channo; ++ ++ INIT_LIST_HEAD(&di->list); ++ ++ /* Initial description of the parameters are unknown */ ++ di->param.detect_signal = unknown_desc; ++ di->param.last_signal = unknown_desc; ++ di->param.cidstart = unknown_desc; ++ ++ /* Initial signal is unknown */ ++ di->cid.cur_sig = UNKNOWN_CID_SIGNAL; ++ ++ /* Initialize dtmf, variety of fsk, waveform parameters */ ++ reset_parser_variable_result(&di->cid); ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) ++static int read_param_proc(char *buf, char **start, off_t off, int count, ++ int *eof, void *data) ++{ ++ int res; ++ const char **p = (const char **)data; ++ ++ if (off > 0) { ++ /* We have finished to read, return 0 */ ++ res = 0; ++ } else { ++ res = sprintf(buf, "%s", *p); ++ } ++ ++ return res; ++} ++ ++static void create_param_proc(const char *name, struct proc_dir_entry *base, void *data) ++{ ++ struct proc_dir_entry *entry; ++ ++ entry = create_proc_entry(name, 0444, base); ++ if (entry) { ++ entry->data = (void *)data; ++ entry->read_proc = read_param_proc; ++ } ++} ++#else ++static int param_proc_show(struct seq_file *m, void *v) ++{ ++ const char **p = (const char **)m->private; ++ ++ seq_printf(m, "%s", *p); ++ return 0; ++} ++ ++static int open_param_proc(struct inode *inode, struct file *file) ++{ ++ return single_open(file, param_proc_show, PDE_DATA(inode)); ++} ++ ++static struct file_operations proc_param_fops = { ++ .open = open_param_proc, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static void create_param_proc(const char *name, struct proc_dir_entry *base, void *data) ++{ ++ proc_create_data(name, 0444, base, &proc_param_fops, data); ++} ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) ++static int write_param_off_proc(struct file *file, const char __user *buf, ++ unsigned long count, void *data) ++#else ++static ssize_t write_param_off_proc(struct file *file, const char __user *buf, ++ size_t count, loff_t *pos) ++#endif ++{ ++ char temp[24]; ++ int newval, len; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) ++ u8 *val = (u8 *)data; ++#else ++ u8 *val = PDE_DATA(file_inode(file)); ++#endif ++ ++ len = count > (sizeof(temp) - 1) ? (sizeof(temp) - 1) : count; ++ ++ if (copy_from_user(temp, buf, len)) { ++ return -EFAULT; ++ } ++ ++ temp[len] = '\0'; ++ ++ newval = simple_strtoul(temp, NULL, 10); ++ *val = newval > 0 ? 1 : 0; ++ ++ return count; ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) ++static int read_param_off_proc(char *buf, char **start, off_t off, int count, ++ int *eof, void *data) ++{ ++ int res; ++ u8 *val = (u8 *)data; ++ ++ if (off > 0) { ++ /* We have finished to read, return 0 */ ++ res = 0; ++ } else { ++ res = sprintf(buf, "%d", *val); ++ } ++ ++ return res; ++} ++ ++static void create_param_off_proc(const char *name, struct proc_dir_entry *base, void *data) ++{ ++ struct proc_dir_entry *entry; ++ ++ entry = create_proc_entry(name, 0644, base); ++ if (entry) { ++ entry->data = data; ++ entry->read_proc = read_param_off_proc; ++ entry->write_proc = write_param_off_proc; ++ } ++} ++#else ++static int param_off_proc_show(struct seq_file *m, void *v) ++{ ++ u8 *val = (u8 *)m->private; ++ ++ seq_printf(m, "%d", *val); ++ return 0; ++} ++ ++static int open_param_off_proc(struct inode *inode, struct file *file) ++{ ++ return single_open(file, param_off_proc_show, PDE_DATA(inode)); ++} ++ ++static struct file_operations proc_param_off_fops = { ++ .open = open_param_off_proc, ++ .read = seq_read, ++ .write = write_param_off_proc, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static void create_param_off_proc(const char *name, struct proc_dir_entry *base, void *data) ++{ ++ proc_create_data(name, 0644, base, &proc_param_off_fops, data); ++} ++#endif ++ ++/* ++ * \brief parameter ++ * is_clean: 0 is to create, 1 is to remove ++ */ ++static void rebuild_callerid_proc(struct detect_info *di, int is_clean) ++{ ++ char temp[24]; ++ struct proc_dir_entry *entry; ++ ++ if (is_clean) { ++ entry = di->entry; ++ if (entry) { ++ remove_proc_entry("last_signal", entry); ++ remove_proc_entry("detect_signal", entry); ++ remove_proc_entry("cidstart", entry); ++ remove_proc_entry("disable", entry); ++ ++ sprintf(temp, "%s/%d/opencid", module_name, di->channo); ++ remove_proc_entry(temp, NULL); ++ } ++ } else { ++ sprintf(temp, "%s/%d/opencid", module_name, di->channo); ++ entry = proc_mkdir(temp, NULL); ++ di->entry = entry; ++ if (entry) { ++ create_param_proc("last_signal", entry, &di->param.last_signal); ++ create_param_proc("detect_signal", entry, &di->param.detect_signal); ++ create_param_proc("cidstart", entry, &di->param.cidstart); ++ create_param_off_proc("disable", entry, &di->param.disable); ++ } ++ } ++} ++ ++static void release_callerid_resource(struct a24xx *wc) ++{ ++ int i; ++ struct detect_info *cur, *next; ++ struct a24xx_dev *wc_dev = &wc->dev; ++ ++ if (wc->span.spanno <= 0 || wc->span.spanno > MAX_LIST_SPAN) { ++ return; ++ } ++ ++ for (i = 0; i < wc_dev->max_cards; i++) { ++ if (wc_dev->modtype[i] == MOD_TYPE_FXO) { ++ list_for_each_entry_safe(cur, next, &di_list[wc->span.spanno - 1], list) { ++ if (cur->channo == wc->chans[i]->channo) { ++ /* Find the corresponding matching */ ++ list_del(&cur->list); ++ rebuild_callerid_proc(cur, 1); ++ kfree(cur); ++ break; ++ } ++ } ++ } ++ } ++} ++ ++/* Called after created the top-level parameters directory "module_name" */ ++int init_callerid(struct a24xx *wc) ++{ ++ int i, res = 0; ++ struct detect_info *di; ++ struct a24xx_dev *wc_dev = &wc->dev; ++ static int first_in = 1; ++ ++ if (first_in) { ++ first_in = 0; ++ for (i = 0; i < MAX_LIST_SPAN; i++) { ++ INIT_LIST_HEAD(&di_list[i]); ++ } ++ } ++ ++ if (wc->span.spanno <= 0 || wc->span.spanno > MAX_LIST_SPAN) { ++ return -ENXIO; ++ } ++ ++ for (i = 0; i < wc_dev->max_cards; i++) { ++ if (wc_dev->modtype[i] == MOD_TYPE_FXO) { ++ di = kzalloc(sizeof(*di), GFP_KERNEL); ++ if (!di) { ++ printk(KERN_ERR "Not enough memory, A2410P not support the calling identity delivery analysis"); ++ res = -ENOMEM; ++ goto out; ++ } ++ ++ init_callerid_info(di, wc->chans[i]->channo); ++ rebuild_callerid_proc(di, 0); ++ ++ list_add(&di->list, &di_list[wc->span.spanno - 1]); ++ } ++ } ++ ++ return 0; ++out: ++ release_callerid_resource(wc); ++ return res; ++} ++ ++/* Called before the release of the top-level parameters directory "module_name" */ ++void destroy_callerid(struct a24xx *wc) ++{ ++ release_callerid_resource(wc); ++} +--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/ec3000.c 1970-01-01 01:00:00.000000000 +0100 ++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/ec3000.c 2015-02-10 14:19:03.000000000 +0100 +@@ -0,0 +1,589 @@ ++/* ++ * Copyright (C) 2005-2006 Digium, Inc. ++ * ++ * Mark Spencer <markster@digium.com> ++ * Mark liu <mark.liu@openvox.cn> ++ * ++ * $Id: ec3000.c 159 2010-12-08 03:27:04Z liuyuan $ ++ * All Rights Reserved ++ */ ++ ++/* ++ * See http://www.asterisk.org for more information about ++ * the Asterisk project. Please do not directly contact ++ * any of the maintainers of this project for assistance; ++ * the project provides a web site, mailing lists and IRC ++ * channels for your use. ++ * ++ * This program is free software, distributed under the terms of ++ * the GNU General Public License Version 2 as published by the ++ * Free Software Foundation. See the LICENSE file included with ++ * this program for more details. ++ */ ++ ++#include <linux/slab.h> ++#include <linux/vmalloc.h> ++#include <linux/string.h> ++#include <linux/time.h> ++#include <linux/version.h> ++ ++#include "ec3000.h" ++#include "oct6100api/oct6100_api.h" ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) ++#include <linux/config.h> ++#endif ++ ++/* API for Octasic access */ ++UINT32 Oct6100UserGetTime(tPOCT6100_GET_TIME f_pTime) ++{ ++ /* Why couldn't they just take a timeval like everyone else? */ ++ struct timeval tv; ++ unsigned long long total_usecs; ++ unsigned int mask = ~0; ++ ++ do_gettimeofday(&tv); ++ total_usecs = (((unsigned long long)(tv.tv_sec)) * 1000000) + ++ (((unsigned long long)(tv.tv_usec))); ++ f_pTime->aulWallTimeUs[0] = (total_usecs & mask); ++ f_pTime->aulWallTimeUs[1] = (total_usecs >> 32); ++ return cOCT6100_ERR_OK; ++} ++ ++UINT32 Oct6100UserMemSet(PVOID f_pAddress, UINT32 f_ulPattern, UINT32 f_ulLength) ++{ ++ memset(f_pAddress, f_ulPattern, f_ulLength); ++ return cOCT6100_ERR_OK; ++} ++ ++UINT32 Oct6100UserMemCopy(PVOID f_pDestination, const void *f_pSource, UINT32 f_ulLength) ++{ ++ memcpy(f_pDestination, f_pSource, f_ulLength); ++ return cOCT6100_ERR_OK; ++} ++ ++UINT32 Oct6100UserCreateSerializeObject(tPOCT6100_CREATE_SERIALIZE_OBJECT f_pCreate) ++{ ++ return cOCT6100_ERR_OK; ++} ++ ++UINT32 Oct6100UserDestroySerializeObject(tPOCT6100_DESTROY_SERIALIZE_OBJECT f_pDestroy) ++{ ++#ifdef OCTASIC_DEBUG ++ printk("I should never be called! (destroy serialize object)\n"); ++#endif ++ return cOCT6100_ERR_OK; ++} ++ ++UINT32 Oct6100UserSeizeSerializeObject(tPOCT6100_SEIZE_SERIALIZE_OBJECT f_pSeize) ++{ ++ /* Not needed */ ++ return cOCT6100_ERR_OK; ++} ++ ++UINT32 Oct6100UserReleaseSerializeObject(tPOCT6100_RELEASE_SERIALIZE_OBJECT f_pRelease) ++{ ++ /* Not needed */ ++ return cOCT6100_ERR_OK; ++} ++ ++UINT32 Oct6100UserDriverWriteApi(tPOCT6100_WRITE_PARAMS f_pWriteParams) ++{ ++ oct_set_reg(f_pWriteParams->pProcessContext, f_pWriteParams->ulWriteAddress, f_pWriteParams->usWriteData); ++ return cOCT6100_ERR_OK; ++} ++ ++UINT32 Oct6100UserDriverWriteSmearApi(tPOCT6100_WRITE_SMEAR_PARAMS f_pSmearParams) ++{ ++ unsigned int x; ++ for (x=0;x<f_pSmearParams->ulWriteLength;x++) { ++ oct_set_reg(f_pSmearParams->pProcessContext, f_pSmearParams->ulWriteAddress + (x << 1), f_pSmearParams->usWriteData); ++ } ++ return cOCT6100_ERR_OK; ++} ++ ++UINT32 Oct6100UserDriverWriteBurstApi(tPOCT6100_WRITE_BURST_PARAMS f_pBurstParams) ++{ ++ unsigned int x; ++ for (x=0;x<f_pBurstParams->ulWriteLength;x++) { ++ oct_set_reg(f_pBurstParams->pProcessContext, f_pBurstParams->ulWriteAddress + (x << 1), f_pBurstParams->pusWriteData[x]); ++ } ++ return cOCT6100_ERR_OK; ++} ++ ++UINT32 Oct6100UserDriverReadApi(tPOCT6100_READ_PARAMS f_pReadParams) ++{ ++ *(f_pReadParams->pusReadData) = oct_get_reg(f_pReadParams->pProcessContext, f_pReadParams->ulReadAddress); ++ return cOCT6100_ERR_OK; ++} ++ ++UINT32 Oct6100UserDriverReadBurstApi(tPOCT6100_READ_BURST_PARAMS f_pBurstParams) ++{ ++ unsigned int x; ++ for (x=0;x<f_pBurstParams->ulReadLength;x++) { ++ f_pBurstParams->pusReadData[x] = oct_get_reg(f_pBurstParams->pProcessContext, f_pBurstParams->ulReadAddress + (x << 1)); ++ } ++ return cOCT6100_ERR_OK; ++} ++ ++#define SOUT_G168_1100GB_ON 0x40000004 ++#define SOUT_DTMF_1 0x40000011 ++#define SOUT_DTMF_2 0x40000012 ++#define SOUT_DTMF_3 0x40000013 ++#define SOUT_DTMF_A 0x4000001A ++#define SOUT_DTMF_4 0x40000014 ++#define SOUT_DTMF_5 0x40000015 ++#define SOUT_DTMF_6 0x40000016 ++#define SOUT_DTMF_B 0x4000001B ++#define SOUT_DTMF_7 0x40000017 ++#define SOUT_DTMF_8 0x40000018 ++#define SOUT_DTMF_9 0x40000019 ++#define SOUT_DTMF_C 0x4000001C ++#define SOUT_DTMF_STAR 0x4000001E ++#define SOUT_DTMF_0 0x40000010 ++#define SOUT_DTMF_POUND 0x4000001F ++#define SOUT_DTMF_D 0x4000001D ++ ++#define ROUT_G168_2100GB_ON 0x10000000 ++#define ROUT_G168_2100GB_WSPR 0x10000002 ++#define ROUT_SOUT_G168_2100HB_END 0x50000003 ++#define ROUT_G168_1100GB_ON 0x10000004 ++ ++#define ROUT_DTMF_1 0x10000011 ++#define ROUT_DTMF_2 0x10000012 ++#define ROUT_DTMF_3 0x10000013 ++#define ROUT_DTMF_A 0x1000001A ++#define ROUT_DTMF_4 0x10000014 ++#define ROUT_DTMF_5 0x10000015 ++#define ROUT_DTMF_6 0x10000016 ++#define ROUT_DTMF_B 0x1000001B ++#define ROUT_DTMF_7 0x10000017 ++#define ROUT_DTMF_8 0x10000018 ++#define ROUT_DTMF_9 0x10000019 ++#define ROUT_DTMF_C 0x1000001C ++#define ROUT_DTMF_STAR 0x1000001E ++#define ROUT_DTMF_0 0x10000010 ++#define ROUT_DTMF_POUND 0x1000001F ++#define ROUT_DTMF_D 0x1000001D ++ ++#if 0 ++#define cOCT6100_ECHO_OP_MODE_DIGITAL cOCT6100_ECHO_OP_MODE_HT_FREEZE ++#else ++#define cOCT6100_ECHO_OP_MODE_DIGITAL cOCT6100_ECHO_OP_MODE_POWER_DOWN ++#endif ++ ++struct ec { ++ tPOCT6100_INSTANCE_API pApiInstance; ++ UINT32 aulEchoChanHndl[ 128 ]; ++ int chanflags[128]; ++ int ecmode[128]; ++ int numchans; ++}; ++ ++#define FLAG_DTMF (1 << 0) ++#define FLAG_MUTE (1 << 1) ++#define FLAG_ECHO (1 << 2) ++ ++static unsigned int tones[] = { ++ SOUT_DTMF_1, ++ SOUT_DTMF_2, ++ SOUT_DTMF_3, ++ SOUT_DTMF_A, ++ SOUT_DTMF_4, ++ SOUT_DTMF_5, ++ SOUT_DTMF_6, ++ SOUT_DTMF_B, ++ SOUT_DTMF_7, ++ SOUT_DTMF_8, ++ SOUT_DTMF_9, ++ SOUT_DTMF_C, ++ SOUT_DTMF_STAR, ++ SOUT_DTMF_0, ++ SOUT_DTMF_POUND, ++ SOUT_DTMF_D, ++ SOUT_G168_1100GB_ON, ++ ++ ROUT_DTMF_1, ++ ROUT_DTMF_2, ++ ROUT_DTMF_3, ++ ROUT_DTMF_A, ++ ROUT_DTMF_4, ++ ROUT_DTMF_5, ++ ROUT_DTMF_6, ++ ROUT_DTMF_B, ++ ROUT_DTMF_7, ++ ROUT_DTMF_8, ++ ROUT_DTMF_9, ++ ROUT_DTMF_C, ++ ROUT_DTMF_STAR, ++ ROUT_DTMF_0, ++ ROUT_DTMF_POUND, ++ ROUT_DTMF_D, ++ ROUT_G168_1100GB_ON, ++}; ++static void opvx_vpm_setecmode(struct ec *ec, int channel, int mode) ++{ ++ tOCT6100_CHANNEL_MODIFY *modify; ++ UINT32 ulResult; ++ ++ if (ec->ecmode[channel] == mode) ++ return; ++ modify = kmalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_ATOMIC); ++ if (!modify) { ++ printk("opvxa24xx: Unable to allocate memory for setec!\n"); ++ return; ++ } ++ Oct6100ChannelModifyDef(modify); ++ modify->ulEchoOperationMode = mode; ++ modify->ulChannelHndl = ec->aulEchoChanHndl[channel]; ++ ulResult = Oct6100ChannelModify(ec->pApiInstance, modify); ++ if (ulResult != GENERIC_OK) { ++ printk("Failed to apply echo can changes on channel %d, 0x%x!\n", channel, ulResult); ++ } else { ++#ifdef OCTASIC_DEBUG ++ printk("Echo can on channel %d set to %d\n", channel, mode); ++#endif ++ ec->ecmode[channel] = mode; ++ } ++ kfree(modify); ++} ++ ++void opvx_vpm_setdtmf(struct ec *ec, int channel, int detect, int mute) ++{ ++ tOCT6100_CHANNEL_MODIFY *modify; ++ UINT32 ulResult; ++ ++ modify = kmalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_KERNEL); ++ if (!modify) { ++ printk("opvxa24xx: Unable to allocate memory for setdtmf!\n"); ++ return; ++ } ++ Oct6100ChannelModifyDef(modify); ++ modify->ulChannelHndl = ec->aulEchoChanHndl[channel]; ++ if (mute) { ++ ec->chanflags[channel] |= FLAG_MUTE; ++ modify->VqeConfig.fDtmfToneRemoval = TRUE; ++ } else { ++ ec->chanflags[channel] &= ~FLAG_MUTE; ++ modify->VqeConfig.fDtmfToneRemoval = FALSE; ++ } ++ if (detect) ++ ec->chanflags[channel] |= FLAG_DTMF; ++ else ++ ec->chanflags[channel] &= ~FLAG_DTMF; ++ if (ec->chanflags[channel] & (FLAG_DTMF|FLAG_MUTE)) { ++ if (!(ec->chanflags[channel] & FLAG_ECHO)) { ++ opvx_vpm_setecmode(ec, channel, cOCT6100_ECHO_OP_MODE_HT_RESET); ++ opvx_vpm_setecmode(ec, channel, cOCT6100_ECHO_OP_MODE_HT_FREEZE); ++ } ++ } else { ++ if (!(ec->chanflags[channel] & FLAG_ECHO)) ++ opvx_vpm_setecmode(ec, channel, cOCT6100_ECHO_OP_MODE_DIGITAL); ++ } ++ ++ ulResult = Oct6100ChannelModify(ec->pApiInstance, modify); ++ if (ulResult != GENERIC_OK) { ++ printk("Failed to apply dtmf mute changes on channel %d!\n", channel); ++ } ++/* printk("VPM450m: Setting DTMF on channel %d: %s / %s\n", channel, (detect ? "DETECT" : "NO DETECT"), (mute ? "MUTE" : "NO MUTE")); */ ++ kfree(modify); ++} ++ ++ ++void opvx_vpm_setec(struct ec *ec, int channel, int eclen) ++{ ++ if (eclen) { ++ ec->chanflags[channel] |= FLAG_ECHO; ++ opvx_vpm_setecmode(ec, channel, cOCT6100_ECHO_OP_MODE_HT_RESET); ++ opvx_vpm_setecmode(ec, channel, cOCT6100_ECHO_OP_MODE_NORMAL); ++ } else { ++ ec->chanflags[channel] &= ~FLAG_ECHO; ++ if (ec->chanflags[channel] & (FLAG_DTMF | FLAG_MUTE)) { ++ opvx_vpm_setecmode(ec, channel, cOCT6100_ECHO_OP_MODE_HT_RESET); ++ opvx_vpm_setecmode(ec, channel, cOCT6100_ECHO_OP_MODE_HT_FREEZE); ++ } else ++ opvx_vpm_setecmode(ec, channel, cOCT6100_ECHO_OP_MODE_DIGITAL); ++ } ++/* printk("VPM450m: Setting EC on channel %d to %d\n", channel, eclen); */ ++} ++ ++int opvx_vpm_checkirq(struct ec *ec) ++{ ++ tOCT6100_INTERRUPT_FLAGS InterruptFlags; ++ ++ Oct6100InterruptServiceRoutineDef(&InterruptFlags); ++ Oct6100InterruptServiceRoutine(ec->pApiInstance, &InterruptFlags); ++ ++ return InterruptFlags.fToneEventsPending ? 1 : 0; ++} ++ ++int opvx_vpm_getdtmf(struct ec *ec, int *channel, int *tone, int *start) ++{ ++ tOCT6100_TONE_EVENT tonefound; ++ tOCT6100_EVENT_GET_TONE tonesearch; ++ UINT32 ulResult; ++ ++ Oct6100EventGetToneDef(&tonesearch); ++ tonesearch.pToneEvent = &tonefound; ++ tonesearch.ulMaxToneEvent = 1; ++ ulResult = Oct6100EventGetTone(ec->pApiInstance, &tonesearch); ++ if (tonesearch.ulNumValidToneEvent) { ++ if (channel) ++ *channel = tonefound.ulUserChanId; ++ if (tone) { ++ switch(tonefound.ulToneDetected) { ++ case SOUT_DTMF_1: ++ *tone = '1'; ++ break; ++ case SOUT_DTMF_2: ++ *tone = '2'; ++ break; ++ case SOUT_DTMF_3: ++ *tone = '3'; ++ break; ++ case SOUT_DTMF_A: ++ *tone = 'A'; ++ break; ++ case SOUT_DTMF_4: ++ *tone = '4'; ++ break; ++ case SOUT_DTMF_5: ++ *tone = '5'; ++ break; ++ case SOUT_DTMF_6: ++ *tone = '6'; ++ break; ++ case SOUT_DTMF_B: ++ *tone = 'B'; ++ break; ++ case SOUT_DTMF_7: ++ *tone = '7'; ++ break; ++ case SOUT_DTMF_8: ++ *tone = '8'; ++ break; ++ case SOUT_DTMF_9: ++ *tone = '9'; ++ break; ++ case SOUT_DTMF_C: ++ *tone = 'C'; ++ break; ++ case SOUT_DTMF_STAR: ++ *tone = '*'; ++ break; ++ case SOUT_DTMF_0: ++ *tone = '0'; ++ break; ++ case SOUT_DTMF_POUND: ++ *tone = '#'; ++ break; ++ case SOUT_DTMF_D: ++ *tone = 'D'; ++ break; ++ case SOUT_G168_1100GB_ON: ++ *tone = 'f'; ++ break; ++ default: ++#ifdef OCTASIC_DEBUG ++ printk("Unknown tone value %08x\n", tonefound.ulToneDetected); ++#endif ++ *tone = 'u'; ++ break; ++ } ++ } ++ if (start) ++ *start = (tonefound.ulEventType == cOCT6100_TONE_PRESENT); ++ return 1; ++ } ++ return 0; ++} ++ ++unsigned int opvx_vpm_getcapacity(void *wc) ++{ ++ UINT32 ulResult; ++ ++ tOCT6100_API_GET_CAPACITY_PINS CapacityPins; ++ ++ Oct6100ApiGetCapacityPinsDef(&CapacityPins); ++ CapacityPins.pProcessContext = wc; ++ CapacityPins.ulMemoryType = cOCT6100_MEM_TYPE_DDR; ++ CapacityPins.fEnableMemClkOut = TRUE; ++ CapacityPins.ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ; ++ ++ ulResult = Oct6100ApiGetCapacityPins(&CapacityPins); ++ if (ulResult != cOCT6100_ERR_OK) { ++ printk("Failed to get chip capacity, code %08x!\n", ulResult); ++ return 0; ++ } ++ ++ return CapacityPins.ulCapacityValue; ++} ++ ++struct ec *opvx_vpm_init(void *wc, int *isalaw, int numspans, const struct firmware *firmware) ++{ ++ tOCT6100_CHIP_OPEN *ChipOpen; ++ tOCT6100_GET_INSTANCE_SIZE InstanceSize; ++ tOCT6100_CHANNEL_OPEN *ChannelOpen; ++ UINT32 ulResult; ++ struct ec *ec; ++ int x,y,law; ++#ifdef CONFIG_4KSTACKS ++ unsigned long flags; ++#endif ++ ++ if (!(ec = kmalloc(sizeof(struct ec), GFP_KERNEL))) ++ return NULL; ++ ++ memset(ec, 0, sizeof(struct ec)); ++ ++ if (!(ChipOpen = kmalloc(sizeof(tOCT6100_CHIP_OPEN), GFP_KERNEL))) { ++ kfree(ec); ++ return NULL; ++ } ++ ++ memset(ChipOpen, 0, sizeof(tOCT6100_CHIP_OPEN)); ++ ++ if (!(ChannelOpen = kmalloc(sizeof(tOCT6100_CHANNEL_OPEN), GFP_KERNEL))) { ++ kfree(ec); ++ kfree(ChipOpen); ++ return NULL; ++ } ++ ++ memset(ChannelOpen, 0, sizeof(tOCT6100_CHANNEL_OPEN)); ++ ++ for (x=0;x<128;x++) ++ ec->ecmode[x] = -1; ++ ++ ec->numchans = numspans * 32; ++ printk("OpenVox VPM: echo cancellation for %d channels\n", ec->numchans); ++ ++ Oct6100ChipOpenDef(ChipOpen); ++ ++ /* Setup Chip Open Parameters */ ++ ChipOpen->ulUpclkFreq = cOCT6100_UPCLK_FREQ_33_33_MHZ; ++ Oct6100GetInstanceSizeDef(&InstanceSize); ++ ++ ChipOpen->pProcessContext = wc; ++ ++ ChipOpen->pbyImageFile = firmware->data; ++ ChipOpen->ulImageSize = firmware->size; ++ ChipOpen->fEnableMemClkOut = TRUE; ++ ChipOpen->ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ; ++ ChipOpen->ulMaxChannels = ec->numchans; ++ ChipOpen->ulMemoryType = cOCT6100_MEM_TYPE_DDR; ++ ChipOpen->ulMemoryChipSize = cOCT6100_MEMORY_CHIP_SIZE_32MB; ++ ChipOpen->ulNumMemoryChips = 1; ++ ChipOpen->ulMaxTdmStreams = 4; ++ ChipOpen->aulTdmStreamFreqs[0] = cOCT6100_TDM_STREAM_FREQ_8MHZ; ++ ChipOpen->ulTdmSampling = cOCT6100_TDM_SAMPLE_AT_FALLING_EDGE; ++#if 0 ++ ChipOpen->fEnableAcousticEcho = TRUE; ++#endif ++ ++ ulResult = Oct6100GetInstanceSize(ChipOpen, &InstanceSize); ++ if (ulResult != cOCT6100_ERR_OK) { ++ printk("Failed to get instance size, code %08x!\n", ulResult); ++ kfree(ec); ++ kfree(ChipOpen); ++ kfree(ChannelOpen); ++ return NULL; ++ } ++ ++ ++ ec->pApiInstance = vmalloc(InstanceSize.ulApiInstanceSize); ++ if (!ec->pApiInstance) { ++ printk("Out of memory (can't allocate %d bytes)!\n", InstanceSize.ulApiInstanceSize); ++ kfree(ec); ++ kfree(ChipOpen); ++ kfree(ChannelOpen); ++ return NULL; ++ } ++ ++ /* I don't know what to curse more in this comment, the problems caused by ++ * the 4K kernel stack limit change or the octasic API for being so darn ++ * stack unfriendly. Stupid, stupid, stupid. So we disable IRQs so we ++ * don't run the risk of overflowing the stack while we initialize the ++ * octasic. */ ++#ifdef CONFIG_4KSTACKS ++ local_irq_save(flags); ++#endif ++ ulResult = Oct6100ChipOpen(ec->pApiInstance, ChipOpen); ++ if (ulResult != cOCT6100_ERR_OK) { ++ printk("Failed to open chip, code %08x!\n", ulResult); ++#ifdef CONFIG_4KSTACKS ++ local_irq_restore(flags); ++#endif ++ kfree(ec); ++ kfree(ChipOpen); ++ kfree(ChannelOpen); ++ return NULL; ++ } ++ for (x=0;x<128;x++) { ++ /* execute this loop always on 4 span cards but ++ * on 2 span cards only execute for the channels related to our spans */ ++ //if (( numspans > 2) || ((x & 0x03) <2)) { ++ //if ((x & 0x03) < numspans) { ++ if ( x < ec->numchans) { ++ if (isalaw[x / 24]) // each span have 24 channels, it is spec for the 24 channel card. miaolin ++ law = cOCT6100_PCM_A_LAW; ++ else ++ law = cOCT6100_PCM_U_LAW; ++ Oct6100ChannelOpenDef(ChannelOpen); ++ ChannelOpen->pulChannelHndl = &ec->aulEchoChanHndl[x]; ++ ChannelOpen->ulUserChanId = x; ++ ChannelOpen->TdmConfig.ulRinPcmLaw = law; ++ ChannelOpen->TdmConfig.ulRinStream = 0; ++ ChannelOpen->TdmConfig.ulRinTimeslot = x; ++ ChannelOpen->TdmConfig.ulSinPcmLaw = law; ++ ChannelOpen->TdmConfig.ulSinStream = 1; ++ ChannelOpen->TdmConfig.ulSinTimeslot = x; ++ ChannelOpen->TdmConfig.ulSoutPcmLaw = law; ++ ChannelOpen->TdmConfig.ulSoutStream = 2; ++ ChannelOpen->TdmConfig.ulSoutTimeslot = x; ++ ChannelOpen->TdmConfig.ulRoutPcmLaw = law; ++ ChannelOpen->TdmConfig.ulRoutStream = 3; ++ ChannelOpen->TdmConfig.ulRoutTimeslot = x; ++ ChannelOpen->VqeConfig.fEnableNlp = TRUE; ++ ChannelOpen->VqeConfig.fRinDcOffsetRemoval = TRUE; ++ ChannelOpen->VqeConfig.fSinDcOffsetRemoval = TRUE; ++ ++ ChannelOpen->fEnableToneDisabler = TRUE; ++ ChannelOpen->ulEchoOperationMode = cOCT6100_ECHO_OP_MODE_DIGITAL; ++ ++ ulResult = Oct6100ChannelOpen(ec->pApiInstance, ChannelOpen); ++ if (ulResult != GENERIC_OK) { ++ printk("Failed to open channel %d!\n", x); ++ } ++ for (y=0;y<sizeof(tones) / sizeof(tones[0]); y++) { ++ tOCT6100_TONE_DETECTION_ENABLE enable; ++ Oct6100ToneDetectionEnableDef(&enable); ++ enable.ulChannelHndl = ec->aulEchoChanHndl[x]; ++ enable.ulToneNumber = tones[y]; ++ if (Oct6100ToneDetectionEnable(ec->pApiInstance, &enable) != GENERIC_OK) ++ printk("Failed to enable tone detection on channel %d for tone %d!\n", x, y); ++ } ++ } ++ } ++ ++#ifdef CONFIG_4KSTACKS ++ local_irq_restore(flags); ++#endif ++ kfree(ChipOpen); ++ kfree(ChannelOpen); ++ return ec; ++} ++ ++void opvx_vpm_release(struct ec *ec) ++{ ++ UINT32 ulResult; ++ tOCT6100_CHIP_CLOSE ChipClose; ++ ++ Oct6100ChipCloseDef(&ChipClose); ++ ulResult = Oct6100ChipClose(ec->pApiInstance, &ChipClose); ++ if (ulResult != cOCT6100_ERR_OK) { ++ printk("Failed to close chip, code %08x!\n", ulResult); ++ } ++ vfree(ec->pApiInstance); ++ kfree(ec); ++} ++ +--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/ec3000.h 1970-01-01 01:00:00.000000000 +0100 ++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/ec3000.h 2015-02-10 14:19:03.000000000 +0100 +@@ -0,0 +1,50 @@ ++/* ++ * OpenVox A24xx FXS/FXO Interface Driver for Zapata Telephony interface ++ * ++ * Written by MiaoLin<miaolin@openvox.cn> ++ * Written by mark.liu<mark.liu@openvox.cn> ++ * $Id: ec3000.h 165 2010-12-09 05:38:49Z liuyuan $ ++ * ++ * Copyright (C) 2005-2010 OpenVox Communication Co. Ltd, ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef _EC3000_H_ ++#define _EC3000_H_ ++ ++#include <linux/firmware.h> ++ ++struct ec; ++ ++/* From driver */ ++unsigned int oct_get_reg(void *data, unsigned int reg); ++void oct_set_reg(void *data, unsigned int reg, unsigned int val); ++ ++/* From vpm450m */ ++extern void opvx_vpm_setec(struct ec *ec, int channel, int eclen); ++extern void opvx_vpm_setdtmf(struct ec *ec, int channel, int detect, int mute); ++extern int opvx_vpm_getdtmf(struct ec *ec, int *channel, int *tone, int *start); ++extern int opvx_vpm_checkirq(struct ec *ec); ++extern unsigned int opvx_vpm_getcapacity(void *wc); ++ ++extern struct ec *opvx_vpm_init(void *wc, int *isalaw, int numspans, const struct firmware *firmware); ++extern void opvx_vpm_release(struct ec *ec); ++ ++#endif ++ +--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/private.c 1970-01-01 01:00:00.000000000 +0100 ++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/private.c 2015-02-10 14:19:03.000000000 +0100 +@@ -0,0 +1,513 @@ ++/* ++ * OpenVox A24xx FXS/FXO Interface Driver for Zapata Telephony interface ++ * ++ * Written by MiaoLin<miaolin@openvox.cn> ++ * Written by mark.liu<mark.liu@openvox.cn> ++ * $Id: private.c 446 2011-05-12 04:01:57Z liuyuan $ ++ * ++ * Copyright (C) 2005-2010 OpenVox Communication Co. Ltd, ++ * ++ * 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. ++ * ++ */ ++ ++//#ifdef VPM_SUPPORT ++/* ec debug */ ++extern int ec_debug; ++extern int vpmsupport; ++//#endif ++#define PEDANTIC_OCTASIC_CHECKING ++ ++#define ZT_CHUNKSIZE 8 ++#define ZT_MIN_CHUNKSIZE ZT_CHUNKSIZE ++#define ZT_DEFAULT_CHUNKSIZE ZT_CHUNKSIZE ++#define ZT_MAX_CHUNKSIZE ZT_CHUNKSIZE ++ ++#define MAX_NUM_CARDS 24 ++ ++#define CARDS_PER_MODULE 4 ++#define MOD_TYPE_FXS 0 ++#define MOD_TYPE_FXO 1 ++ ++ ++/* register base address */ ++#define REG_BASE 0x00000000 ++#define PIO_BASE 0x000004e0 ++#define TDM0_BASE 0x00000400 ++#define SPI_PCM_BASE 0x000004a0 ++#define TDM_MEM_BASE 0x00001000 ++#define PCI_BASE 0x00004000 ++ ++/* swap memory offset */ ++#define OPVX_RUN 0x0 ++#define OPVX_FWREADY 0x1 ++#define OPVX_FWVERSION 0x2 ++#define OPVX_DMA_REG 0x3 ++#define OPVX_ERR_REG 0x4 ++#define OPVX_IRQ_CNT_LO 0x5 ++#define OPVX_IRQ_CNT_HI 0x6 ++#define OPVX_BURST_SIZE 0x7 ++#define OPVX_BURST_INTERVAL 0x8 ++#define OPVX_PCI_IRQ_FRQ 0x9 ++#define OPVX_CARD_MASTER 0xa ++#define OPVX_IRQ_COMMAND 0xb ++#define OPVX_VPM_PRESENT 0x12 ++#define V2_OPVX_PIO_DATA 0x1c ++#define OPVX_TEST 0x1f ++#define V2_EC_BASE 0x00000100 ++ ++/* irq status register */ ++#define OPVX_IRQ_STATUS (0x40>>2) /* irq status register */ ++#define OPVX_IRQ_ENABLE (0x50>>2) /* irq status register */ ++ ++/* PIO register offset */ ++#define OPVX_PIO_DATA 0 ++#define OPVX_PIO_DIR 1 ++#define OPVX_PIO_CNTL 2 ++ ++/* SPI register offset */ ++#define OPVX_SPI_IN 0 ++#define OPVX_SPI_OUT 1 ++#define OPVX_SPI_STATUS 2 ++#define OPVX_SPI_CNTL 3 ++#define OPVX_SPI_CS 5 ++/* ec controller base addr */ ++#define EC_BASE 0x00000540 ++ ++#define OPVX_EC_CNTL 0 ++#define OPVX_EC_DATA 1 ++#define OPVX_EC_VPM 2 ++ ++/* echo canceller stuff */ ++#define BIT_EC_ADDR_STAGE (1<<0) ++#define BIT_EC_CS (1<<1) ++#define BIT_EC_WR (1<<2) ++#define BIT_EC_RD (1<<3) ++#define BIT_EC_ALE (1<<4) ++#define BIT_EC_RDY (1<<5) ++#define BIT_EC_DAS (1<<6) ++#define BIT_EC_IRQ (1<<7) ++ ++#define BIT_EC_PRESENT (1<<0) ++ ++void __opvx_a24xx_setcreg(unsigned long mem32, unsigned int offset, unsigned int reg, unsigned int val) ++{ ++ //printk("writing offset %d, reg %d at %d, value %d\n", offset, reg, offset + (reg<<2), val); ++ unsigned int *p = (unsigned int*)(mem32 + offset + (reg<<2)); ++ *p = val; ++} ++ ++unsigned int __opvx_a24xx_getcreg(unsigned long mem32, unsigned int offset, unsigned char reg) ++{ ++ //printk("reding offset %d, reg %d at %d\n", offset, reg, offset + (reg<<2)); ++ volatile unsigned int *p = (unsigned int*)(mem32 + offset + (reg<<2)); ++ return (*p); ++} ++ ++unsigned char __opvx_a24xx_read_8bits(unsigned long mem32) ++{ ++ unsigned int res=0; ++ ++ while((__opvx_a24xx_getcreg(mem32, SPI_PCM_BASE, OPVX_SPI_STATUS)&0x40)!=0x40); ++ __opvx_a24xx_setcreg(mem32, SPI_PCM_BASE, OPVX_SPI_OUT, 0); /* we have to write something so the spi can work */ ++ while( (__opvx_a24xx_getcreg(mem32, SPI_PCM_BASE, OPVX_SPI_STATUS)&0x80) != 0x80); /* wait rx finish */ ++ res = __opvx_a24xx_getcreg(mem32, SPI_PCM_BASE, OPVX_SPI_IN); ++ ++ return res&0xff; ++} ++ ++void __opvx_a24xx_start_dma(unsigned long mem32, unsigned int data) ++{ ++ __opvx_a24xx_setcreg(mem32, REG_BASE, OPVX_DMA_REG, data); ++} ++ ++void __opvx_a24xx_stop_dma(unsigned long mem32) ++{ ++ __opvx_a24xx_setcreg(mem32, REG_BASE, OPVX_DMA_REG, 0xffffffff); // -1 means stop dma. ++} ++ ++void __opvx_a24xx_restart_dma(unsigned long mem32) ++{ ++ /* Reset Master and TDM */ ++ // TODO: do our work here. ++} ++ ++void __opvx_a24xx_reset_tdm(unsigned long mem32) ++{ ++ /* Reset TDM */ ++ //TODO: do our work here; ++} ++ ++void __opvx_a24xx_set_irq_frq(unsigned long mem32,unsigned int frq) ++{ ++ __opvx_a24xx_setcreg(mem32, REG_BASE, OPVX_PCI_IRQ_FRQ,frq); ++} ++void __opvx_a24xx_set_master(unsigned long mem32,unsigned int master) ++{ ++ __opvx_a24xx_setcreg(mem32, REG_BASE, OPVX_CARD_MASTER,master); ++} ++unsigned int __opvx_a24xx_get_master(unsigned long mem32) ++{ ++ return __opvx_a24xx_getcreg(mem32, REG_BASE, OPVX_CARD_MASTER); ++} ++ ++unsigned int __opvx_a24xx_get_version(unsigned long mem32) ++{ ++ return __opvx_a24xx_getcreg(mem32, REG_BASE, OPVX_FWVERSION); ++} ++ ++unsigned int __opvx_a24xx_get_irqstatus(unsigned long mem32) ++{ ++ return __opvx_a24xx_getcreg(mem32, PCI_BASE, OPVX_IRQ_STATUS); ++} ++ ++void __opvx_a24xx_set_irqstatus(unsigned long mem32, unsigned int value) ++{ ++ __opvx_a24xx_setcreg(mem32, PCI_BASE, OPVX_IRQ_STATUS, value); // clear interrupt register. ++} ++ ++void __opvx_a24xx_clear_irqs(unsigned long mem32) ++{ ++ __opvx_a24xx_setcreg(mem32, PCI_BASE, OPVX_IRQ_STATUS, 0xffffffff); /* clear all pending irqs */ ++ __opvx_a24xx_setcreg(mem32, SPI_PCM_BASE, OPVX_SPI_CNTL, 0); /* init spi port */ ++} ++ ++void __opvx_a24xx_enable_interrupts(unsigned long mem32) ++{ ++ __opvx_a24xx_setcreg(mem32, REG_BASE, OPVX_BURST_INTERVAL, 0); ++ __opvx_a24xx_setcreg(mem32, REG_BASE, OPVX_BURST_SIZE, 2); ++ __opvx_a24xx_setcreg(mem32, REG_BASE, OPVX_RUN, 1); ++} ++ ++void __opvx_a24xx_disable_interrupts(unsigned long mem32) ++{ ++ __opvx_a24xx_setcreg(mem32, REG_BASE, OPVX_RUN, 0); ++} ++ ++unsigned int __opvx_a24xx_get_irqcnt_lo(unsigned long mem32) ++{ ++ return __opvx_a24xx_getcreg(mem32, REG_BASE, OPVX_IRQ_CNT_LO); ++} ++ ++void __opvx_a24xx_reset_modules(unsigned long mem32, void (*func)(int), int data) ++{ ++ __opvx_a24xx_setcreg(mem32, PIO_BASE, OPVX_PIO_DIR, 0xffffffff); /* all io as output */ ++ __opvx_a24xx_setcreg(mem32, PIO_BASE, OPVX_PIO_CNTL, 0); /* disable irq */ ++ ++// if(debug) { ++// printk("opvxa24xx: raise reset\n"); ++// } ++ ++ __opvx_a24xx_setcreg(mem32, PIO_BASE, OPVX_PIO_DATA, 0x1); /* GPIO0 As reset*/ ++ /* Wait for 1 second */ ++ ++ (*func)(data/2); /* delay 1/2 sec */ ++ ++ __opvx_a24xx_setcreg(mem32, PIO_BASE, OPVX_PIO_DATA, 0x0); /* GPIO0 As reset*/ ++// if(debug) { ++// printk("opvxa24xx: pull down reset\n"); ++// } ++ ++ (*func)(data/2); /* delay 1/2 sec */ ++ ++ __opvx_a24xx_setcreg(mem32, PIO_BASE, OPVX_PIO_DATA, 0x1); /* GPIO0 As reset*/ ++// if(debug) { ++// printk("opvxa24xx: raise reset finally\n"); ++// } ++ ++ (*func)(data/2); /* delay 1/2 sec */ ++} ++ ++ ++void __opvx_a24xx_write_8bits(unsigned long mem32, unsigned char bits) ++{ ++ volatile unsigned int t; ++ ++ while((__opvx_a24xx_getcreg(mem32, SPI_PCM_BASE, OPVX_SPI_STATUS)&0x40)!=0x40); ++ __opvx_a24xx_setcreg(mem32, SPI_PCM_BASE, OPVX_SPI_OUT, bits); /* we have to write something so the spi can work */ ++ while( (__opvx_a24xx_getcreg(mem32, SPI_PCM_BASE, OPVX_SPI_STATUS)&0x40) != 0x40); /* wait tx finish */ ++ t = __opvx_a24xx_getcreg(mem32, SPI_PCM_BASE, OPVX_SPI_IN); ++} ++ ++static inline void __reset_spi(void *wc_dev) ++{ ++ return; /* we do nothing here */ ++} ++ ++ ++void __opvx_a24xx_setcard(unsigned long mem32, int card) ++{ ++ __opvx_a24xx_setcreg(mem32, SPI_PCM_BASE, OPVX_SPI_CS, 1<<(card/CARDS_PER_MODULE)); ++} ++ ++void __opvx_a24xx_reset_spi(void *wc_dev, int card, void (*func)(void*, int)) ++{ ++ (*func)(wc_dev, card); ++ __reset_spi(wc_dev); ++ __reset_spi(wc_dev); ++} ++ ++static inline int __opvx_a24xx_hit_fxo_daisy(int number_daisy) ++{ ++ int cid; ++ ++ if (number_daisy==0) { ++ cid=0; ++ } else if (number_daisy==1) { ++ cid=0x8; ++ } else if (number_daisy==2) { ++ cid=0x4; ++ } else if (number_daisy==3) { ++ cid=0xc; ++ } else { ++ cid= -1; ++ } ++ ++ return cid; ++} ++ ++void __opvx_a24xx_spi_setreg(void *wc_dev, unsigned long mem32, int card, int modtype, unsigned char reg, unsigned char value, void (*func)(void*, int)) ++{ ++ (*func)(wc_dev, card); ++ if (modtype == MOD_TYPE_FXO) { ++ __opvx_a24xx_write_8bits(mem32, 0x20 | __opvx_a24xx_hit_fxo_daisy(card%CARDS_PER_MODULE)); // fxo daisy operate. ++ __opvx_a24xx_write_8bits(mem32, reg & 0x7f); ++ } else { ++ __opvx_a24xx_write_8bits(mem32, 1<<(card%CARDS_PER_MODULE)); // fxs daisy operate. ++ __opvx_a24xx_write_8bits(mem32, reg & 0x7f); ++ } ++ __opvx_a24xx_write_8bits(mem32, value); ++} ++ ++unsigned char __opvx_a24xx_spi_getreg(void *wc_dev, unsigned long mem32, int card, int modtype, unsigned char reg, void (*func)(void*, int)) ++{ ++ (*func)(wc_dev, card); ++ if (modtype == MOD_TYPE_FXO) { ++ __opvx_a24xx_write_8bits(mem32, 0x60 | __opvx_a24xx_hit_fxo_daisy(card%CARDS_PER_MODULE)); // fxo daisy operate. ++ __opvx_a24xx_write_8bits(mem32, reg & 0x7f); ++ } else { ++ __opvx_a24xx_write_8bits(mem32, 1<<(card%CARDS_PER_MODULE)); // fxs daisy operate. ++ __opvx_a24xx_write_8bits(mem32, reg | 0x80); ++ } ++ return __opvx_a24xx_read_8bits(mem32); ++} ++ ++ ++static inline void __a24xx_raw_oct_out(unsigned long mem32, const unsigned int addr, const unsigned int value) ++{ ++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, (addr<<16) | BIT_EC_ADDR_STAGE); ++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, (addr<<16) | BIT_EC_ADDR_STAGE | BIT_EC_WR); ++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, (addr<<16) | BIT_EC_ADDR_STAGE | BIT_EC_WR | BIT_EC_ALE); ++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_DATA, value); ++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, BIT_EC_WR | BIT_EC_ALE | BIT_EC_CS); ++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, 0); ++} ++ ++static inline unsigned int __a24xx_raw_oct_in(unsigned long mem32, const unsigned int addr) ++{ ++ unsigned int ret; ++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, (addr<<16) | BIT_EC_ADDR_STAGE); ++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, (addr<<16) | BIT_EC_ADDR_STAGE | BIT_EC_WR); ++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, (addr<<16) | BIT_EC_ADDR_STAGE | BIT_EC_WR | BIT_EC_ALE); ++#ifdef PEDANTIC_OCTASIC_CHECKING ++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, (addr<<16) | BIT_EC_ADDR_STAGE | BIT_EC_ALE); ++#endif ++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, BIT_EC_RD | BIT_EC_ALE | BIT_EC_CS); ++ ret = __opvx_a24xx_getcreg(mem32, EC_BASE, OPVX_EC_DATA); ++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, 0); ++ ++ return ret&0xffff; ++} ++ ++unsigned int __opvx_a24xx_oct_in(unsigned long mem32, unsigned int addr) ++{ ++#ifdef PEDANTIC_OCTASIC_CHECKING ++ int count = 1000; ++#endif ++ __a24xx_raw_oct_out(mem32, 0x0008, (addr >> 20)); ++ __a24xx_raw_oct_out(mem32, 0x000a, (addr >> 4) & ((1 << 16) - 1)); ++ __a24xx_raw_oct_out(mem32, 0x0000, (((addr >> 1) & 0x7) << 9) | (1 << 8) | (1)); ++#ifdef PEDANTIC_OCTASIC_CHECKING ++ while((__a24xx_raw_oct_in(mem32, 0x0000) & (1 << 8)) && --count); ++ if (count != 1000) { ++// printk("Yah, read can be slow...\n"); ++ } ++ if (!count) { ++// printk("Read timed out!\n"); ++ } ++#endif ++ return __a24xx_raw_oct_in(mem32, 0x0004); ++} ++ ++void __opvx_a24xx_oct_out(unsigned long mem32, unsigned int addr, unsigned int value) ++{ ++#ifdef PEDANTIC_OCTASIC_CHECKING ++ int count = 1000; ++#endif ++ __a24xx_raw_oct_out(mem32, 0x0008, (addr >> 20)); ++ __a24xx_raw_oct_out(mem32, 0x000a, (addr >> 4) & ((1 << 16) - 1)); ++ __a24xx_raw_oct_out(mem32, 0x0004, value); ++ __a24xx_raw_oct_out(mem32, 0x0000, (((addr >> 1) & 0x7) << 9) | (1 << 8) | (3 << 12) | 1); ++#ifdef PEDANTIC_OCTASIC_CHECKING ++ while((__a24xx_raw_oct_in(mem32, 0x0000) & (1 << 8)) && --count); ++ if (count != 1000) { ++// printk("Yah, write can be slow\n"); ++ } ++ if (!count) { ++// printk("Write timed out!\n"); ++ } ++#endif ++} ++ ++int __opvx_a24xx_check_vpm(unsigned long mem32) ++{ ++ unsigned int check1, check2; ++ ++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_VPM, 0); //disable vpm support at first. ++ ++ if (!vpmsupport) { ++// printk("OpenVox VPM: Support Disabled\n"); ++ return -1; ++ } ++ ++ __a24xx_raw_oct_out(mem32, 0x000a, 0x5678); ++ __a24xx_raw_oct_out(mem32, 0x0004, 0x1234); ++ check1 = __a24xx_raw_oct_in(mem32, 0x0004); ++ check2 = __a24xx_raw_oct_in(mem32, 0x000a); ++ ++// if (ec_debug) { ++// printk("OCT Result: %04x/%04x\n", __a24xx_raw_oct_in(mem32, 0x0004), __a24xx_raw_oct_in(mem32, 0x000a)); ++// } ++ ++ if (__a24xx_raw_oct_in(mem32, 0x0004) != 0x1234) { ++// printk("OpenVox VPM: Not Present\n"); ++ return -2; ++ } ++ ++ return 0; ++} ++ ++void __opvx_a24xx_vpm_setpresent(unsigned long mem32) ++{ ++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_VPM, BIT_EC_PRESENT); ++} ++ ++void __opvx_a24xx_set_chunk(void *readchunk, void *writechunk,unsigned int frq,int buf_mult) ++{ ++ unsigned char *tmp; ++ tmp = *((unsigned char **)(writechunk)) + frq * ZT_MAX_CHUNKSIZE * (MAX_NUM_CARDS) * 2; /* in bytes */ ++ *(char **)readchunk = tmp; ++} ++ ++void __opvx_a24xx_transmit(unsigned long mem32, volatile unsigned char *writechunk, volatile unsigned char **txbuf,unsigned int irq_frq , unsigned int order) ++{ ++ unsigned int int_cnt_lo = __opvx_a24xx_get_irqcnt_lo(mem32); ++ ++ if (int_cnt_lo & 0x01) { ++ /* Write is at interrupt address. Start writing from normal offset */ ++ *txbuf = writechunk + ZT_CHUNKSIZE * MAX_NUM_CARDS * order; ++ } else { ++ *txbuf = writechunk + ZT_CHUNKSIZE * MAX_NUM_CARDS * irq_frq + ZT_CHUNKSIZE * MAX_NUM_CARDS * order; ++ } ++} ++ ++void __opvx_a24xx_receive(unsigned long mem32, volatile unsigned char *readchunk, volatile unsigned char **rxbuf,unsigned int irq_frq , unsigned int order) ++{ ++ unsigned int int_cnt_lo = __opvx_a24xx_get_irqcnt_lo(mem32); ++ ++ if (int_cnt_lo & 0x01) { ++ /* Read is at interrupt address. Valid data is available at normal offset */ ++ *rxbuf = readchunk + ZT_CHUNKSIZE * MAX_NUM_CARDS * order; ++ } else { ++ *rxbuf = readchunk + ZT_CHUNKSIZE * MAX_NUM_CARDS * irq_frq + ZT_CHUNKSIZE * MAX_NUM_CARDS * order; ++ } ++} ++ ++void __opvx_a24xx_reset_modules_v2(unsigned long mem32, void (*func)(int), int data) ++{ ++ __opvx_a24xx_setcreg(mem32, REG_BASE, V2_OPVX_PIO_DATA, 0x02020200); /* gpio bit[1] set to 1*/ ++ (*func)(data/2); /* delay 1/2 sec */ ++ __opvx_a24xx_setcreg(mem32, REG_BASE, V2_OPVX_PIO_DATA, 0x02020000); /* gpio bit[1] set to 0*/ ++ (*func)(data/2); /* delay 1/2 sec */ ++ __opvx_a24xx_setcreg(mem32, REG_BASE, V2_OPVX_PIO_DATA, 0x02020200); /* gpio bit[1] set to 1*/ ++ (*func)(data/2); /* delay 1/2 sec */ ++} ++ ++unsigned int __opvx_a24xx_oct_in_v2(unsigned long mem32, unsigned int addr) ++{ ++ int count = 1000; ++ __opvx_a24xx_setcreg(mem32, V2_EC_BASE, 0x0008, (addr >> 20)); ++ __opvx_a24xx_setcreg(mem32, V2_EC_BASE, 0x000a, (addr >> 4) & ((1 << 16) - 1)); ++ __opvx_a24xx_setcreg(mem32, V2_EC_BASE, 0x0000, (((addr >> 1) & 0x7) << 9) | (1 << 8) | (1)); ++ while((__opvx_a24xx_getcreg(mem32, V2_EC_BASE, 0x0000) & (1 << 8)) && --count); ++ if (count != 1000) { ++// printk("Yah, read can be slow...\n"); ++ } ++ if (!count) { ++// printk("Read timed out!\n"); ++ } ++ return __opvx_a24xx_getcreg(mem32,V2_EC_BASE, 0x0004); ++} ++ ++ ++void __opvx_a24xx_oct_out_v2(unsigned long mem32, unsigned int addr, unsigned int value) ++{ ++ int count = 1000; ++ __opvx_a24xx_setcreg(mem32, V2_EC_BASE, 0x0008, (addr >> 20)); ++ __opvx_a24xx_setcreg(mem32, V2_EC_BASE, 0x000a, (addr >> 4) & ((1 << 16) - 1)); ++ __opvx_a24xx_setcreg(mem32, V2_EC_BASE, 0x0004, value); ++ __opvx_a24xx_setcreg(mem32, V2_EC_BASE, 0x0000, (((addr >> 1) & 0x7) << 9) | (1 << 8) | (3 << 12) | 1); ++ while((__opvx_a24xx_getcreg(mem32,V2_EC_BASE, 0x0000) & (1 << 8)) && --count); ++ if (count != 1000) { ++// printk("Yah, write can be slow\n"); ++ } ++ if (!count) { ++// printk("Write timed out!\n"); ++ } ++} ++ ++int __opvx_a24xx_check_vpm_v2(unsigned long mem32) ++{ ++ unsigned int check1, check2; ++ __opvx_a24xx_setcreg(mem32, REG_BASE, OPVX_VPM_PRESENT, 0); //disable vpm support at first. ++ ++ if (!vpmsupport) { ++// printk("OpenVox VPM: Support Disabled\n"); ++ return -1; ++ } ++ ++ __opvx_a24xx_setcreg(mem32, V2_EC_BASE, 0x000a, 0x5678); ++ __opvx_a24xx_setcreg(mem32, V2_EC_BASE, 0x0004, 0x1234); ++ check1 = __opvx_a24xx_getcreg(mem32, V2_EC_BASE, 0x0004); ++ check2 = __opvx_a24xx_getcreg(mem32, V2_EC_BASE, 0x000a); ++ ++// if (ec_debug) { ++// printk("OCT Result: %04x/%04x\n", __opvx_a24xx_getcreg(mem32, V2_EC_BASE, 0x0004), __opvx_a24xx_getcreg(mem32, V2_EC_BASE, 0x000a)); ++// } ++ ++ if (__opvx_a24xx_getcreg(mem32, V2_EC_BASE, 0x0004) != 0x1234) { ++// printk("OpenVox VPM: Not Present\n"); ++ return -2; ++ } ++ return 0; ++} ++ ++void __opvx_a24xx_vpm_setpresent_v2(unsigned long mem32) ++{ ++ __opvx_a24xx_setcreg(mem32, REG_BASE, OPVX_VPM_PRESENT, BIT_EC_PRESENT); ++ ++} +--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/si3050.c 1970-01-01 01:00:00.000000000 +0100 ++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/si3050.c 2015-02-10 14:19:03.000000000 +0100 +@@ -0,0 +1,228 @@ ++/* ++ * OpenVox A24xx FXS/FXO Interface Driver for Zapata Telephony interface ++ * ++ * Written by MiaoLin<miaolin@openvox.cn> ++ * Written by mark.liu<mark.liu@openvox.cn> ++ * $Id: si3050.c 301 2011-01-19 05:20:32Z yangshugang $ ++ * ++ * Copyright (C) 2005-2010 OpenVox Communication Co. Ltd, ++ * ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include <linux/string.h> ++#include <linux/param.h> ++#include <linux/jiffies.h> ++ ++#include "fxo_modes.h" ++#include "base.h" ++ ++extern int debug; ++extern int alawoverride; ++extern int fxofullscale; /* fxo full scale tx/rx, register 30, acim */ ++extern int fwringdetect; ++extern int _opermode; ++extern int fxotxgain; ++extern int fxorxgain; ++extern int fastpickup; ++ ++static int si3050_voicedaa_insane(struct a24xx_dev *wc_dev, int card) ++{ ++ int blah; ++ blah = a24xx_spi_getreg(wc_dev, card, 2); ++ if (blah != 0x3) { ++ return -2; ++ } ++ blah = a24xx_spi_getreg(wc_dev, card, 11); ++ if (debug) { ++ printk("VoiceDAA System: %02x\n", blah & 0xf); ++ } ++ return 0; ++} ++ ++/********************************************************************* ++ * Set the hwgain on the analog modules ++ * ++ * card = the card position for this module (0-23) ++ * gain = gain in dB x10 (e.g. -3.5dB would be gain=-35) ++ * tx = (0 for rx; 1 for tx) ++ * ++ *******************************************************************/ ++int si3050_set_hwgain(struct a24xx_dev *wc_dev, int card, __s32 gain, __u32 tx) ++{ ++ if (!(wc_dev->modtype[card] == MOD_TYPE_FXO)) { ++ printk("Cannot adjust gain. Unsupported module type!\n"); ++ return -1; ++ } ++ if (tx) { ++ if (debug) { ++ printk("setting FXO tx gain for card=%d to %d\n", card, gain); ++ } ++ if (gain >= -150 && gain <= 0) { ++ a24xx_spi_setreg(wc_dev, card, 38, 16 + (gain/-10)); ++ a24xx_spi_setreg(wc_dev, card, 40, 16 + (-gain%10)); ++ } else if (gain <= 120 && gain > 0) { ++ a24xx_spi_setreg(wc_dev, card, 38, gain/10); ++ a24xx_spi_setreg(wc_dev, card, 40, (gain%10)); ++ } else { ++ printk("FXO tx gain is out of range (%d)\n", gain); ++ return -1; ++ } ++ } else { /* rx */ ++ if (debug) { ++ printk("setting FXO rx gain for card=%d to %d\n", card, gain); ++ } ++ if (gain >= -150 && gain <= 0) { ++ a24xx_spi_setreg(wc_dev, card, 39, 16+ (gain/-10)); ++ a24xx_spi_setreg(wc_dev, card, 41, 16 + (-gain%10)); ++ } else if (gain <= 120 && gain > 0) { ++ a24xx_spi_setreg(wc_dev, card, 39, gain/10); ++ a24xx_spi_setreg(wc_dev, card, 41, (gain%10)); ++ } else { ++ printk("FXO rx gain is out of range (%d)\n", gain); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++int si3050_init_voicedaa(struct a24xx_dev *wc_dev, int card, int fast, int manual, int sane) ++{ ++ unsigned char reg16=0, reg26=0, reg30=0, reg31=0; ++ unsigned char ch; ++ long newjiffies; ++ ++ wc_dev->modtype[card] = MOD_TYPE_FXO; ++ /* Sanity check the ProSLIC */ ++ a24xx_reset_spi(wc_dev, card); ++ if (!sane && si3050_voicedaa_insane(wc_dev, card)) { ++ return -2; ++ } ++ ++ /* Software reset */ ++ a24xx_spi_setreg(wc_dev, card, 1, 0x80); ++ ++ /* Wait just a bit */ ++ __a24xx_wait_just_a_bit(HZ/10); ++ ++ /* Enable PCM, ulaw */ ++ if (alawoverride) { ++ a24xx_spi_setreg(wc_dev, card, 33, 0x20); ++ } else { ++ a24xx_spi_setreg(wc_dev, 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); ++ a24xx_spi_setreg(wc_dev, card, 16, reg16); ++ ++ if(fwringdetect) { ++ /* Enable ring detector full-wave rectifier mode */ ++ a24xx_spi_setreg(wc_dev, card, 18, 2); ++ a24xx_spi_setreg(wc_dev, card, 24, 0); ++ } else { ++ /* Set to the device defaults */ ++ a24xx_spi_setreg(wc_dev, card, 18, 0); ++ a24xx_spi_setreg(wc_dev, card, 24, 0x19); ++ } ++ ++ /* Set DC Termination: ++ Tip/Ring voltage adjust, minimum operational current, current limitation */ ++ reg26 |= (fxo_modes[_opermode].dcv << 6); ++ reg26 |= (fxo_modes[_opermode].mini << 4); ++ reg26 |= (fxo_modes[_opermode].ilim << 1); ++ a24xx_spi_setreg(wc_dev, card, 26, reg26); ++ ++ /* Set AC Impedence */ ++ reg30 = (fxofullscale==1) ? (fxo_modes[_opermode].acim|0x10) : (fxo_modes[_opermode].acim); ++ a24xx_spi_setreg(wc_dev, card, 30, reg30); ++ ++ /* Misc. DAA parameters */ ++ if (fastpickup) { ++ reg31 = 0xb3; ++ } else { ++ reg31 = 0xa3; ++ } ++ ++ reg31 |= (fxo_modes[_opermode].ohs2 << 3); ++ a24xx_spi_setreg(wc_dev, card, 31, reg31); ++ ++ /* Set Transmit/Receive timeslot */ ++ //printk("set card %d to %d\n", card, (3-(card%4)) * 8 + (card/4) * 64); ++ a24xx_spi_setreg(wc_dev, card, 34, (card%4) * 8 + (card/4) * 32); ++ a24xx_spi_setreg(wc_dev, card, 35, 0x00); ++ a24xx_spi_setreg(wc_dev, card, 36, (card%4) * 8 + (card/4) * 32); ++ a24xx_spi_setreg(wc_dev, card, 37, 0x00); ++ ++ /* Enable ISO-Cap */ ++ a24xx_spi_setreg(wc_dev, card, 6, 0x00); ++ ++ if (fastpickup) { ++ a24xx_spi_setreg(wc_dev, card, 17, a24xx_spi_getreg(wc_dev, card, 17) | 0x20); ++ } ++ ++ /* Wait 1000ms for ISO-cap to come up */ ++ newjiffies = jiffies; ++ newjiffies += 2 * HZ; ++ while((jiffies < newjiffies) && !(a24xx_spi_getreg(wc_dev, card, 11) & 0xf0)) { ++ __a24xx_wait_just_a_bit(HZ/10); ++ } ++ ++ /*if (!(a24xx_spi_getreg(wc_dev, card, 11) & 0xf0)) {*/ ++ ch = a24xx_spi_getreg(wc_dev, card, 11); ++ if( ch == 0xff ) { ++ printk("VoiceDAA not installed at card %d\n", card); ++ return -1; ++ } ++ if (!(ch & 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", ++ a24xx_spi_getreg(wc_dev, card, 11) >> 4, ++ (a24xx_spi_getreg(wc_dev, card, 13) >> 2) & 0xf); ++ } ++ /* Enable on-hook line monitor */ ++ a24xx_spi_setreg(wc_dev, card, 5, 0x08); ++ ++ /* Take values for fxotxgain and fxorxgain and apply them to module */ ++ si3050_set_hwgain(wc_dev, card, fxotxgain, 1); ++ si3050_set_hwgain(wc_dev, card, fxorxgain, 0); ++ ++ /* NZ -- crank the tx gain up by 7 dB */ ++ if (!strcmp(fxo_modes[_opermode].name, "NEWZEALAND")) { ++ printk("Adjusting gain\n"); ++ si3050_set_hwgain(wc_dev, card, 7, 1); ++ } ++ ++ if(debug) { ++ printk("DEBUG fxotxgain:%i.%i fxorxgain:%i.%i\n", ++ (a24xx_spi_getreg(wc_dev, card, 38)/16) ? -(a24xx_spi_getreg(wc_dev, card, 38) - 16) : a24xx_spi_getreg(wc_dev, card, 38), ++ (a24xx_spi_getreg(wc_dev, card, 40)/16) ? -(a24xx_spi_getreg(wc_dev, card, 40) - 16) : a24xx_spi_getreg(wc_dev, card, 40), ++ (a24xx_spi_getreg(wc_dev, card, 39)/16) ? -(a24xx_spi_getreg(wc_dev, card, 39) - 16) : a24xx_spi_getreg(wc_dev, card, 39), ++ (a24xx_spi_getreg(wc_dev, card, 41)/16) ? -(a24xx_spi_getreg(wc_dev, card, 41) - 16) : a24xx_spi_getreg(wc_dev, card, 41)); ++ } ++ ++ /* battery state still unknown */ ++ return 0; ++ ++} +--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/si321x.c 1970-01-01 01:00:00.000000000 +0100 ++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/si321x.c 2015-02-10 14:19:03.000000000 +0100 +@@ -0,0 +1,1469 @@ ++/* ++ * OpenVox A24xx FXS/FXO Interface Driver for Zapata Telephony interface ++ * ++ * Written by MiaoLin<miaolin@openvox.cn> ++ * Written by mark.liu<mark.liu@openvox.cn> ++ * $Id: si321x.c 482 2011-06-02 08:58:56Z liuyuan $ ++ * ++ * Copyright (C) 2005-2010 OpenVox Communication Co. Ltd, ++ * ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include <linux/param.h> ++#include <linux/jiffies.h> ++#include <linux/sched.h> ++ ++#include "proslic.h" ++#include "fxo_modes.h" ++#include "base.h" ++ ++/* module param */ ++extern int debug; ++extern int loopcurrent; ++extern int reversepolarity; ++extern int fxstxgain; ++extern int fxsrxgain; ++extern int boostringer; ++extern int fastringer; ++extern int lowpower; ++extern int _opermode; ++extern int alawoverride; ++extern int fxshonormode; ++ ++static int acim2tiss[16] = { 0x0, 0x1, 0x4, 0x5, 0x7, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x2, 0x0, 0x3 }; ++ ++/* indirect_resg */ ++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",0x0ff4}, ++{33,20,"PWR_ALARM_Q3Q4",0x6e7e}, ++{34,21,"PWR_ALARM_Q5Q6",0x0ff4}, ++{35,22,"LOOP_CLOSURE_FILTER",0x8000}, ++{36,23,"RING_TRIP_FILTER",0x0320}, ++{37,24,"TERM_LP_POLE_Q1Q2",0x0012}, ++{38,25,"TERM_LP_POLE_Q3Q4",0x0012}, ++{39,26,"TERM_LP_POLE_Q5Q6",0x0012}, ++{40,27,"CM_BIAS_RINGING",0x0C00}, ++{41,64,"DCDC_MIN_V",0x0C00}, ++{42,255,"DCDC_XTRA",0x1000}, ++{43,66,"LOOP_CLOSE_TRES_LOW",0x1000}, ++}; ++ ++static int si321x_powerup_proslic(struct a24xx_dev *wc_dev, int card, int fast) ++{ ++ unsigned char vbat; ++ unsigned long origjiffies; ++ int lim; ++ ++ /* Set period of DC-DC converter to 1/64 khz */ ++ a24xx_spi_setreg(wc_dev, card, 92, 0xff /* was 0xff */); ++ ++ /* Wait for VBat to powerup */ ++ origjiffies = jiffies; ++ ++ /* Disable powerdown */ ++ a24xx_spi_setreg(wc_dev, card, 14, 0); ++ ++ /* If fast, don't bother checking anymore */ ++ if (fast) ++ return 0; ++ ++ while((vbat = a24xx_spi_getreg(wc_dev, card, 82)) < 0xc0) { ++ /* Wait no more than 500ms */ ++ if ((jiffies - origjiffies) > HZ/2) { ++ break; ++ } ++ } ++ ++ if (vbat < 0xc0) { ++ if (wc_dev->proslic_power == PROSLIC_POWER_UNKNOWN) { ++ 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 A24xxP??\n", ++ card, (int)(((jiffies - origjiffies) * 1000 / HZ)), ++ vbat * 375); ++ } ++ wc_dev->proslic_power = PROSLIC_POWER_WARNED; ++ 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))); ++ } ++ wc_dev->proslic_power = PROSLIC_POWER_ON; ++ ++ /* Proslic max allowed loop current, reg 71 LOOP_I_LIMIT */ ++ /* If out of range, just set it to the default value */ ++ lim = (loopcurrent - 20) / 3; ++ if ( loopcurrent > 41 ) { ++ lim = 0; ++ if (debug) { ++ printk("Loop current out of range! Setting to default 20mA!\n"); ++ } ++ } ++ else if (debug) { ++ printk("Loop current set to %dmA!\n",(lim*3)+20); ++ } ++ a24xx_spi_setreg(wc_dev,card,LOOP_I_LIMIT,lim); ++ ++ /* Engage DC-DC converter */ ++ a24xx_spi_setreg(wc_dev, card, 93, 0x19 /* was 0x19 */); ++#if 0 ++ origjiffies = jiffies; ++ while(0x80 & opvx_a24xx_spi_getreg(wc_dev, 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 = opvx_a24xx_spi_getreg(wc_dev, 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; ++ ++} ++ ++/*return not OK modules flag*/ ++static int si321x_powerup_proslic_all(struct a24xx_dev *wc_dev, int flag, int fast) ++{ ++ unsigned long origjiffies; ++ struct stat{ ++ unsigned char vbat; ++ unsigned long jifs; ++ }; ++ ++ struct stat stats[24]; ++ int lim; ++ int tmp_flag,x; ++ ++ ++ for(x=0; x < wc_dev->max_cards; x++){ ++ if(flag & (1 << x)){ ++ /* Set period of DC-DC converter to 1/64 khz */ ++ a24xx_spi_setreg(wc_dev, x, 92, 0xff /* was 0xff */); ++ ++ /* Disable powerdown */ ++ a24xx_spi_setreg(wc_dev, x, 14, 0); ++ ++ stats[x].vbat = 0; ++ stats[x].jifs = 0; ++ } ++ } ++ ++ /* Wait for VBat to powerup */ ++ origjiffies = jiffies; ++ ++ /* If fast, don't bother checking anymore */ ++ if (fast) ++ return 0; ++ ++ tmp_flag = flag; ++ while( ((jiffies - origjiffies) <= HZ/2) && tmp_flag){ /* Wait no more than 500ms */ ++ for(x=0;x<wc_dev->max_cards;x++){ ++ if(tmp_flag & (1 << x)){ ++ //stats[x].vbat = a24xx_spi_getreg(wc_dev, x, 82); ++ stats[x].vbat = a24xx_spi_getreg(wc_dev, x, 82); ++ if(stats[x].vbat >= 0xc0){ ++ stats[x].jifs = jiffies - origjiffies; ++ tmp_flag &=~(1 << x); ++ } ++ } ++ } ++ } ++ ++ for(x=0; x < wc_dev->max_cards; x++){ ++ if(flag & (1 << x)){ ++ if(stats[x].vbat < 0xc0){ ++ if (wc_dev->proslic_power == PROSLIC_POWER_UNKNOWN){ ++ 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 A24xxP??\n", ++ x, (int)(((jiffies - origjiffies) * 1000 / HZ)), ++ stats[x].vbat * 375); ++ } ++ //wc_dev->proslic_power = PROSLIC_POWER_WARNED; ++ } ++ else if(debug){ ++ printk("ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n", ++ x, stats[x].vbat * 376 / 1000, stats[x].vbat, (int)((stats[x].jifs * 1000 / HZ))); ++ } ++ } ++ } ++ if(tmp_flag == flag){ ++ wc_dev->proslic_power = PROSLIC_POWER_WARNED; ++ return tmp_flag; ++ } ++ ++ wc_dev->proslic_power = PROSLIC_POWER_ON; ++ ++ /* Proslic max allowed loop current, reg 71 LOOP_I_LIMIT */ ++ /* If out of range, just set it to the default value */ ++ lim = (loopcurrent - 20) / 3; ++ if ( loopcurrent > 41 ) { ++ lim = 0; ++ if (debug) { ++ printk("Loop current out of range! Setting to default 20mA!\n"); ++ } ++ } ++ else if (debug) { ++ printk("Loop current set to %dmA!\n",(lim*3)+20); ++ } ++ flag &= ~tmp_flag; ++ ++ for(x=0;x < wc_dev->max_cards;x++){ ++ if(flag & (1 << x)){ ++ a24xx_spi_setreg(wc_dev,x,LOOP_I_LIMIT,lim); ++ ++ /* Engage DC-DC converter */ ++ a24xx_spi_setreg(wc_dev, x, 93, 0x19 /* was 0x19 */); ++ } ++ } ++ ++ return tmp_flag; ++} ++ ++static int si321x_proslic_insane(struct a24xx_dev *wc_dev, int card) ++{ ++ int blah,insane_report; ++ insane_report=0; ++ ++ blah = a24xx_spi_getreg(wc_dev, card, 0); ++ if (debug) { ++ printk("ProSLIC on module %d, product %d, version %d (0x%x)\n", card, (blah & 0x30) >> 4, (blah & 0xf), blah&0xff); ++ } ++ ++#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 (a24xx_spi_getreg(wc_dev, card, 1) & 0x80) { ++ /* ProSLIC 3215, not a 3210 */ ++ wc_dev->flags[card] |= FLAG_3215; ++ } ++ ++ blah = a24xx_spi_getreg(wc_dev, 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 = a24xx_spi_getreg(wc_dev, 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 = a24xx_spi_getreg(wc_dev, 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. */ ++ a24xx_spi_setreg(wc_dev, card, 30, 0); ++ ++ if (debug) { ++ printk("ProSLIC on module %d seems sane.\n", card); ++ } ++ ++ return 0; ++} ++ ++ ++#if 1 ++static int si321x_proslic_calibrate(struct a24xx_dev *wc_dev, int card) ++{ ++ unsigned long origjiffies; ++ int x; ++ /* Perform all calibrations */ ++ a24xx_spi_setreg(wc_dev, card, 97, 0x1f); ++ ++ /* Begin, no speedup */ ++ a24xx_spi_setreg(wc_dev, card, 96, 0x5f); ++ ++ /* Wait for it to finish */ ++ origjiffies = jiffies; ++ while(a24xx_spi_getreg(wc_dev, 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, a24xx_spi_getreg(wc_dev, card, x)); ++ } ++ } ++ return 0; ++} ++#endif ++ ++/*return not OK cards flag*/ ++static int si321x_proslic_calibrate_all(struct a24xx_dev *wc_dev, int flag) ++{ ++ unsigned long origjiffies; ++ int i,x,tmp_flag; ++ ++ for(x=0;x<wc_dev->max_cards;x++){ ++ if(flag & (1 << x)){ ++ /* Perform all calibrations */ ++ a24xx_spi_setreg(wc_dev, x, 97, 0x1f); ++ ++ /* Begin, no speedup */ ++ a24xx_spi_setreg(wc_dev, x, 96, 0x5f); ++ } ++ } ++ ++ /* Wait for it to finish */ ++ origjiffies = jiffies; ++ tmp_flag = flag; ++ ++ while(((jiffies - origjiffies) < 2 * HZ) && tmp_flag){ ++ for(x=0;x<wc_dev->max_cards;x++){ ++ if(tmp_flag & (1 << x)){ ++ if(!a24xx_spi_getreg(wc_dev, x, 96)){ ++ tmp_flag &= ~(1 << x); ++ } ++ } ++ } ++ } ++ ++ if (debug) { ++ /* Print calibration parameters */ ++ for(x=0;x<wc_dev->max_cards;x++){ ++ if(flag & (1 << x)){ ++ printk("Calibration Vector Regs 98 - 107: \n"); ++ for (i=98;i<108;i++) { ++ printk("Module %d %d: %02x\n", x,i, a24xx_spi_getreg(wc_dev, x, i)); ++ } ++ } ++ } ++ } ++ return tmp_flag; ++} ++ ++static int si321x_proslic_powerleak_test(struct a24xx_dev *wc_dev, int card) ++{ ++ unsigned long origjiffies; ++ unsigned char vbat; ++ ++ /* Turn off linefeed */ ++ a24xx_spi_setreg(wc_dev, card, 64, 0); ++ ++ /* Power down */ ++ a24xx_spi_setreg(wc_dev, card, 14, 0x10); ++ ++ /* Wait for one second */ ++ origjiffies = jiffies; ++ ++ while((vbat = a24xx_spi_getreg(wc_dev, 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; ++} ++ ++/* return OK cards flag*/ ++static int si321x_proslic_powerleak_test_all(struct a24xx_dev *wc_dev, int flag) ++{ ++ unsigned long origjiffies; ++ struct stat{ ++ unsigned char vbat; ++ unsigned long jifs; ++ }; ++ ++ struct stat stats[24]; ++ int x,tmp_flag=0; ++ ++ for(x=0;x<wc_dev->max_cards;x++){ ++ if(flag & (1 << x)){ ++ /* Turn off linefeed */ ++ a24xx_spi_setreg(wc_dev, x, 64, 0); ++ ++ /* Power down */ ++ a24xx_spi_setreg(wc_dev, x, 14, 0x10); ++ } ++ stats[x].vbat=0; ++ stats[x].jifs=0; ++ } ++ ++ /* Wait for one second */ ++ origjiffies = jiffies; ++ ++ tmp_flag = flag; ++ while(((jiffies - origjiffies) < (HZ/2)) && tmp_flag){ ++ for(x=0;x<wc_dev->max_cards;x++){ ++ if(tmp_flag & (1 << x)){ ++ if((stats[x].vbat = a24xx_spi_getreg(wc_dev, x, 82)) < 0x6){ ++ tmp_flag &= ~(1 << x); ++ stats[x].jifs = jiffies - origjiffies; ++ } ++ } ++ } ++ } ++ ++ for(x=0;x<wc_dev->max_cards;x++){ ++ if(flag & (1 << x)){ ++ if (stats[x].vbat < 0x06) { ++ printk("Excessive leakage detected on module %d: %d volts (%02x) after %d ms\n", x, ++ 376 * stats[x].vbat / 1000, stats[x].vbat, (int)(stats[x].jifs * 1000 / HZ)); ++ } else if (debug) { ++ printk("Post-leakage voltage: %d volts\n", 376 * stats[x].vbat / 1000); ++ } ++ } ++ } ++ ++ return tmp_flag; ++} ++ ++static int si321x_proslic_manual_calibrate(struct a24xx_dev *wc_dev, int card) ++{ ++ unsigned long origjiffies; ++ unsigned char i; ++ ++ a24xx_spi_setreg(wc_dev, card, 21, 0);//(0) Disable all interupts in DR21 ++ a24xx_spi_setreg(wc_dev, card, 22, 0);//(0)Disable all interupts in DR21 ++ a24xx_spi_setreg(wc_dev, card, 23, 0);//(0)Disable all interupts in DR21 ++ a24xx_spi_setreg(wc_dev, card, 64, 0);//(0) ++ ++ a24xx_spi_setreg(wc_dev, card, 97, 0x18); //(0x18)Calibrations without the ADC and DAC offset and without common mode calibration. ++ a24xx_spi_setreg(wc_dev, card, 96, 0x47); //(0x47) Calibrate common mode and differential DAC mode DAC + ILIM ++ ++ origjiffies=jiffies; ++ while( a24xx_spi_getreg(wc_dev,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); ++ si321x_proslic_setreg_indirect(wc_dev, card, 88,0); ++ si321x_proslic_setreg_indirect(wc_dev,card,89,0); ++ si321x_proslic_setreg_indirect(wc_dev,card,90,0); ++ si321x_proslic_setreg_indirect(wc_dev,card,91,0); ++ si321x_proslic_setreg_indirect(wc_dev,card,92,0); ++ si321x_proslic_setreg_indirect(wc_dev,card,93,0); ++ ++ a24xx_spi_setreg(wc_dev, card, 98,0x10); // This is necessary if the calibration occurs other than at reset time ++ a24xx_spi_setreg(wc_dev, card, 99,0x10); ++ ++ for ( i=0x1f; i>0; i--) { ++ a24xx_spi_setreg(wc_dev, card, 98,i); ++ origjiffies=jiffies; ++ while((jiffies-origjiffies)<4); ++ if((a24xx_spi_getreg(wc_dev,card,88)) == 0) ++ break; ++ } // for ++ ++ for ( i=0x1f; i>0; i--) { ++ a24xx_spi_setreg(wc_dev, card, 99,i); ++ origjiffies=jiffies; ++ while((jiffies-origjiffies)<4); ++ if((a24xx_spi_getreg(wc_dev,card,89)) == 0) ++ break; ++ }//for ++ ++ /*******************************The preceding is the manual gain mismatch calibration****************************/ ++ /**********************************The following is the longitudinal Balance Cal***********************************/ ++ a24xx_spi_setreg(wc_dev,card,64,1); ++ while((jiffies-origjiffies)<10); // Sleep 100? ++ ++ a24xx_spi_setreg(wc_dev, card, 64, 0); ++ a24xx_spi_setreg(wc_dev, card, 23, 0x4); // enable interrupt for the balance Cal ++ a24xx_spi_setreg(wc_dev, card, 97, 0x1); // this is a singular calibration bit for longitudinal calibration ++ a24xx_spi_setreg(wc_dev, card, 96,0x40); ++ ++ a24xx_spi_getreg(wc_dev,card,96); /* Read Reg 96 just cause */ ++ ++ a24xx_spi_setreg(wc_dev, card, 21, 0xFF); ++ a24xx_spi_setreg(wc_dev, card, 22, 0xFF); ++ a24xx_spi_setreg(wc_dev, card, 23, 0xFF); ++ ++ /**The preceding is the longitudinal Balance Cal***/ ++ return(0); ++ ++} ++ ++/*return not OK cards flag*/ ++static int si321x_proslic_manual_calibrate_all(struct a24xx_dev *wc_dev, int flag) ++{ ++ unsigned long origjiffies; ++ unsigned char i; ++ int x,tmp_flag; ++ ++ for(x=0;x<wc_dev->max_cards;x++){ ++ if(flag & (1 << x)){ ++ a24xx_spi_setreg(wc_dev, x, 21, 0);//(0) Disable all interupts in DR21 ++ a24xx_spi_setreg(wc_dev, x, 22, 0);//(0)Disable all interupts in DR21 ++ a24xx_spi_setreg(wc_dev, x, 23, 0);//(0)Disable all interupts in DR21 ++ a24xx_spi_setreg(wc_dev, x, 64, 0);//(0) ++ ++ a24xx_spi_setreg(wc_dev, x, 97, 0x18); //(0x18)Calibrations without the ADC and DAC offset and without common mode calibration. ++ a24xx_spi_setreg(wc_dev, x, 96, 0x47); //(0x47) Calibrate common mode and differential DAC mode DAC + ILIM ++ } ++ } ++ ++ origjiffies=jiffies; ++ ++ /*remove not OK cards flag*/ ++ tmp_flag = flag; ++ while(((jiffies-origjiffies)< 80) && tmp_flag){ ++ for(x=0;x<wc_dev->max_cards;x++){ ++ if(tmp_flag & (1 << x)){ ++ if(a24xx_spi_getreg(wc_dev,x,96) == 0){ ++ tmp_flag &= ~(1 << x); ++ } ++ } ++ } ++ } ++ ++ flag &= ~tmp_flag; ++ ++ //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); ++ ++ for(x=0;x<wc_dev->max_cards;x++){ ++ if(flag & (1 << x)){ ++ si321x_proslic_setreg_indirect(wc_dev,x,88,0); ++ si321x_proslic_setreg_indirect(wc_dev,x,89,0); ++ si321x_proslic_setreg_indirect(wc_dev,x,90,0); ++ si321x_proslic_setreg_indirect(wc_dev,x,91,0); ++ si321x_proslic_setreg_indirect(wc_dev,x,92,0); ++ si321x_proslic_setreg_indirect(wc_dev,x,93,0); ++ ++ a24xx_spi_setreg(wc_dev, x, 98,0x10); // This is necessary if the calibration occurs other than at reset time ++ a24xx_spi_setreg(wc_dev, x, 99,0x10); ++ ++ for ( i=0x1f; i>0; i--) { ++ a24xx_spi_setreg(wc_dev, x, 98,i); ++ origjiffies=jiffies; ++ while((jiffies-origjiffies)<4); ++ if((a24xx_spi_getreg(wc_dev,x,88)) == 0) ++ break; ++ } // for ++ ++ for ( i=0x1f; i>0; i--) { ++ a24xx_spi_setreg(wc_dev, x, 99,i); ++ origjiffies=jiffies; ++ while((jiffies-origjiffies)<4); ++ if((a24xx_spi_getreg(wc_dev,x,89)) == 0) ++ break; ++ }//for ++ ++ /*******************************The preceding is the manual gain mismatch calibration****************************/ ++ /**********************************The following is the longitudinal Balance Cal***********************************/ ++ a24xx_spi_setreg(wc_dev,x,64,1); ++ } ++ } ++ ++ while((jiffies-origjiffies)<10); // Sleep 100? ++ ++ for(x=0;x<wc_dev->max_cards;x++){ ++ if(flag & (1 << x)){ ++ a24xx_spi_setreg(wc_dev, x, 64, 0); ++ a24xx_spi_setreg(wc_dev, x, 23, 0x4); // enable interrupt for the balance Cal ++ a24xx_spi_setreg(wc_dev, x, 97, 0x1); // this is a singular calibration bit for longitudinal calibration ++ a24xx_spi_setreg(wc_dev, x, 96,0x40); ++ ++ a24xx_spi_getreg(wc_dev,x,96); /* Read Reg 96 just cause */ ++ ++ a24xx_spi_setreg(wc_dev, x, 21, 0xFF); ++ a24xx_spi_setreg(wc_dev, x, 22, 0xFF); ++ a24xx_spi_setreg(wc_dev, x, 23, 0xFF); ++ } ++ } ++ ++ /**The preceding is the longitudinal Balance Cal***/ ++ return tmp_flag; ++} ++ ++static int si321x_proslic_verify_indirect_regs(struct a24xx_dev *wc_dev, int card) ++{ ++ int passed = 1; ++ unsigned short i, initial; ++ int j; ++ ++ for (i=0; i<sizeof(indirect_regs) / sizeof(indirect_regs[0]); i++) { ++ if((j = si321x_proslic_getreg_indirect(wc_dev, card, (unsigned char) indirect_regs[i].address)) < 0) { ++ printk("Failed to read indirect register %d\n", i); ++ return -1; ++ } ++ initial= indirect_regs[i].initial; ++ ++ if ( j != initial && (!(wc_dev->flags[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 int si321x_proslic_init_indirect_regs(struct a24xx_dev *wc_dev, int card) ++{ ++ unsigned char i; ++ ++ for (i=0; i<sizeof(indirect_regs) / sizeof(indirect_regs[0]); i++) { ++ if(si321x_proslic_setreg_indirect(wc_dev, card, indirect_regs[i].address,indirect_regs[i].initial)) { ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++int si321x_set_ring_generator_mode(struct a24xx_dev *wc_dev, int card, int mode) ++{ ++ int reg20, reg21, reg74; /* RCO, RNGX, VBATH */ ++ struct fxs *const fxs = &wc_dev->mod[card].fxs; ++ ++ fxs->neonringing = mode; /* track ring generator mode */ ++ ++ if (mode) { /* Neon */ ++ if (debug) ++ printk(KERN_DEBUG "NEON ring on chan %d, " ++ "lasttxhook was 0x%x\n", card, fxs->lasttxhook); ++ /* Must be in FORWARD ACTIVE before setting ringer */ ++ fxs->lasttxhook = SLIC_LF_ACTIVE_FWD; ++ a24xx_spi_setreg(wc_dev, card, LINE_STATE, fxs->lasttxhook); ++ ++ si321x_proslic_setreg_indirect(wc_dev, card, 22, ++ NEON_MWI_RNGY_PULSEWIDTH); ++ si321x_proslic_setreg_indirect(wc_dev, card, 21, ++ 0x7bef); /* RNGX (91.5Vpk) */ ++ si321x_proslic_setreg_indirect(wc_dev, card, 20, ++ 0x009f); /* RCO (RNGX, t rise)*/ ++ ++ a24xx_spi_setreg(wc_dev, card, 34, 0x19); /* Ringing Osc. Control */ ++ a24xx_spi_setreg(wc_dev, card, 74, 0x3f); /* VBATH 94.5V */ ++ si321x_proslic_setreg_indirect(wc_dev, card, 29, 0x4600); /* RPTP */ ++ /* A write of 0x04 to register 64 will turn on the VM led */ ++ } else { ++ a24xx_spi_setreg(wc_dev, card, 34, 0x00); /* Ringing Osc. Control */ ++ /* RNGY Initial Phase */ ++ si321x_proslic_setreg_indirect(wc_dev, card, 22, 0x0000); ++ si321x_proslic_setreg_indirect(wc_dev, card, 29, 0x3600); /* RPTP */ ++ /* A write of 0x04 to register 64 will turn on the ringer */ ++ ++ if (fastringer) { ++ /* Speed up Ringer */ ++ reg20 = 0x7e6d; ++ reg74 = 0x32; /* Default */ ++ /* Beef up Ringing voltage to 89V */ ++ if (boostringer) { ++ reg74 = 0x3f; ++ reg21 = 0x0247; /* RNGX */ ++ if (debug) ++ printk(KERN_DEBUG "Boosting fast ringer" ++ " on chan %d (89V peak)\n", ++ card); ++ } else if (lowpower) { ++ reg21 = 0x014b; /* RNGX */ ++ if (debug) ++ printk(KERN_DEBUG "Reducing fast ring " ++ "power on chan %d (50V peak)\n", ++ card); ++ } else if (fxshonormode && ++ fxo_modes[_opermode].ring_x) { ++ reg21 = fxo_modes[_opermode].ring_x; ++ if (debug) ++ printk(KERN_DEBUG "fxshonormode: fast " ++ "ring_x power on chan %d\n", ++ card); ++ } else { ++ reg21 = 0x01b9; ++ if (debug) ++ printk(KERN_DEBUG "Speeding up ringer " ++ "on chan %d (25Hz)\n", ++ card); ++ } ++ /* VBATH */ ++ a24xx_spi_setreg(wc_dev, card, 74, reg74); ++ /*RCO*/ ++ si321x_proslic_setreg_indirect(wc_dev, card, 20, reg20); ++ /*RNGX*/ ++ si321x_proslic_setreg_indirect(wc_dev, card, 21, reg21); ++ ++ } else { ++ /* Ringer Speed */ ++ if (fxshonormode && fxo_modes[_opermode].ring_osc) { ++ reg20 = fxo_modes[_opermode].ring_osc; ++ if (debug) ++ printk(KERN_DEBUG "fxshonormode: " ++ "ring_osc speed on chan %d\n", ++ card); ++ } else { ++ reg20 = 0x7ef0; /* Default */ ++ } ++ ++ reg74 = 0x32; /* Default */ ++ /* Beef up Ringing voltage to 89V */ ++ if (boostringer) { ++ reg74 = 0x3f; ++ reg21 = 0x1d1; ++ if (debug) ++ printk(KERN_DEBUG "Boosting ringer on " ++ "chan %d (89V peak)\n", ++ card); ++ } else if (lowpower) { ++ reg21 = 0x108; ++ if (debug) ++ printk(KERN_DEBUG "Reducing ring power " ++ "on chan %d (50V peak)\n", ++ card); ++ } else if (fxshonormode && ++ fxo_modes[_opermode].ring_x) { ++ reg21 = fxo_modes[_opermode].ring_x; ++ if (debug) ++ printk(KERN_DEBUG "fxshonormode: ring_x" ++ " power on chan %d\n", ++ card); ++ } else { ++ reg21 = 0x160; ++ if (debug) ++ printk(KERN_DEBUG "Normal ring power on" ++ " chan %d\n", ++ card); ++ } ++ /* VBATH */ ++ a24xx_spi_setreg(wc_dev, card, 74, reg74); ++ /* RCO */ ++ si321x_proslic_setreg_indirect(wc_dev, card, 20, reg20); ++ /* RNGX */ ++ si321x_proslic_setreg_indirect(wc_dev, card, 21, reg21); ++ } ++ } ++ return 0; ++} ++ ++int si321x_init_ring_generator_mode(struct a24xx_dev *wc_dev, int card){ ++ a24xx_spi_setreg(wc_dev, card, 34, 0x00); /* Ringing Osc. Control */ ++ /* neon trapezoid timers */ ++ a24xx_spi_setreg(wc_dev, card, 48, 0xe0); /* Active Timer low byte */ ++ a24xx_spi_setreg(wc_dev, card, 49, 0x01); /* Active Timer high byte */ ++ a24xx_spi_setreg(wc_dev, card, 50, 0xF0); /* Inactive Timer low byte */ ++ a24xx_spi_setreg(wc_dev, card, 51, 0x05); /* Inactive Timer high byte */ ++ ++ si321x_set_ring_generator_mode(wc_dev, card, 0); ++ ++ return 0; ++} ++ ++int si321x_init_proslic(struct a24xx_dev *wc_dev, int card, int fast, int manual, int sane) ++{ ++ unsigned short tmp[5]; ++ unsigned char r19, r9; ++ int x; ++ int fxsmode=0; ++ ++ /* Sanity check the ProSLIC */ ++ if (!sane && si321x_proslic_insane(wc_dev, card)) { ++ return -2; ++ } ++ ++ /* default messages to none and method to FSK */ ++ memset(&wc_dev->mod[card].fxs.vmwisetting, 0, sizeof(wc_dev->mod[card].fxs.vmwisetting)); ++ wc_dev->mod[card].fxs.vmwi_lrev = 0; ++ wc_dev->mod[card].fxs.vmwi_hvdc = 0; ++ wc_dev->mod[card].fxs.vmwi_hvac = 0; ++ ++ /* By default, don't send on hook */ ++ if (reversepolarity) { ++ wc_dev->mod[card].fxs.idletxhookstate = 5; ++ } else { ++ wc_dev->mod[card].fxs.idletxhookstate = 1; ++ } ++ ++ if (sane) { ++ /* Make sure we turn off the DC->DC converter to prevent anything from blowing up */ ++ a24xx_spi_setreg(wc_dev, card, 14, 0x10); ++ } ++ ++ if (si321x_proslic_init_indirect_regs(wc_dev, card)) { ++ printk(KERN_INFO "Indirect Registers failed to initialize on module %d.\n", card); ++ return -1; ++ } ++ ++ /* Clear scratch pad area */ ++ si321x_proslic_setreg_indirect(wc_dev, card, 97,0); ++ ++ /* Clear digital loopback */ ++ a24xx_spi_setreg(wc_dev, card, 8, 0); ++ ++ /* Revision C optimization */ ++ a24xx_spi_setreg(wc_dev, card, 108, 0xeb); ++ ++ /* Disable automatic VBat switching for safety to prevent ++ Q7 from accidently turning on and burning out. */ ++ a24xx_spi_setreg(wc_dev, card, 67, 0x07); /* Note, if pulse dialing has problems at high REN loads ++ change this to 0x17 */ ++ ++ /* Turn off Q7 */ ++ a24xx_spi_setreg(wc_dev, card, 66, 1); ++ ++ /* Flush ProSLIC digital filters by setting to clear, while ++ saving old values */ ++ for (x=0;x<5;x++) { ++ tmp[x] = si321x_proslic_getreg_indirect(wc_dev, card, x + 35); ++ si321x_proslic_setreg_indirect(wc_dev, card, x + 35, 0x8000); ++ } ++ ++ /* Power up the DC-DC converter */ ++ if (si321x_powerup_proslic(wc_dev, card, fast)) { ////////////**************** ++ printk("Unable to do INITIAL ProSLIC powerup on module %d\n", card); ++ return -1; ++ } ++ ++ if (!fast) { ++ /* Check for power leaks */ ++ if (si321x_proslic_powerleak_test(wc_dev, card)) {///////**************** ++ printk("ProSLIC module %d failed leakage test. Check for short circuit\n", card); ++ } ++ /* Power up again */ ++ if (si321x_powerup_proslic(wc_dev, card, fast)) { ////////////**************** ++ printk("Unable to do FINAL ProSLIC powerup on module %d\n", card); ++ return -1; ++ } ++#ifndef NO_CALIBRATION ++ /* Perform calibration */ ++ if(manual) { ++ if (si321x_proslic_manual_calibrate(wc_dev, card)) { //////////**************** ++ //printk("Proslic failed on Manual Calibration\n"); ++ if (si321x_proslic_manual_calibrate(wc_dev, 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(si321x_proslic_calibrate(wc_dev, card)) { ///////**************** ++ //printk("ProSlic died on Auto Calibration.\n"); ++ if (si321x_proslic_calibrate(wc_dev, 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 */ ++ a24xx_spi_setreg(wc_dev, card, 93, 0x99); ++ r19 = a24xx_spi_getreg(wc_dev, card, 107); ++ if ((r19 < 0x2) || (r19 > 0xd)) { ++ printk("DC-DC cal has a surprising direct 107 of 0x%02x!\n", r19); ++ a24xx_spi_setreg(wc_dev, card, 107, 0x8); ++ } ++ ++ /* Save calibration vectors */ ++ for (x=0;x<NUM_CAL_REGS;x++) { ++ wc_dev->mod[card].fxs.calregs.vals[x] = a24xx_spi_getreg(wc_dev, card, 96 + x); ++ } ++#endif ++ ++ } else { ++ /* Restore calibration registers */ ++ for (x=0;x<NUM_CAL_REGS;x++) { ++ a24xx_spi_setreg(wc_dev, card, 96 + x, wc_dev->mod[card].fxs.calregs.vals[x]); ++ } ++ } ++ /* Calibration complete, restore original values */ ++ for (x=0;x<5;x++) { ++ si321x_proslic_setreg_indirect(wc_dev, card, x + 35, tmp[x]); ++ } ++ ++ if (si321x_proslic_verify_indirect_regs(wc_dev, card)) { ++ printk(KERN_INFO "Indirect Registers failed verification.\n"); ++ return -1; ++ } ++ ++ ++#if 0 ++ /* Disable Auto Power Alarm Detect and other "features" */ ++ a24xx_spi_setreg(wc_dev, card, 67, 0x0e); ++ blah = opvx_a24xx_spi_getreg(wc_dev, card, 67); ++#endif ++ ++#if 0 ++ if (si321x_proslic_setreg_indirect(wc_dev, card, 97, 0x0)) { // Stanley: for the bad recording fix ++ printk(KERN_INFO "ProSlic IndirectReg Died.\n"); ++ return -1; ++ } ++#endif ++ ++ if (alawoverride) { ++ a24xx_spi_setreg(wc_dev, card, 1, 0x20); ++ } else { ++ a24xx_spi_setreg(wc_dev, card, 1, 0x28); ++ } ++ // U-Law 8-bit interface ++ a24xx_spi_setreg(wc_dev, card, 2, (card%4) * 8 + (card/4) * 32); // Tx Start count low byte 0 ++ a24xx_spi_setreg(wc_dev, card, 3, 0); // Tx Start count high byte 0 ++ a24xx_spi_setreg(wc_dev, card, 4, (card%4) * 8 + (card/4) * 32); // Rx Start count low byte 0 ++ a24xx_spi_setreg(wc_dev, card, 5, 0); // Rx Start count high byte 0 ++ a24xx_spi_setreg(wc_dev, card, 18, 0xff); // clear all interrupt ++ a24xx_spi_setreg(wc_dev, card, 19, 0xff); ++ a24xx_spi_setreg(wc_dev, card, 20, 0xff); ++ a24xx_spi_setreg(wc_dev, card, 73, 0x04); ++ if (fxshonormode) { ++ fxsmode = acim2tiss[fxo_modes[_opermode].acim]; ++ a24xx_spi_setreg(wc_dev, card, 10, 0x08 | fxsmode); ++ if (fxo_modes[_opermode].ring_osc) { ++ si321x_proslic_setreg_indirect(wc_dev, card, 20, fxo_modes[_opermode].ring_osc); ++ } ++ if (fxo_modes[_opermode].ring_x) { ++ si321x_proslic_setreg_indirect(wc_dev, card, 21, fxo_modes[_opermode].ring_x); ++ } ++ } ++ if (lowpower) { ++ a24xx_spi_setreg(wc_dev, card, 72, 0x10); ++ } ++ ++#if 0 ++ a24xx_spi_setreg(wc_dev, card, 21, 0x00); // enable interrupt ++ a24xx_spi_setreg(wc_dev, card, 22, 0x02); // Loop detection interrupt ++ a24xx_spi_setreg(wc_dev, card, 23, 0x01); // DTMF detection interrupt ++#endif ++ ++#if 0 ++ /* Enable loopback */ ++ a24xx_spi_setreg(wc_dev, card, 8, 0x2); ++ a24xx_spi_setreg(wc_dev, card, 14, 0x0); ++ a24xx_spi_setreg(wc_dev, card, 64, 0x0); ++ a24xx_spi_setreg(wc_dev, card, 1, 0x08); ++#endif ++ ++ if (fastringer) { ++ /* Speed up Ringer */ ++ si321x_proslic_setreg_indirect(wc_dev, card, 20, 0x7e6d); ++ si321x_proslic_setreg_indirect(wc_dev, card, 21, 0x01b9); ++ /* Beef up Ringing voltage to 89V */ ++ if (boostringer) { ++ a24xx_spi_setreg(wc_dev, card, 74, 0x3f); ++ if (si321x_proslic_setreg_indirect(wc_dev, card, 21, 0x247)) { ++ return -1; ++ } ++ printk("Boosting fast ringer on slot %d (89V peak)\n", card + 1); ++ } else if (lowpower) { ++ if (si321x_proslic_setreg_indirect(wc_dev, 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) { ++ a24xx_spi_setreg(wc_dev, card, 74, 0x3f); ++ if (si321x_proslic_setreg_indirect(wc_dev, card, 21, 0x1d1)) { ++ return -1; ++ } ++ printk("Boosting ringer on slot %d (89V peak)\n", card + 1); ++ } else if (lowpower) { ++ if (si321x_proslic_setreg_indirect(wc_dev, card, 21, 0x108)) { ++ return -1; ++ } ++ printk("Reducing ring power on slot %d (50V peak)\n", card + 1); ++ } ++ } ++ if (si321x_init_ring_generator_mode(wc_dev, card)) { ++ return -1; ++ } ++ if(fxstxgain || fxsrxgain) { ++ r9 = a24xx_spi_getreg(wc_dev, 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; ++ } ++ a24xx_spi_setreg(wc_dev,card,9,r9); ++ } ++ ++ if(debug) { ++ printk("DEBUG: fxstxgain:%s fxsrxgain:%s\n", ++ ((a24xx_spi_getreg(wc_dev, card, 9)/8) == 1) ? "3.5": ++ (((a24xx_spi_getreg(wc_dev, card, 9)/4) == 1) ? "-3.5":"0.0"), ++ ((a24xx_spi_getreg(wc_dev, card, 9)/2) == 1) ?"3.5":((a24xx_spi_getreg(wc_dev,card,9)%2) ? "-3.5":"0.0") ++ ); ++ } ++ ++ a24xx_spi_setreg(wc_dev, card, 64, 0x01); ++ ++ return 0; ++} ++ ++/*** ++*return ++ ret_flag : not OK cards flag ++ blk_flag : not installed cards flag ++***/ ++int si321x_init_proslic_all(struct a24xx_dev *wc_dev, int fxs_flag,int fast, int manual, int sane,int *blk_flag) ++{ ++ int flag=fxs_flag,tmp_flag=0,ret_flag=0; ++ unsigned short tmp[24][5]; ++ unsigned char r19, r9; ++ int x,i; ++ int fxsmode=0; ++ ++ ++ for(i=0;i<wc_dev->max_cards;i++) ++ { ++ if(flag & (1 << i)){ ++ if((i%4) == 0){ ++ __a24xx_setcard(wc_dev, i); ++ __opvx_a24xx_write_8bits(wc_dev->mem32, 0x00); ++ __opvx_a24xx_write_8bits(wc_dev->mem32, 0x80); ++ } ++ ++ /* Sanity check the ProSLIC */ ++ if (!sane && si321x_proslic_insane(wc_dev, i)) { ++ tmp_flag |= 1<<i; ++ continue; ++ } ++ ++ /* default messages to none and method to FSK */ ++ memset(&wc_dev->mod[i].fxs.vmwisetting, 0, sizeof(wc_dev->mod[i].fxs.vmwisetting)); ++ wc_dev->mod[i].fxs.vmwi_lrev = 0; ++ wc_dev->mod[i].fxs.vmwi_hvdc = 0; ++ wc_dev->mod[i].fxs.vmwi_hvac = 0; ++ ++ /* By default, don't send on hook */ ++ if (reversepolarity) { ++ wc_dev->mod[i].fxs.idletxhookstate = 5; ++ } else { ++ wc_dev->mod[i].fxs.idletxhookstate = 1; ++ } ++ ++ if (sane) { ++ /* Make sure we turn off the DC->DC converter to prevent anything from blowing up */ ++ a24xx_spi_setreg(wc_dev, i, 14, 0x10); ++ } ++ ++ if (si321x_proslic_init_indirect_regs(wc_dev, i)) { ++ printk(KERN_INFO "Indirect Registers failed to initialize on module %d.\n", i); ++ tmp_flag |= 1<<i; ++ continue; ++ } ++ ++ /* Clear scratch pad area */ ++ si321x_proslic_setreg_indirect(wc_dev, i, 97,0); ++ ++ /* Clear digital loopback */ ++ a24xx_spi_setreg(wc_dev, i, 8, 0); ++ ++ /* Revision C optimization */ ++ a24xx_spi_setreg(wc_dev, i, 108, 0xeb); ++ ++ /* Disable automatic VBat switching for safety to prevent ++ Q7 from accidently turning on and burning out. */ ++ a24xx_spi_setreg(wc_dev, i, 67, 0x07); /* Note, if pulse dialing has problems at high REN loads ++ change this to 0x17 */ ++ ++ /* Turn off Q7 */ ++ a24xx_spi_setreg(wc_dev, i, 66, 1); ++ ++ /* Flush ProSLIC digital filters by setting to clear, while ++ saving old values */ ++ for (x=0;x<5;x++) { ++ tmp[i][x] = si321x_proslic_getreg_indirect(wc_dev, i, x + 35); ++ si321x_proslic_setreg_indirect(wc_dev, i, x + 35, 0x8000); ++ } ++ } ++ } ++ ++ /*remove not installed cards*/ ++ flag &= ~(tmp_flag); ++ if(blk_flag) ++ *blk_flag = tmp_flag; ++ ++ ++ touch_softlockup_watchdog(); ++ ++ /* Power up the DC-DC converter */ ++ tmp_flag = si321x_powerup_proslic_all(wc_dev, flag, fast); ++ for(i=0;i<wc_dev->max_cards;i++){ ++ if(tmp_flag & (1 << i)) ++ printk("Unable to do INITIAL ProSLIC powerup on module %d\n",i); ++ } ++ ++ /*remove not OK cards flag*/ ++ flag &= ~(tmp_flag); ++ ret_flag |= tmp_flag; ++ ++ ++ if (!fast){ ++ /* Check for power leaks */ ++ tmp_flag=si321x_proslic_powerleak_test_all(wc_dev,flag); ++ for(i=0;i<wc_dev->max_cards;i++){ ++ if( (flag & (1 << i)) && !(tmp_flag & (1 << i)) ){ ++ printk("ProSLIC module %d failed leakage test. Check for short circuit\n", i); ++ } ++ } ++ ++ /* Power up again */ ++ tmp_flag = si321x_powerup_proslic_all(wc_dev, flag, fast); ++ for(i=0;i<wc_dev->max_cards;i++){ ++ if(tmp_flag & (1 << i)) ++ printk("Unable to do FINAL ProSLIC powerup on module %d\n",i); ++ } ++ ++ /*remove not OK cards flag*/ ++ flag &= ~(tmp_flag); ++ ret_flag |= tmp_flag; ++ ++#ifndef NO_CALIBRATION ++ if(manual) { ++ tmp_flag = si321x_proslic_manual_calibrate_all(wc_dev,flag); ++ if(tmp_flag){ ++ tmp_flag = si321x_proslic_manual_calibrate_all(wc_dev,tmp_flag); ++ for(i=0;i<wc_dev->max_cards;i++){ ++ if(flag & (1 << i)){ ++ if(tmp_flag & (1 << i)) ++ printk("Proslic Failed on Second Attempt to Calibrate Manually module %d . (Try -DNO_CALIBRATION in Makefile)\n",i); ++ else ++ printk("Proslic Passed Manual Calibration on Second Attempt on module %d \n",i); ++ } ++ } ++ } ++ }else{ ++ tmp_flag = si321x_proslic_calibrate_all(wc_dev,flag); ++ if(tmp_flag){ ++ tmp_flag = si321x_proslic_calibrate_all(wc_dev,tmp_flag); ++ for(i=0;i<wc_dev->max_cards;i++){ ++ if(flag & (1 << i)){ ++ if(tmp_flag & (1 << i)) ++ printk("Proslic Failed on Second Attempt to Auto Calibrate module %d . (Try -DNO_CALIBRATION in Makefile)\n",i); ++ else ++ printk("Proslic Passed Auto Calibration on Second Attempt on module %d \n",i); ++ } ++ } ++ } ++ } ++ ++ /*remove not OK cards flag*/ ++ flag &= ~(tmp_flag); ++ ret_flag |= tmp_flag; ++ ++ for(i=0;i<wc_dev->max_cards;i++){ ++ if(flag & (1 << i)){ ++ /* Perform DC-DC calibration */ ++ a24xx_spi_setreg(wc_dev, i, 93, 0x99); ++ r19 = a24xx_spi_getreg(wc_dev, i, 107); ++ if ((r19 < 0x2) || (r19 > 0xd)) { ++ printk("DC-DC cal has a surprising direct 107 of 0x%02x!\n", r19); ++ a24xx_spi_setreg(wc_dev, i, 107, 0x8); ++ } ++ /* Save calibration vectors */ ++ for (x=0;x<NUM_CAL_REGS;x++) { ++ wc_dev->mod[i].fxs.calregs.vals[x] = a24xx_spi_getreg(wc_dev, i, 96 + x); ++ } ++ } ++ } ++#endif ++ }else{ ++ for(i=0;i<wc_dev->max_cards;i++){ ++ if(flag & (1 << i)){ ++ /* Restore calibration registers */ ++ for (x=0;x<NUM_CAL_REGS;x++) { ++ a24xx_spi_setreg(wc_dev, i, 96 + x, wc_dev->mod[i].fxs.calregs.vals[x]); ++ } ++ } ++ } ++ } ++ ++ tmp_flag = 0; ++ for(i=0;i<wc_dev->max_cards;i++){ ++ if(flag & (1 << i)){ ++ /* Calibration complete, restore original values */ ++ for (x=0;x<5;x++) { ++ si321x_proslic_setreg_indirect(wc_dev, i, x + 35, tmp[i][x]); ++ } ++ ++ if (si321x_proslic_verify_indirect_regs(wc_dev, i)) { ++ printk(KERN_INFO "Indirect Registers failed verification on module %d.\n",i); ++ tmp_flag |=(1<<i); ++ continue; ++ } ++ ++ ++ if (alawoverride) { ++ a24xx_spi_setreg(wc_dev, i, 1, 0x20); ++ } else { ++ a24xx_spi_setreg(wc_dev, i, 1, 0x28); ++ } ++ // U-Law 8-bit interface ++ a24xx_spi_setreg(wc_dev, i, 2, (i%4) * 8 + (i/4) * 32); // Tx Start count low byte 0 ++ a24xx_spi_setreg(wc_dev, i, 3, 0); // Tx Start count high byte 0 ++ a24xx_spi_setreg(wc_dev, i, 4, (i%4) * 8 + (i/4) * 32); // Rx Start count low byte 0 ++ a24xx_spi_setreg(wc_dev, i, 5, 0); // Rx Start count high byte 0 ++ a24xx_spi_setreg(wc_dev, i, 18, 0xff); // clear all interrupt ++ a24xx_spi_setreg(wc_dev, i, 19, 0xff); ++ a24xx_spi_setreg(wc_dev, i, 20, 0xff); ++ a24xx_spi_setreg(wc_dev, i, 73, 0x04); ++ if (fxshonormode) { ++ fxsmode = acim2tiss[fxo_modes[_opermode].acim]; ++ a24xx_spi_setreg(wc_dev, i, 10, 0x08 | fxsmode); ++ if (fxo_modes[_opermode].ring_osc) { ++ si321x_proslic_setreg_indirect(wc_dev, i, 20, fxo_modes[_opermode].ring_osc); ++ } ++ if (fxo_modes[_opermode].ring_x) { ++ si321x_proslic_setreg_indirect(wc_dev, i, 21, fxo_modes[_opermode].ring_x); ++ } ++ } ++ if (lowpower) { ++ a24xx_spi_setreg(wc_dev, i, 72, 0x10); ++ } ++ ++ if (fastringer) { ++ /* Speed up Ringer */ ++ si321x_proslic_setreg_indirect(wc_dev, i, 20, 0x7e6d); ++ si321x_proslic_setreg_indirect(wc_dev, i, 21, 0x01b9); ++ /* Beef up Ringing voltage to 89V */ ++ if (boostringer) { ++ a24xx_spi_setreg(wc_dev, i, 74, 0x3f); ++ if (si321x_proslic_setreg_indirect(wc_dev, i, 21, 0x247)) { ++ tmp_flag |=(1<<i); ++ continue; ++ } ++ printk("Boosting fast ringer on slot %d (89V peak)\n", i + 1); ++ } else if (lowpower) { ++ if (si321x_proslic_setreg_indirect(wc_dev, i, 21, 0x14b)) { ++ tmp_flag |=(1<<i); ++ continue; ++ } ++ printk("Reducing fast ring power on slot %d (50V peak)\n", i + 1); ++ } else { ++ printk("Speeding up ringer on slot %d (25Hz)\n", i + 1); ++ } ++ } else { ++ /* Beef up Ringing voltage to 89V */ ++ if (boostringer) { ++ a24xx_spi_setreg(wc_dev, i, 74, 0x3f); ++ if (si321x_proslic_setreg_indirect(wc_dev, i, 21, 0x1d1)) { ++ tmp_flag |=(1<<i); ++ continue; ++ } ++ printk("Boosting ringer on slot %d (89V peak)\n", i + 1); ++ } else if (lowpower) { ++ if (si321x_proslic_setreg_indirect(wc_dev, i, 21, 0x108)) { ++ tmp_flag |=(1<<i); ++ continue; ++ } ++ printk("Reducing ring power on slot %d (50V peak)\n", i + 1); ++ } ++ } ++ ++ if (si321x_init_ring_generator_mode(wc_dev, i)) { ++ tmp_flag |=(1<<i); ++ continue; ++ } ++ ++ if(fxstxgain || fxsrxgain) { ++ r9 = a24xx_spi_getreg(wc_dev, i, 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; ++ } ++ a24xx_spi_setreg(wc_dev,i,9,r9); ++ } ++ ++ if(debug) { ++ printk("DEBUG: fxstxgain:%s fxsrxgain:%s\n", ++ ((a24xx_spi_getreg(wc_dev, i, 9)/8) == 1) ? "3.5": ++ (((a24xx_spi_getreg(wc_dev, i, 9)/4) == 1) ? "-3.5":"0.0"), ++ ((a24xx_spi_getreg(wc_dev, i, 9)/2) == 1) ?"3.5":((a24xx_spi_getreg(wc_dev,i,9)%2) ? "-3.5":"0.0") ++ ); ++ } ++ ++ a24xx_spi_setreg(wc_dev, i, 64, 0x01); ++ } ++ } ++ ret_flag |= tmp_flag; ++ ++ return ret_flag; ++} ++ ++int si321x_proslic_setreg_indirect(struct a24xx_dev *wc_dev, int card, unsigned char address, unsigned short data) ++{ ++ unsigned long flags; ++ int ret; ++ ++ spin_lock_irqsave(&wc_dev->lock, flags); ++ ret = __a24xx_proslic_setreg_indirect(wc_dev, card, address, data); ++ spin_unlock_irqrestore(&wc_dev->lock, flags); ++ ++ return ret; ++} ++ ++int si321x_proslic_getreg_indirect(struct a24xx_dev *wc_dev, int card, unsigned char address) ++{ ++ unsigned long flags; ++ int ret; ++ ++ spin_lock_irqsave(&wc_dev->lock, flags); ++ ret = __a24xx_proslic_getreg_indirect(wc_dev, card, address); ++ spin_unlock_irqrestore(&wc_dev->lock, flags); ++ ++ return ret; ++} ++ ++void si321x_proslic_recheck_sanity(struct a24xx_dev *wc_dev, int card) ++{ ++ int res; ++ ++ /* Check loopback */ ++ res = wc_dev->reg1shadow[card]; ++ if (!res && (res != wc_dev->mod[card].fxs.lasttxhook)) // read real state from register By wx ++ res=a24xx_spi_getreg(wc_dev, card, 64); ++ if (!res && (res != wc_dev->mod[card].fxs.lasttxhook)) { ++ res = a24xx_spi_getreg(wc_dev, card, 8); ++ if (res) { ++ printk("Ouch, part reset, quickly restoring reality (%d)\n", card); ++ si321x_init_proslic(wc_dev, card, 1, 0, 1); ++ } else { ++ if (wc_dev->mod[card].fxs.palarms++ < MAX_ALARMS) { ++ printk("Power alarm on module %d, resetting!\n", card + 1); ++ if (wc_dev->mod[card].fxs.lasttxhook == 4) ++ wc_dev->mod[card].fxs.lasttxhook = 1; ++ a24xx_spi_setreg(wc_dev, card, 64, wc_dev->mod[card].fxs.lasttxhook); ++ } else { ++ if (wc_dev->mod[card].fxs.palarms == MAX_ALARMS) ++ printk("Too many power alarms on card %d, NOT resetting!\n", card + 1); ++ } ++ } ++ } ++} +--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxd115/Kbuild 1970-01-01 01:00:00.000000000 +0100 ++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxd115/Kbuild 2015-02-10 14:19:03.000000000 +0100 +@@ -0,0 +1,33 @@ ++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OPVXD115) += opvxd115.o ++ ++FIRM_DIR := ../firmware ++ ++EXTRA_CFLAGS += -I$(src)/.. -I$(src)/../oct612x/ $(shell $(src)/../oct612x/octasic-helper cflags $(src)/../oct612x) -Wno-undef ++ ++# The OCT612X source files are from a vendor drop and we do not want to edit ++# them to make this warning go away. Therefore, turn off the ++# unused-but-set-variable warning for this driver. ++ ++EXTRA_CFLAGS += $(call cc-option, -Wno-unused-but-set-variable) ++ ++ifeq ($(HOTPLUG_FIRMWARE),yes) ++ EXTRA_CFLAGS+=-DHOTPLUG_FIRMWARE ++endif ++ ++opvxd115-objs := base.o vpm450m.o ++ ++ifneq ($(HOTPLUG_FIRMWARE),yes) ++opvxd115-objs += $(FIRM_DIR)/dahdi-fw-oct6114-032.o $(FIRM_DIR)/dahdi-fw-oct6114-064.o $(FIRM_DIR)/dahdi-fw-oct6114-128.o $(FIRM_DIR)/dahdi-fw-oct6114-256.o ++$(warning WARNING: You are compiling firmware into wct4xxp.ko which is not available under the terms of the GPL. It may be a violation of the GPL to distribute the resulting image since it combines both GPL and non-GPL work. You should consult a lawyer of your own before distributing such an image.) ++endif ++$(obj)/$(FIRM_DIR)/dahdi-fw-oct6114-032.o: $(obj)/base.o ++ $(MAKE) -C $(obj)/$(FIRM_DIR) dahdi-fw-oct6114-032.o ++ ++$(obj)/$(FIRM_DIR)/dahdi-fw-oct6114-064.o: $(obj)/base.o ++ $(MAKE) -C $(obj)/$(FIRM_DIR) dahdi-fw-oct6114-064.o ++ ++$(obj)/$(FIRM_DIR)/dahdi-fw-oct6114-128.o: $(obj)/base.o ++ $(MAKE) -C $(obj)/$(FIRM_DIR) dahdi-fw-oct6114-128.o ++ ++$(obj)/$(FIRM_DIR)/dahdi-fw-oct6114-256.o: $(obj)/base.o ++ $(MAKE) -C $(obj)/$(FIRM_DIR) dahdi-fw-oct6114-256.o |