--- 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 + * $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 +#include + +#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 +#include "busydetect.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) +#include +#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 +#include +#include +#include "base.h" +#include "busydetect.h" /* use sin_table[] */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) +#include +#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 + * Mark liu + * + * $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 +#include +#include +#include +#include + +#include "ec3000.h" +#include "oct6100api/oct6100_api.h" + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) +#include +#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;xulWriteLength;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;xulWriteLength;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;xulReadLength;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;yaulEchoChanHndl[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 + * Written by mark.liu + * $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 + +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 + * Written by mark.liu + * $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 + * Written by mark.liu + * $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 +#include +#include + +#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 + * Written by mark.liu + * $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 +#include +#include + +#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;xmax_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;xmax_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;xmax_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;xmax_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;xmax_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;xmax_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;xmax_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;xmax_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;xmax_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;xmax_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;xmax_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; iflags[card] & FLAG_3215) || (indirect_regs[i].altaddr != 255))) { + printk("!!!!!!! %s iREG %X = %X should be %X\n", + indirect_regs[i].name,indirect_regs[i].address,j,initial ); + passed = 0; + } + } + + if (passed) { + if (debug) { + printk("Init Indirect Registers completed successfully.\n"); + } + } else { + printk(" !!!!! Init Indirect Registers UNSUCCESSFULLY.\n"); + return -1; + } + return 0; +} + +static int si321x_proslic_init_indirect_regs(struct a24xx_dev *wc_dev, int card) +{ + unsigned char i; + + for (i=0; imod[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;xmod[card].fxs.calregs.vals[x] = a24xx_spi_getreg(wc_dev, card, 96 + x); + } +#endif + + } else { + /* Restore calibration registers */ + for (x=0;xmod[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;imax_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<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<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;imax_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;imax_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;imax_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;imax_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;imax_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;xmod[i].fxs.calregs.vals[x] = a24xx_spi_getreg(wc_dev, i, 96 + x); + } + } + } +#endif + }else{ + for(i=0;imax_cards;i++){ + if(flag & (1 << i)){ + /* Restore calibration registers */ + for (x=0;xmod[i].fxs.calregs.vals[x]); + } + } + } + } + + tmp_flag = 0; + for(i=0;imax_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<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