1705 lines
46 KiB
C
1705 lines
46 KiB
C
/***************************************************************************
|
|
* sdla_remora_tdmv.c WANPIPE(tm) Multiprotocol WAN Link Driver.
|
|
* AFT REMORA and FXO/FXS support module.
|
|
*
|
|
* Author: Alex Feldman <al.feldman@sangoma.com>
|
|
*
|
|
* Copyright: (c) 2005 Sangoma Technologies Inc.
|
|
*
|
|
* 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.
|
|
* ============================================================================
|
|
* Oct 6, 2005 Alex Feldman Initial version.
|
|
******************************************************************************
|
|
*/
|
|
|
|
/*******************************************************************************
|
|
** INCLUDE FILES
|
|
*******************************************************************************/
|
|
#if defined(__FreeBSD__) || defined(__OpenBSD__)
|
|
# include <wanpipe_includes.h>
|
|
# include <wanpipe_debug.h>
|
|
# include <wanpipe_defines.h>
|
|
# include <wanpipe_abstr.h>
|
|
# include <wanpipe_common.h>
|
|
# include <wanpipe.h>
|
|
# include <sdla_remora.h>
|
|
# include <zaptel.h>
|
|
#elif (defined __WINDOWS__)
|
|
# include <wanpipe\csu_dsu.h>
|
|
#else
|
|
# include <zaptel.h>
|
|
# include <linux/wanpipe_includes.h>
|
|
# include <linux/wanpipe_defines.h>
|
|
# include <linux/wanpipe.h>
|
|
# include <linux/sdla_remora.h>
|
|
#endif
|
|
|
|
/*******************************************************************************
|
|
** DEFINES AND MACROS
|
|
*******************************************************************************/
|
|
#define REG_SHADOW
|
|
#define REG_WRITE_SHADOW
|
|
#undef PULSE_DIALING
|
|
|
|
#if 0
|
|
# define SPI2STEP
|
|
#endif
|
|
/* 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 RING_DEBOUNCE 16 /* Ringer Debounce (64 ms) */
|
|
#define DEFAULT_BATT_DEBOUNCE 16 /* Battery debounce (64 ms) */
|
|
#define POLARITY_DEBOUNCE 16 /* 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 MAX_ALARMS 10
|
|
|
|
/* Interrupt flag enable */
|
|
#if 0
|
|
# define WAN_REMORA_FXS_LCIP
|
|
#endif
|
|
#if 0
|
|
# define WAN_REMORA_FXS_DTMF
|
|
#endif
|
|
|
|
|
|
/* flags bits */
|
|
#define WP_TDMV_REGISTER 1 /*0x01*/
|
|
#define WP_TDMV_RUNNING 2 /*0x02*/
|
|
#define WP_TDMV_UP 3 /*0x04*/
|
|
|
|
#define IS_TDMV_RUNNING(wr) wan_test_bit(WP_TDMV_RUNNING, &(wr)->flags)
|
|
#define IS_TDMV_UP(wr) wan_test_bit(WP_TDMV_UP, &(wr)->flags)
|
|
#define IS_TDMV_UP_RUNNING(wr) (IS_TDMV_UP(wr) && IS_TDMV_RUNNING(wr))
|
|
|
|
/*******************************************************************************
|
|
** STRUCTURES AND TYPEDEFS
|
|
*******************************************************************************/
|
|
typedef struct {
|
|
int ready;
|
|
int offhook;
|
|
int lastpol;
|
|
int polarity;
|
|
int polaritydebounce;
|
|
int battery;
|
|
int battdebounce;
|
|
int ringdebounce;
|
|
int nobatttimer;
|
|
int wasringing;
|
|
|
|
int echotune; /* echo tune */
|
|
struct wan_rm_echo_coefs echoregs; /* echo tune */
|
|
} tdmv_fxo_t;
|
|
|
|
typedef struct {
|
|
int ready;
|
|
int lasttxhook;
|
|
int lasttxhook_update;
|
|
int lastrxhook;
|
|
int oldrxhook;
|
|
int debouncehook;
|
|
int debounce;
|
|
int palarms;
|
|
int ohttimer;
|
|
} tdmv_fxs_t;
|
|
|
|
typedef struct wp_tdmv_remora_ {
|
|
void *card;
|
|
char *devname;
|
|
int num;
|
|
int flags;
|
|
wan_spinlock_t lock;
|
|
wan_spinlock_t tx_rx_lock;
|
|
union {
|
|
tdmv_fxo_t fxo;
|
|
tdmv_fxs_t fxs;
|
|
} mod[MAX_REMORA_MODULES];
|
|
|
|
int spanno;
|
|
struct zt_span span;
|
|
struct zt_chan chans[MAX_REMORA_MODULES];
|
|
unsigned long reg_module_map; /* Registered modules */
|
|
|
|
unsigned char reg0shadow[MAX_REMORA_MODULES]; /* read> fxs: 68 fxo: 5 */
|
|
unsigned char reg1shadow[MAX_REMORA_MODULES]; /* read> fxs: 64 fxo: 29 */
|
|
unsigned char reg2shadow[MAX_REMORA_MODULES]; /* read> fxs: 64 fxo: 29 */
|
|
|
|
unsigned char reg0shadow_write[MAX_REMORA_MODULES]; /* write> fxs: 68 fxo: 5 */
|
|
int reg0shadow_update[MAX_REMORA_MODULES];
|
|
|
|
/* Global configuration */
|
|
|
|
u32 intcount;
|
|
int pollcount;
|
|
unsigned char ec_chunk1[31][ZT_CHUNKSIZE];
|
|
unsigned char ec_chunk2[31][ZT_CHUNKSIZE];
|
|
int usecount;
|
|
u16 max_timeslots; /* up to MAX_REMORA_MODULES */
|
|
int max_rxtx_len;
|
|
int channelized;
|
|
unsigned char hwec;
|
|
unsigned long echo_off_map;
|
|
int rxsig_state[MAX_REMORA_MODULES];
|
|
int txsig_state[MAX_REMORA_MODULES]; /* not used */
|
|
|
|
int battdebounce;
|
|
int battthresh;
|
|
|
|
u_int8_t dtmfsupport;
|
|
unsigned int dtmfactive;
|
|
unsigned int dtmfmask;
|
|
unsigned int dtmfmutemask;
|
|
|
|
} wp_tdmv_remora_t;
|
|
|
|
/*******************************************************************************
|
|
** GLOBAL VARIABLES
|
|
*******************************************************************************/
|
|
static int wp_remora_no = 0;
|
|
extern WAN_LIST_HEAD(, wan_tdmv_) wan_tdmv_head;
|
|
//static int battdebounce = DEFAULT_BATT_DEBOUNCE;
|
|
//static int battthresh = DEFAULT_BATT_THRESH;
|
|
|
|
/*******************************************************************************
|
|
** FUNCTION PROTOTYPES
|
|
*******************************************************************************/
|
|
static int wp_tdmv_remora_check_mtu(void* pcard, unsigned long timeslot_map, int *mtu);
|
|
static int wp_tdmv_remora_create(void* pcard, wan_tdmv_conf_t*);
|
|
static int wp_tdmv_remora_remove(void* pcard);
|
|
static int wp_tdmv_remora_reg(void* pcard, wan_tdmv_if_conf_t*, unsigned int, unsigned char,netdevice_t*);
|
|
static int wp_tdmv_remora_unreg(void* pcard, unsigned long ts_map);
|
|
static int wp_tdmv_remora_software_init(wan_tdmv_t *wan_tdmv);
|
|
static int wp_tdmv_remora_state(void* pcard, int state);
|
|
static int wp_tdmv_remora_running(void* pcard);
|
|
static int wp_tdmv_remora_is_rbsbits(wan_tdmv_t *wan_tdmv);
|
|
static int wp_tdmv_remora_rx_tx_span(void *pcard);
|
|
static int wp_tdmv_remora_rx_chan(wan_tdmv_t*, int,unsigned char*,unsigned char*);
|
|
static int wp_tdmv_remora_ec_span(void *pcard);
|
|
|
|
static void wp_tdmv_remora_dtmf (void* card_id, wan_event_t *event);
|
|
|
|
extern int wp_init_proslic(sdla_fe_t *fe, int mod_no, int fast, int sane);
|
|
extern int wp_init_voicedaa(sdla_fe_t *fe, int mod_no, int fast, int sane);
|
|
|
|
/*******************************************************************************
|
|
** FUNCTION DEFINITIONS
|
|
*******************************************************************************/
|
|
|
|
static int
|
|
#if defined(__FreeBSD__) || defined(__OpenBSD__)
|
|
wp_remora_zap_ioctl(struct zt_chan *chan, unsigned int cmd, caddr_t data)
|
|
#else
|
|
wp_remora_zap_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data)
|
|
#endif
|
|
{
|
|
wp_tdmv_remora_t *wr = chan->pvt;
|
|
sdla_t *card = NULL;
|
|
sdla_fe_t *fe = NULL;
|
|
int x, err;
|
|
|
|
WAN_ASSERT(wr->card == NULL);
|
|
card = wr->card;
|
|
fe = &card->fe;
|
|
|
|
switch (cmd) {
|
|
case ZT_ONHOOKTRANSFER:
|
|
if (fe->rm_param.mod[chan->chanpos - 1].type != MOD_TYPE_FXS) {
|
|
return -EINVAL;
|
|
}
|
|
err = WAN_COPY_FROM_USER(&x, (int*)data, sizeof(int));
|
|
/*err = get_user(x, (int *)data);*/
|
|
if (err) return -EFAULT;
|
|
wr->mod[chan->chanpos - 1].fxs.ohttimer = x << 3;
|
|
if (fe->fe_cfg.cfg.remora.reversepolarity){
|
|
/* OHT mode when idle */
|
|
fe->rm_param.mod[chan->chanpos - 1].u.fxs.idletxhookstate = 0x6;
|
|
}else{
|
|
fe->rm_param.mod[chan->chanpos - 1].u.fxs.idletxhookstate = 0x2;
|
|
}
|
|
if (wr->mod[chan->chanpos - 1].fxs.lasttxhook == 0x1) {
|
|
/* Apply the change if appropriate */
|
|
if (fe->fe_cfg.cfg.remora.reversepolarity){
|
|
wr->mod[chan->chanpos - 1].fxs.lasttxhook = 0x6;
|
|
}else{
|
|
wr->mod[chan->chanpos - 1].fxs.lasttxhook = 0x2;
|
|
}
|
|
#if defined(REG_WRITE_SHADOW)
|
|
wr->mod[chan->chanpos-1].fxs.lasttxhook_update = 1;
|
|
#else
|
|
WRITE_RM_REG(chan->chanpos - 1, 64, wr->mod[chan->chanpos - 1].fxs.lasttxhook);
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case ZT_SETPOLARITY:
|
|
err = WAN_COPY_FROM_USER(&x, (int*)data, sizeof(int));
|
|
/*err = get_user(x, (int *)data);*/
|
|
if (err) return -EFAULT;
|
|
if (fe->rm_param.mod[chan->chanpos - 1].type != MOD_TYPE_FXS) {
|
|
return -EINVAL;
|
|
}
|
|
/* Can't change polarity while ringing or when open */
|
|
if ((wr->mod[chan->chanpos - 1].fxs.lasttxhook == 0x04) ||
|
|
(wr->mod[chan->chanpos - 1 ].fxs.lasttxhook == 0x00)){
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((x && !fe->fe_cfg.cfg.remora.reversepolarity) || (!x && fe->fe_cfg.cfg.remora.reversepolarity)){
|
|
wr->mod[chan->chanpos - 1].fxs.lasttxhook |= 0x04;
|
|
}else{
|
|
wr->mod[chan->chanpos - 1].fxs.lasttxhook &= ~0x04;
|
|
}
|
|
#if defined(REG_WRITE_SHADOW)
|
|
wr->mod[chan->chanpos-1].fxs.lasttxhook_update = 1;
|
|
#else
|
|
WRITE_RM_REG(chan->chanpos - 1, 64, wr->mod[chan->chanpos - 1].fxs.lasttxhook);
|
|
#endif
|
|
break;
|
|
|
|
case WAN_RM_SET_ECHOTUNE:
|
|
if (fe->rm_param.mod[chan->chanpos - 1].type == MOD_TYPE_FXO) {
|
|
|
|
err = WAN_COPY_FROM_USER(
|
|
&wr->mod[chan->chanpos-1].fxo.echoregs,
|
|
(struct wan_rm_echo_coefs*)data,
|
|
sizeof(struct wan_rm_echo_coefs));
|
|
if (err) return -EFAULT;
|
|
|
|
#if 1
|
|
wr->mod[chan->chanpos-1].fxo.echotune = 1;
|
|
#else
|
|
DEBUG_EVENT("%s: Module %d: Setting echo registers: \n",
|
|
fe->name, chan->chanpos-1);
|
|
/* Set the ACIM register */
|
|
WRITE_RM_REG(chan->chanpos - 1, 30, echoregs.acim);
|
|
|
|
/* Set the digital echo canceller registers */
|
|
WRITE_RM_REG(chan->chanpos - 1, 45, echoregs.coef1);
|
|
WRITE_RM_REG(chan->chanpos - 1, 46, echoregs.coef2);
|
|
WRITE_RM_REG(chan->chanpos - 1, 47, echoregs.coef3);
|
|
WRITE_RM_REG(chan->chanpos - 1, 48, echoregs.coef4);
|
|
WRITE_RM_REG(chan->chanpos - 1, 49, echoregs.coef5);
|
|
WRITE_RM_REG(chan->chanpos - 1, 50, echoregs.coef6);
|
|
WRITE_RM_REG(chan->chanpos - 1, 51, echoregs.coef7);
|
|
WRITE_RM_REG(chan->chanpos - 1, 52, echoregs.coef8);
|
|
|
|
DEBUG_EVENT("%s: Module %d: Set echo registers successfully\n",
|
|
fe->name, chan->chanpos-1);
|
|
#endif
|
|
break;
|
|
} else {
|
|
return -EINVAL;
|
|
|
|
}
|
|
break;
|
|
|
|
|
|
case ZT_TONEDETECT:
|
|
err = WAN_COPY_FROM_USER(&x, (int*)data, sizeof(int));
|
|
/*err = get_user(x, (int *)data);*/
|
|
if (err) return -EFAULT;
|
|
|
|
#if 0
|
|
|
|
if (!wc->vpm)
|
|
return -ENOSYS;
|
|
#endif
|
|
if (wr->dtmfsupport != WANOPT_YES){
|
|
return -ENOSYS;
|
|
}
|
|
DEBUG_EVENT("%s: Hardware Tone Event detection (%s:%s)!\n",
|
|
fe->name,
|
|
(x & ZT_TONEDETECT_ON) ? "ON" : "OFF",
|
|
(x & ZT_TONEDETECT_MUTE) ? "Mute ON" : "Mute OFF");
|
|
|
|
if (x & ZT_TONEDETECT_ON)
|
|
wr->dtmfmask |= (1 << (chan->chanpos - 1));
|
|
else
|
|
wr->dtmfmask &= ~(1 << (chan->chanpos - 1));
|
|
if (x & ZT_TONEDETECT_MUTE)
|
|
wr->dtmfmutemask |= (1 << (chan->chanpos - 1));
|
|
else
|
|
wr->dtmfmutemask &= ~(1 << (chan->chanpos - 1));
|
|
break;
|
|
|
|
default:
|
|
return -ENOTTY;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int wp_remora_zap_hooksig(struct zt_chan *chan, zt_txsig_t txsig)
|
|
{
|
|
wp_tdmv_remora_t *wr = chan->pvt;
|
|
sdla_t *card = NULL;
|
|
sdla_fe_t *fe = NULL;
|
|
|
|
WAN_ASSERT(wr->card == NULL);
|
|
card = wr->card;
|
|
fe = &card->fe;
|
|
|
|
if (fe->rm_param.mod[chan->chanpos - 1].type == MOD_TYPE_FXO) {
|
|
/* XXX Enable hooksig for FXO XXX */
|
|
switch(txsig) {
|
|
case ZT_TXSIG_START:
|
|
case ZT_TXSIG_OFFHOOK:
|
|
DEBUG_TDMV("%s: Module %d: goes off-hook (txsig %d)\n",
|
|
wr->devname, chan->chanpos, txsig);
|
|
wr->mod[chan->chanpos - 1].fxo.offhook = 1;
|
|
#if defined(REG_WRITE_SHADOW)
|
|
wr->reg0shadow[chan->chanpos-1] = 0x09;
|
|
wr->reg0shadow_update[chan->chanpos-1] = 1;
|
|
#else
|
|
WRITE_RM_REG(chan->chanpos - 1, 5, 0x9);
|
|
#endif
|
|
break;
|
|
case ZT_TXSIG_ONHOOK:
|
|
DEBUG_TDMV("%s: Module %d: goes on-hook (txsig %d)\n",
|
|
wr->devname, chan->chanpos, txsig);
|
|
wr->mod[chan->chanpos - 1].fxo.offhook = 0;
|
|
#if defined(REG_WRITE_SHADOW)
|
|
wr->reg0shadow[chan->chanpos-1] = 0x08;
|
|
wr->reg0shadow_update[chan->chanpos-1] = 1;
|
|
#else
|
|
WRITE_RM_REG(chan->chanpos - 1, 5, 0x8);
|
|
#endif
|
|
break;
|
|
default:
|
|
DEBUG_TDMV("%s: Can't set tx state to %d (chan %d)\n",
|
|
wr->devname, txsig, chan->chanpos);
|
|
}
|
|
}else if (fe->rm_param.mod[chan->chanpos - 1].type == MOD_TYPE_FXS) {
|
|
switch(txsig) {
|
|
case ZT_TXSIG_ONHOOK:
|
|
DEBUG_TDMV("%s: Module %d: goes on-hook (txsig %d).\n",
|
|
wr->devname, chan->chanpos, txsig);
|
|
switch(chan->sig) {
|
|
case ZT_SIG_EM:
|
|
case ZT_SIG_FXOKS:
|
|
case ZT_SIG_FXOLS:
|
|
wr->mod[chan->chanpos-1].fxs.lasttxhook =
|
|
fe->rm_param.mod[chan->chanpos-1].u.fxs.idletxhookstate;
|
|
break;
|
|
case ZT_SIG_FXOGS:
|
|
wr->mod[chan->chanpos-1].fxs.lasttxhook = 3;
|
|
break;
|
|
}
|
|
break;
|
|
case ZT_TXSIG_OFFHOOK:
|
|
DEBUG_TDMV("%s: Module %d: goes off-hook (txsig %d).\n",
|
|
wr->devname, chan->chanpos, txsig);
|
|
switch(chan->sig) {
|
|
case ZT_SIG_EM:
|
|
wr->mod[chan->chanpos-1].fxs.lasttxhook = 5;
|
|
break;
|
|
default:
|
|
wr->mod[chan->chanpos-1].fxs.lasttxhook =
|
|
fe->rm_param.mod[chan->chanpos-1].u.fxs.idletxhookstate;
|
|
break;
|
|
}
|
|
break;
|
|
case ZT_TXSIG_START:
|
|
DEBUG_TDMV("%s: Module %d: txsig START (txsig %d).\n",
|
|
wr->devname, chan->chanpos, txsig);
|
|
wr->mod[chan->chanpos-1].fxs.lasttxhook = 4;
|
|
break;
|
|
case ZT_TXSIG_KEWL:
|
|
wr->mod[chan->chanpos-1].fxs.lasttxhook = 0;
|
|
break;
|
|
default:
|
|
DEBUG_EVENT("%s: Can't set tx state to %d\n",
|
|
wr->devname, txsig);
|
|
return 0;
|
|
break;
|
|
}
|
|
#if defined(REG_WRITE_SHADOW)
|
|
wr->mod[chan->chanpos-1].fxs.lasttxhook_update = 1;
|
|
#else
|
|
WRITE_RM_REG(chan->chanpos - 1, 64, wr->mod[chan->chanpos-1].fxs.lasttxhook);
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int wp_remora_zap_open(struct zt_chan *chan)
|
|
{
|
|
wp_tdmv_remora_t *wr = NULL;
|
|
|
|
WAN_ASSERT2(chan == NULL, -ENODEV);
|
|
WAN_ASSERT2(chan->pvt == NULL, -ENODEV);
|
|
wr = chan->pvt;
|
|
wr->usecount++;
|
|
wan_set_bit(WP_TDMV_RUNNING, &wr->flags);
|
|
DEBUG_EVENT("%s: Open (usecount=%d, channo=%d, chanpos=%d)...\n",
|
|
wr->devname,
|
|
wr->usecount,
|
|
chan->channo,
|
|
chan->chanpos);
|
|
return 0;
|
|
}
|
|
|
|
static int wp_remora_zap_close(struct zt_chan *chan)
|
|
{
|
|
sdla_t *card = NULL;
|
|
wp_tdmv_remora_t* wr = NULL;
|
|
sdla_fe_t *fe = NULL;
|
|
|
|
WAN_ASSERT2(chan == NULL, -ENODEV);
|
|
WAN_ASSERT2(chan->pvt == NULL, -ENODEV);
|
|
wr = chan->pvt;
|
|
card = wr->card;
|
|
fe = &card->fe;
|
|
wr->usecount--;
|
|
wan_clear_bit(WP_TDMV_RUNNING, &wr->flags);
|
|
|
|
#if 1
|
|
if (fe->rm_param.mod[chan->chanpos - 1].type == MOD_TYPE_FXS) {
|
|
if (fe->fe_cfg.cfg.remora.reversepolarity)
|
|
fe->rm_param.mod[chan->chanpos - 1].u.fxs.idletxhookstate = 5;
|
|
else
|
|
fe->rm_param.mod[chan->chanpos - 1].u.fxs.idletxhookstate = 1;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int wp_remora_zap_watchdog(struct zt_span *span, int event)
|
|
{
|
|
#if 0
|
|
printk("TDM: Restarting DMA\n");
|
|
wctdm_restart_dma(span->pvt);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************
|
|
** wp_remora_zap_hwec() -
|
|
**
|
|
** OK
|
|
*/
|
|
static int wp_remora_zap_hwec(struct zt_chan *chan, int enable)
|
|
{
|
|
wp_tdmv_remora_t *wr = NULL;
|
|
sdla_t *card = NULL;
|
|
int channel = chan->chanpos;
|
|
int err = -ENODEV;
|
|
|
|
WAN_ASSERT2(chan == NULL, -ENODEV);
|
|
WAN_ASSERT2(chan->pvt == NULL, -ENODEV);
|
|
wr = chan->pvt;
|
|
WAN_ASSERT2(wr->card == NULL, -ENODEV);
|
|
card = wr->card;
|
|
|
|
if (card->wandev.ec_enable){
|
|
/* The ec persist flag enables and disables
|
|
* persistent echo control. In persist mode
|
|
* echo cancellation is enabled regardless of
|
|
* asterisk. In persist mode off asterisk
|
|
* controls hardware echo cancellation */
|
|
if (card->hwec_conf.persist_disable) {
|
|
err = card->wandev.ec_enable(card, enable, channel-1);
|
|
} else {
|
|
err = 0;
|
|
}
|
|
DEBUG_TDMV("[TDMV_RM]: %s: %s HW echo canceller on channel %d\n",
|
|
wr->devname,
|
|
(enable) ? "Enable" : "Disable",
|
|
channel);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static void wp_tdmv_remora_proslic_recheck_sanity(wp_tdmv_remora_t *wr, int mod_no)
|
|
{
|
|
sdla_t *card = NULL;
|
|
sdla_fe_t *fe = NULL;
|
|
int res;
|
|
|
|
WAN_ASSERT1(wr->card == NULL);
|
|
card = wr->card;
|
|
fe = &card->fe;
|
|
|
|
/* Check loopback */
|
|
#if 0
|
|
#if defined(REG_SHADOW)
|
|
res = wr->reg2shadow[mod_no];
|
|
#else
|
|
res = READ_RM_REG(mod_no, 8);
|
|
#endif
|
|
if (res) {
|
|
DEBUG_EVENT(
|
|
"%s: Module %d: Ouch, part reset, quickly restoring reality (%02X) -- Comment out\n",
|
|
wr->devname, mod_no, res);
|
|
wp_init_proslic(fe, mod_no, 1, 1);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if defined(REG_SHADOW)
|
|
res = wr->reg1shadow[mod_no];
|
|
#else
|
|
res = READ_RM_REG(mod_no, 64);
|
|
#endif
|
|
if (!res && (res != wr->mod[mod_no].fxs.lasttxhook)) {
|
|
#if defined(REG_SHADOW)
|
|
res = wr->reg2shadow[mod_no];
|
|
#else
|
|
res = READ_RM_REG(mod_no, 8);
|
|
#endif
|
|
if (res) {
|
|
DEBUG_EVENT(
|
|
"%s: Module %d: Ouch, part reset, quickly restoring reality\n",
|
|
wr->devname, mod_no);
|
|
wp_init_proslic(fe, mod_no, 1, 1);
|
|
} else {
|
|
if (wr->mod[mod_no].fxs.palarms++ < MAX_ALARMS) {
|
|
DEBUG_EVENT(
|
|
"%s: Module %d: Power alarm, resetting!\n",
|
|
wr->devname, mod_no + 1);
|
|
if (wr->mod[mod_no].fxs.lasttxhook == 4)
|
|
wr->mod[mod_no].fxs.lasttxhook = 1;
|
|
WRITE_RM_REG(mod_no, 64, wr->mod[mod_no].fxs.lasttxhook);
|
|
} else {
|
|
if (wr->mod[mod_no].fxs.palarms == MAX_ALARMS)
|
|
DEBUG_EVENT(
|
|
"%s: Module %d: Too many power alarms, NOT resetting!\n",
|
|
wr->devname, mod_no + 1);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void
|
|
wp_tdmv_remora_voicedaa_recheck_sanity(wp_tdmv_remora_t *wr, int mod_no)
|
|
{
|
|
sdla_t *card = NULL;
|
|
sdla_fe_t *fe = NULL;
|
|
int res;
|
|
|
|
WAN_ASSERT1(wr->card == NULL);
|
|
card = wr->card;
|
|
fe = &card->fe;
|
|
|
|
/* Check loopback */
|
|
#if defined(REG_SHADOW)
|
|
res = wr->reg2shadow[mod_no];
|
|
#else
|
|
res = READ_RM_REG(mod_no, 34);
|
|
#endif
|
|
if (!res) {
|
|
DEBUG_EVENT(
|
|
"%s: Module %d: Ouch, part reset, quickly restoring reality\n",
|
|
wr->devname, mod_no);
|
|
wp_init_voicedaa(fe, mod_no, 1, 1);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void wp_tdmv_remora_voicedaa_check_hook(wp_tdmv_remora_t *wr, int mod_no)
|
|
{
|
|
sdla_t *card = NULL;
|
|
sdla_fe_t *fe = NULL;
|
|
#ifndef AUDIO_RINGCHECK
|
|
unsigned char res;
|
|
#endif
|
|
signed char b;
|
|
int poopy = 0;
|
|
|
|
WAN_ASSERT1(wr->card == NULL);
|
|
card = wr->card;
|
|
fe = &card->fe;
|
|
|
|
/* Try to track issues that plague slot one FXO's */
|
|
#if defined(REG_SHADOW)
|
|
b = wr->reg0shadow[mod_no];
|
|
#else
|
|
b = READ_RM_REG(mod_no, 5);
|
|
#endif
|
|
if ((b & 0x2) || !(b & 0x8)) {
|
|
/* Not good -- don't look at anything else */
|
|
DEBUG_TDMV("%s: Module %d: Poopy (%02x)!\n",
|
|
wr->devname, mod_no + 1, b);
|
|
poopy++;
|
|
}
|
|
b &= 0x9b;
|
|
if (wr->mod[mod_no].fxo.offhook) {
|
|
if (b != 0x9){
|
|
WRITE_RM_REG(mod_no, 5, 0x9);
|
|
}
|
|
} else {
|
|
if (b != 0x8){
|
|
WRITE_RM_REG(mod_no, 5, 0x8);
|
|
}
|
|
}
|
|
if (poopy)
|
|
return;
|
|
#ifndef AUDIO_RINGCHECK
|
|
if (!wr->mod[mod_no].fxo.offhook) {
|
|
#if defined(REG_SHADOW)
|
|
res = wr->reg0shadow[mod_no];
|
|
#else
|
|
res = READ_RM_REG(mod_no, 5);
|
|
#endif
|
|
if ((res & 0x60) && wr->mod[mod_no].fxo.battery) {
|
|
wr->mod[mod_no].fxo.ringdebounce += (ZT_CHUNKSIZE * 16);
|
|
if (wr->mod[mod_no].fxo.ringdebounce >= ZT_CHUNKSIZE * 64) {
|
|
if (!wr->mod[mod_no].fxo.wasringing) {
|
|
wr->mod[mod_no].fxo.wasringing = 1;
|
|
wr->rxsig_state[mod_no] = ZT_RXSIG_RING;
|
|
zt_hooksig(&wr->chans[mod_no], ZT_RXSIG_RING);
|
|
DEBUG_TDMV("%s: Module %d: RING on span %d!\n",
|
|
wr->devname,
|
|
mod_no + 1,
|
|
wr->span.spanno);
|
|
}
|
|
wr->mod[mod_no].fxo.ringdebounce = ZT_CHUNKSIZE * 64;
|
|
}
|
|
} else {
|
|
wr->mod[mod_no].fxo.ringdebounce -= ZT_CHUNKSIZE * 4;
|
|
if (wr->mod[mod_no].fxo.ringdebounce <= 0) {
|
|
if (wr->mod[mod_no].fxo.wasringing) {
|
|
wr->mod[mod_no].fxo.wasringing = 0;
|
|
wr->rxsig_state[mod_no] = ZT_RXSIG_OFFHOOK;
|
|
zt_hooksig(&wr->chans[mod_no], ZT_RXSIG_OFFHOOK);
|
|
DEBUG_TDMV("%s: Module %d: NO RING on span %d!\n",
|
|
wr->devname,
|
|
mod_no + 1,
|
|
wr->span.spanno);
|
|
}
|
|
wr->mod[mod_no].fxo.ringdebounce = 0;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(REG_SHADOW)
|
|
b = wr->reg1shadow[mod_no];
|
|
#else
|
|
b = READ_RM_REG(mod_no, 29);
|
|
#endif
|
|
#if 0
|
|
{
|
|
static int count = 0;
|
|
if (!(count++ % 100)) {
|
|
printk("mod_no %d: Voltage: %d Debounce %d\n", mod_no + 1,
|
|
b, wr->mod[mod_no].fxo.battdebounce);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (abs(b) < wr->battthresh) {
|
|
wr->mod[mod_no].fxo.nobatttimer++;
|
|
#if 0
|
|
if (wr->mod[mod_no].fxo.battery)
|
|
printk("Battery loss: %d (%d debounce)\n", b, wr->mod[mod_no].fxo.battdebounce);
|
|
#endif
|
|
if (wr->mod[mod_no].fxo.battery && !wr->mod[mod_no].fxo.battdebounce) {
|
|
DEBUG_TDMV(
|
|
"%s: Module %d: NO BATTERY on span %d!\n",
|
|
wr->devname,
|
|
mod_no + 1,
|
|
wr->span.spanno);
|
|
wr->mod[mod_no].fxo.battery = 0;
|
|
#ifdef JAPAN
|
|
if ((!wr->mod[mod_no].fxo.ohdebounce) &&
|
|
wr->mod[mod_no].fxo.offhook) {
|
|
wr->rxsig_state[mod_no] = ZT_RXSIG_ONHOOK;
|
|
zt_hooksig(&wr->chans[mod_no], ZT_RXSIG_ONHOOK);
|
|
DEBUG_TDMV(
|
|
"%s: Module %d: Signalled On Hook span %d\n",
|
|
wr->devname,
|
|
mod_no + 1,
|
|
wr->span.spanno);
|
|
#ifdef ZERO_BATT_RING
|
|
wr->mod[mod_no].fxo.onhook++;
|
|
#endif
|
|
}
|
|
#else
|
|
DEBUG_TDMV(
|
|
"%s: Module %d: Signalled On Hook span %d\n",
|
|
wr->devname,
|
|
mod_no + 1,
|
|
wr->span.spanno);
|
|
zt_hooksig(&wr->chans[mod_no], ZT_RXSIG_ONHOOK);
|
|
#endif
|
|
wr->mod[mod_no].fxo.battdebounce = wr->battdebounce;
|
|
} else if (!wr->mod[mod_no].fxo.battery)
|
|
wr->mod[mod_no].fxo.battdebounce = wr->battdebounce;
|
|
} else if (abs(b) > wr->battthresh) {
|
|
if (!wr->mod[mod_no].fxo.battery && !wr->mod[mod_no].fxo.battdebounce) {
|
|
DEBUG_TDMV(
|
|
"%s: Module %d: BATTERY on span %d (%s)!\n",
|
|
wr->devname,
|
|
mod_no + 1,
|
|
wr->span.spanno,
|
|
(b < 0) ? "-" : "+");
|
|
#ifdef ZERO_BATT_RING
|
|
if (wr->mod[mod_no].fxo.onhook) {
|
|
wr->mod[mod_no].fxo.onhook = 0;
|
|
wr->rxsig_state[mod_no] = ZT_RXSIG_OFFHOOK;
|
|
zt_hooksig(&wr->chans[mod_no], ZT_RXSIG_OFFHOOK);
|
|
DEBUG_TDMV(
|
|
"%s: Module %d: Signalled Off Hook span %d\n",
|
|
wr->devname,
|
|
mod_no + 1,
|
|
wr->span.spanno);
|
|
}
|
|
#else
|
|
DEBUG_TDMV(
|
|
"%s: Module %d: Signalled Off Hook span %d\n",
|
|
wr->devname,
|
|
mod_no + 1,
|
|
wr->span.spanno);
|
|
wr->rxsig_state[mod_no] = ZT_RXSIG_OFFHOOK;
|
|
zt_hooksig(&wr->chans[mod_no], ZT_RXSIG_OFFHOOK);
|
|
#endif
|
|
wr->mod[mod_no].fxo.battery = 1;
|
|
wr->mod[mod_no].fxo.nobatttimer = 0;
|
|
wr->mod[mod_no].fxo.battdebounce = wr->battdebounce;
|
|
} else if (wr->mod[mod_no].fxo.battery)
|
|
wr->mod[mod_no].fxo.battdebounce = wr->battdebounce;
|
|
|
|
if (wr->mod[mod_no].fxo.lastpol >= 0) {
|
|
if (b < 0) {
|
|
wr->mod[mod_no].fxo.lastpol = -1;
|
|
wr->mod[mod_no].fxo.polaritydebounce = POLARITY_DEBOUNCE;
|
|
}
|
|
}
|
|
if (wr->mod[mod_no].fxo.lastpol <= 0) {
|
|
if (b > 0) {
|
|
wr->mod[mod_no].fxo.lastpol = 1;
|
|
wr->mod[mod_no].fxo.polaritydebounce = POLARITY_DEBOUNCE;
|
|
}
|
|
}
|
|
} else {
|
|
/* It's something else... */
|
|
wr->mod[mod_no].fxo.battdebounce = wr->battdebounce;
|
|
}
|
|
if (wr->mod[mod_no].fxo.battdebounce)
|
|
wr->mod[mod_no].fxo.battdebounce--;
|
|
if (wr->mod[mod_no].fxo.polaritydebounce) {
|
|
wr->mod[mod_no].fxo.polaritydebounce--;
|
|
if (wr->mod[mod_no].fxo.polaritydebounce < 1) {
|
|
if (wr->mod[mod_no].fxo.lastpol != wr->mod[mod_no].fxo.polarity) {
|
|
DEBUG_TDMV(
|
|
"%s: Module %d: Polarity reversed %d -> %d (%lu)\n",
|
|
wr->devname,
|
|
mod_no + 1,
|
|
wr->mod[mod_no].fxo.polarity,
|
|
wr->mod[mod_no].fxo.lastpol,
|
|
SYSTEM_TICKS);
|
|
if (wr->mod[mod_no].fxo.polarity){
|
|
zt_qevent_lock(&wr->chans[mod_no],
|
|
ZT_EVENT_POLARITY);
|
|
}
|
|
wr->mod[mod_no].fxo.polarity =
|
|
wr->mod[mod_no].fxo.lastpol;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void wp_tdmv_remora_proslic_check_hook(wp_tdmv_remora_t *wr, int mod_no)
|
|
{
|
|
sdla_t *card = NULL;
|
|
sdla_fe_t *fe = NULL;
|
|
int hook;
|
|
char res;
|
|
|
|
WAN_ASSERT1(wr->card == NULL);
|
|
card = wr->card;
|
|
fe = &card->fe;
|
|
/* For some reason we have to debounce the
|
|
hook detector. */
|
|
|
|
#if defined(REG_SHADOW)
|
|
res = wr->reg0shadow[mod_no];
|
|
#else
|
|
res = READ_RM_REG(mod_no, 68);
|
|
#endif
|
|
hook = (res & 1);
|
|
if (hook != wr->mod[mod_no].fxs.lastrxhook) {
|
|
/* Reset the debounce (must be multiple of 4ms) */
|
|
wr->mod[mod_no].fxs.debounce = 4 * (4 * 8);
|
|
DEBUG_TDMV(
|
|
"%s: Module %d: Resetting debounce hook %d, %d\n",
|
|
wr->devname, mod_no + 1, hook,
|
|
wr->mod[mod_no].fxs.debounce);
|
|
} else {
|
|
if (wr->mod[mod_no].fxs.debounce > 0) {
|
|
wr->mod[mod_no].fxs.debounce-= 16 * ZT_CHUNKSIZE;
|
|
DEBUG_TDMV(
|
|
"%s: Module %d: Sustaining hook %d, %d\n",
|
|
wr->devname, mod_no + 1,
|
|
hook, wr->mod[mod_no].fxs.debounce);
|
|
if (!wr->mod[mod_no].fxs.debounce) {
|
|
DEBUG_TDMV(
|
|
"%s: Module %d: Counted down debounce, newhook: %d\n",
|
|
wr->devname,
|
|
mod_no + 1,
|
|
hook);
|
|
wr->mod[mod_no].fxs.debouncehook = hook;
|
|
}
|
|
if (!wr->mod[mod_no].fxs.oldrxhook && wr->mod[mod_no].fxs.debouncehook) {
|
|
/* Off hook */
|
|
DEBUG_TDMV(
|
|
"%s: Module %d: Going off hook\n",
|
|
wr->devname, mod_no + 1);
|
|
wr->rxsig_state[mod_no] = ZT_RXSIG_OFFHOOK;
|
|
zt_hooksig(&wr->chans[mod_no], ZT_RXSIG_OFFHOOK);
|
|
#if 0
|
|
if (robust)
|
|
wp_init_proslic(wc, card, 1, 0, 1);
|
|
#endif
|
|
wr->mod[mod_no].fxs.oldrxhook = 1;
|
|
|
|
} else if (wr->mod[mod_no].fxs.oldrxhook && !wr->mod[mod_no].fxs.debouncehook) {
|
|
/* On hook */
|
|
DEBUG_TDMV(
|
|
"%s: Module %d: Going on hook\n",
|
|
wr->devname, mod_no + 1);
|
|
wr->rxsig_state[mod_no] = ZT_RXSIG_ONHOOK;
|
|
zt_hooksig(&wr->chans[mod_no], ZT_RXSIG_ONHOOK);
|
|
wr->mod[mod_no].fxs.oldrxhook = 0;
|
|
}
|
|
}
|
|
}
|
|
wr->mod[mod_no].fxs.lastrxhook = hook;
|
|
}
|
|
|
|
static int wp_tdmv_remora_check_hook(sdla_fe_t *fe, int mod_no)
|
|
{
|
|
sdla_t *card = fe->card;
|
|
wan_tdmv_t *wan_tdmv = NULL;
|
|
wp_tdmv_remora_t *wr = NULL;
|
|
|
|
wan_tdmv = &card->wan_tdmv;
|
|
WAN_ASSERT(wan_tdmv->sc == NULL);
|
|
wr = wan_tdmv->sc;
|
|
|
|
if (fe->rm_param.mod[mod_no].type == MOD_TYPE_FXS) {
|
|
wp_tdmv_remora_proslic_check_hook(wr, mod_no);
|
|
} else if (fe->rm_param.mod[mod_no].type == MOD_TYPE_FXO) {
|
|
wp_tdmv_remora_voicedaa_check_hook(wr, mod_no);
|
|
}
|
|
|
|
#if 0
|
|
if (wr->rxsig_state[mod_no] != wr->chans[mod_no].rxhooksig){
|
|
DEBUG_EVENT(
|
|
"%s: Module %d: WARNING: Update RX SIG state again %X (%X)\n",
|
|
wr->devname, mod_no + 1,
|
|
wr->rxsig_state[mod_no],
|
|
wr->chans[mod_no].rxhooksig);
|
|
zt_hooksig(&wr->chans[mod_no], wr->rxsig_state[mod_no]);
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int wp_tdmv_remora_hook(sdla_fe_t *fe, int mod_no, int off_hook)
|
|
{
|
|
sdla_t *card = fe->card;
|
|
wan_tdmv_t *wan_tdmv = NULL;
|
|
wp_tdmv_remora_t *wr = NULL;
|
|
|
|
wan_tdmv = &card->wan_tdmv;
|
|
WAN_ASSERT(wan_tdmv->sc == NULL);
|
|
wr = wan_tdmv->sc;
|
|
|
|
if (off_hook){
|
|
wr->rxsig_state[mod_no] = ZT_RXSIG_OFFHOOK;
|
|
zt_hooksig(&wr->chans[mod_no], ZT_RXSIG_OFFHOOK);
|
|
}else{
|
|
wr->rxsig_state[mod_no] = ZT_RXSIG_ONHOOK;
|
|
zt_hooksig(&wr->chans[mod_no], ZT_RXSIG_ONHOOK);
|
|
}
|
|
wr->mod[mod_no].fxs.lastrxhook = off_hook;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
** wp_tdmv_remora_init() -
|
|
**
|
|
** OK
|
|
*/
|
|
int wp_tdmv_remora_init(wan_tdmv_iface_t *iface)
|
|
{
|
|
WAN_ASSERT(iface == NULL);
|
|
|
|
memset(iface, 0, sizeof(wan_tdmv_iface_t));
|
|
iface->check_mtu = wp_tdmv_remora_check_mtu;
|
|
iface->create = wp_tdmv_remora_create;
|
|
iface->remove = wp_tdmv_remora_remove;
|
|
iface->reg = wp_tdmv_remora_reg;
|
|
iface->unreg = wp_tdmv_remora_unreg;
|
|
iface->software_init = wp_tdmv_remora_software_init;
|
|
iface->state = wp_tdmv_remora_state;
|
|
iface->running = wp_tdmv_remora_running;
|
|
iface->is_rbsbits = wp_tdmv_remora_is_rbsbits;
|
|
iface->rx_tx_span = wp_tdmv_remora_rx_tx_span;
|
|
iface->rx_chan = wp_tdmv_remora_rx_chan;
|
|
iface->ec_span = wp_tdmv_remora_ec_span;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int wp_tdmv_remora_software_init(wan_tdmv_t *wan_tdmv)
|
|
{
|
|
sdla_t *card = NULL;
|
|
sdla_fe_t *fe = NULL;
|
|
wp_tdmv_remora_t *wr = wan_tdmv->sc;
|
|
int x = 0, num = 0;
|
|
|
|
WAN_ASSERT(wr == NULL);
|
|
WAN_ASSERT(wr->card == NULL);
|
|
card = wr->card;
|
|
fe = &card->fe;
|
|
|
|
if (wan_test_bit(WP_TDMV_REGISTER, &wr->flags)){
|
|
DEBUG_EVENT(
|
|
"%s: Wanpipe device is already registered to Zaptel span # %d!\n",
|
|
wr->devname, wr->span.spanno);
|
|
return 0;
|
|
}
|
|
/* Zapata stuff */
|
|
sprintf(wr->span.name, "WRTDM/%d", wr->num);
|
|
sprintf(wr->span.desc, "wrtdm Board %d", wr->num + 1);
|
|
switch(fe->fe_cfg.tdmv_law){
|
|
case WAN_TDMV_ALAW:
|
|
DEBUG_EVENT(
|
|
"%s: ALAW override parameter detected. Device will be operating in ALAW\n",
|
|
wr->devname);
|
|
wr->span.deflaw = ZT_LAW_ALAW;
|
|
break;
|
|
case WAN_TDMV_MULAW:
|
|
wr->span.deflaw = ZT_LAW_MULAW;
|
|
break;
|
|
}
|
|
|
|
wr->battthresh = (fe->fe_cfg.cfg.remora.battthresh) ?
|
|
fe->fe_cfg.cfg.remora.battthresh : DEFAULT_BATT_THRESH;
|
|
wr->battdebounce = (fe->fe_cfg.cfg.remora.battdebounce) ?
|
|
fe->fe_cfg.cfg.remora.battdebounce : DEFAULT_BATT_DEBOUNCE;
|
|
DEBUG_EVENT("%s: Battery Threshhold %d (%d)\n",
|
|
wr->devname, wr->battthresh, DEFAULT_BATT_THRESH);
|
|
DEBUG_EVENT("%s: Battery Debounce %d (%d)\n",
|
|
wr->devname, wr->battdebounce, DEFAULT_BATT_DEBOUNCE);
|
|
wr->dtmfsupport = card->u.aft.tdmv_hw_dtmf;
|
|
|
|
for (x = 0; x < MAX_REMORA_MODULES; x++) {
|
|
if (wan_test_bit(x, &fe->rm_param.module_map)){
|
|
|
|
sprintf(wr->chans[x].name, "WRTDM/%d/%d", wr->num, x);
|
|
DEBUG_TDMV("%s: Configure Module %d for voice (%s, type %s)!\n",
|
|
wr->devname,
|
|
x + 1,
|
|
wr->chans[x].name,
|
|
WP_REMORA_DECODE_TYPE(fe->rm_param.mod[x].type));
|
|
if (fe->rm_param.mod[x].type == MOD_TYPE_FXO){
|
|
wr->chans[x].sigcap = ZT_SIG_FXSKS |
|
|
ZT_SIG_FXSLS |
|
|
ZT_SIG_SF |
|
|
ZT_SIG_CLEAR;
|
|
}else if (fe->rm_param.mod[x].type == MOD_TYPE_FXS){
|
|
wr->chans[x].sigcap = ZT_SIG_FXOKS |
|
|
ZT_SIG_FXOLS |
|
|
ZT_SIG_FXOGS |
|
|
ZT_SIG_SF |
|
|
ZT_SIG_EM |
|
|
ZT_SIG_CLEAR;
|
|
}
|
|
wr->chans[x].chanpos = x+1;
|
|
wr->chans[x].pvt = wr;
|
|
num++;
|
|
}else{
|
|
|
|
sprintf(wr->chans[x].name, "WRTDM/%d/%d", wr->num, x);
|
|
DEBUG_TEST("%s: Not used module %d!\n",
|
|
wr->devname,
|
|
x + 1);
|
|
wr->chans[x].sigcap = ZT_SIG_CLEAR;
|
|
wr->chans[x].chanpos = x+1;
|
|
wr->chans[x].pvt = wr;
|
|
num++;
|
|
}
|
|
wr->rxsig_state[x] = ZT_RXSIG_INITIAL;
|
|
}
|
|
wr->span.pvt = wr;
|
|
wr->span.chans = wr->chans;
|
|
wr->span.channels = num/*wr->max_timeslots*/;
|
|
wr->span.hooksig = wp_remora_zap_hooksig;
|
|
wr->span.open = wp_remora_zap_open;
|
|
wr->span.close = wp_remora_zap_close;
|
|
wr->span.flags = ZT_FLAG_RBS;
|
|
wr->span.ioctl = wp_remora_zap_ioctl;
|
|
wr->span.watchdog = wp_remora_zap_watchdog;
|
|
/* Set this pointer only if card has hw echo canceller module */
|
|
if (wr->hwec == WANOPT_YES && card->wandev.ec_dev){
|
|
wr->span.echocan = wp_remora_zap_hwec;
|
|
}
|
|
#if defined(__LINUX__)
|
|
init_waitqueue_head(&wr->span.maintq);
|
|
#endif
|
|
if (zt_register(&wr->span, 0)) {
|
|
DEBUG_EVENT("%s: Unable to register span with zaptel\n",
|
|
wr->devname);
|
|
return -EINVAL;
|
|
}
|
|
if (wr->span.spanno != wr->spanno +1){
|
|
DEBUG_EVENT("\n");
|
|
DEBUG_EVENT("WARNING: Span number %d is already used by another device!\n",
|
|
wr->spanno + 1);
|
|
DEBUG_EVENT(" Possible cause: Another TDM driver already loaded!\n");
|
|
DEBUG_EVENT(" Solution: Unload wanpipe and check currently\n");
|
|
DEBUG_EVENT(" used spans in /proc/zaptel directory.\n");
|
|
DEBUG_EVENT(" Reconfiguring device %s to new span number # %d\n",
|
|
wr->devname,wr->span.spanno);
|
|
DEBUG_EVENT("\n");
|
|
wr->spanno = wr->span.spanno-1;
|
|
}else{
|
|
DEBUG_EVENT("%s: Wanpipe device is registered to Zaptel span # %d!\n",
|
|
wr->devname, wr->span.spanno);
|
|
}
|
|
wp_tdmv_remora_check_mtu(card, wr->reg_module_map, &wr->max_rxtx_len);
|
|
wan_set_bit(WP_TDMV_REGISTER, &wr->flags);
|
|
|
|
/* Initialize Callback event function pointers */
|
|
if (wr->dtmfsupport == WANOPT_YES){
|
|
card->wandev.event_callback.dtmf = wp_tdmv_remora_dtmf;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************
|
|
** wp_tdmv_release() -
|
|
**
|
|
** OK
|
|
*/
|
|
static void wp_tdmv_release(wp_tdmv_remora_t *wr)
|
|
{
|
|
WAN_ASSERT1(wr == NULL);
|
|
if (wan_test_bit(WP_TDMV_REGISTER, &wr->flags)){
|
|
DEBUG_EVENT("%s: Unregister WAN FXS/FXO device from Zaptel!\n",
|
|
wr->devname);
|
|
wan_clear_bit(WP_TDMV_REGISTER, &wr->flags);
|
|
zt_unregister(&wr->span);
|
|
wan_clear_bit(WP_TDMV_REGISTER, &wr->flags);
|
|
}
|
|
wan_free(wr);
|
|
return;
|
|
}
|
|
|
|
|
|
static wp_tdmv_remora_t *wan_remora_search(sdla_t * card)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/******************************************************************************
|
|
** wp_tdmv_remora_check_mtu() -
|
|
**
|
|
** OK
|
|
*/
|
|
static int wp_tdmv_remora_check_mtu(void* pcard, unsigned long timeslot_map, int *mtu)
|
|
{
|
|
sdla_t *card = (sdla_t*)pcard;
|
|
int x, num_of_channels = 0, max_channels;
|
|
|
|
max_channels = WAN_FE_MAX_CHANNELS(&card->fe);
|
|
for (x = 0; x < max_channels; x++) {
|
|
if (wan_test_bit(x,×lot_map)){
|
|
num_of_channels++;
|
|
}
|
|
}
|
|
*mtu = ZT_CHUNKSIZE * num_of_channels;
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************
|
|
** wp_tdmv_remora_create() -
|
|
*tdmv_*
|
|
** OK
|
|
*/
|
|
static int wp_tdmv_remora_create(void* pcard, wan_tdmv_conf_t *tdmv_conf)
|
|
{
|
|
sdla_t *card = (sdla_t*)pcard;
|
|
wp_tdmv_remora_t *wr = NULL;
|
|
wan_tdmv_t *tmp = NULL;
|
|
|
|
WAN_ASSERT(card == NULL);
|
|
WAN_ASSERT(tdmv_conf->span_no == 0);
|
|
wr = wan_remora_search(card);
|
|
if (wr){
|
|
DEBUG_EVENT("%s: AFT remora FXO/FXS card already configured!\n",
|
|
card->devname);
|
|
return -EINVAL;
|
|
}
|
|
/* We are forcing to register wanpipe devices at the same sequence
|
|
* that it defines in /etc/zaptel.conf */
|
|
WAN_LIST_FOREACH(tmp, &wan_tdmv_head, next){
|
|
if (tmp->spanno == tdmv_conf->span_no){
|
|
DEBUG_EVENT("%s: Registering device with an incorrect span number!\n",
|
|
card->devname);
|
|
DEBUG_EVENT("%s: Another wanpipe device already configured to span #%d!\n",
|
|
card->devname, tdmv_conf->span_no);
|
|
return -EINVAL;
|
|
}
|
|
if (!WAN_LIST_NEXT(tmp, next)){
|
|
break;
|
|
}
|
|
}
|
|
|
|
memset(&card->wan_tdmv, 0x0, sizeof(wan_tdmv_t));
|
|
card->wan_tdmv.max_timeslots = card->fe.rm_param.max_fe_channels;
|
|
card->wan_tdmv.spanno = tdmv_conf->span_no;
|
|
card->wandev.fe_notify_iface.hook_state = wp_tdmv_remora_hook;
|
|
card->wandev.fe_notify_iface.check_hook_state = wp_tdmv_remora_check_hook;
|
|
|
|
wr = wan_malloc(sizeof(wp_tdmv_remora_t));
|
|
if (wr == NULL){
|
|
return -ENOMEM;
|
|
}
|
|
memset(wr, 0x0, sizeof(wp_tdmv_remora_t));
|
|
card->wan_tdmv.sc = wr;
|
|
wr->spanno = tdmv_conf->span_no-1;
|
|
wr->num = wp_remora_no++;
|
|
wr->card = card;
|
|
wr->devname = card->devname;
|
|
wr->max_timeslots = card->fe.rm_param.max_fe_channels;
|
|
wr->max_rxtx_len = 0;
|
|
wan_spin_lock_init(&wr->lock);
|
|
wan_spin_lock_init(&wr->tx_rx_lock);
|
|
|
|
if (tmp){
|
|
WAN_LIST_INSERT_AFTER(tmp, &card->wan_tdmv, next);
|
|
}else{
|
|
WAN_LIST_INSERT_HEAD(&wan_tdmv_head, &card->wan_tdmv, next);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
** wp_tdmv_reg() -
|
|
**
|
|
** Returns: 0-31 - Return TDM Voice channel number.
|
|
** -EINVAL - otherwise
|
|
** OK
|
|
*/
|
|
static int wp_tdmv_remora_reg( void *pcard,
|
|
wan_tdmv_if_conf_t *tdmv_conf,
|
|
unsigned int active_ch,
|
|
unsigned char ec_enable,
|
|
netdevice_t *dev)
|
|
{
|
|
sdla_t *card = (sdla_t*)pcard;
|
|
sdla_fe_t *fe = &card->fe;
|
|
wan_tdmv_t *wan_tdmv = &card->wan_tdmv;
|
|
wp_tdmv_remora_t *wr = NULL;
|
|
int i, channo = 0;
|
|
|
|
WAN_ASSERT(wan_tdmv->sc == NULL);
|
|
wr = wan_tdmv->sc;
|
|
|
|
if (wan_test_bit(WP_TDMV_REGISTER, &wr->flags)){
|
|
DEBUG_EVENT(
|
|
"%s: Error: Master device has already been configured!\n",
|
|
card->devname);
|
|
return -EINVAL;
|
|
}
|
|
|
|
for(i = 0; i < wr->max_timeslots; i++){
|
|
if (wan_test_bit(i, &active_ch) &&
|
|
wan_test_bit(i, &fe->rm_param.module_map)){
|
|
if (tdmv_conf->tdmv_echo_off){
|
|
wan_set_bit(i, &wr->echo_off_map);
|
|
}
|
|
channo = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == wr->max_timeslots){
|
|
DEBUG_EVENT(
|
|
"%s: Error: TDMV iface %s failed to configure for %08X timeslots!\n",
|
|
card->devname,
|
|
wan_netif_name(dev),
|
|
active_ch);
|
|
return -EINVAL;
|
|
}
|
|
|
|
DEBUG_EVENT(
|
|
"%s: Registering TDMV %s iface to module %d!\n",
|
|
card->devname,
|
|
WP_REMORA_DECODE_TYPE(fe->rm_param.mod[channo].type),
|
|
channo+1);
|
|
wan_set_bit(channo, &wr->reg_module_map);
|
|
|
|
if (tdmv_conf->tdmv_echo_off){
|
|
DEBUG_EVENT("%s: TDMV Echo Ctrl:Off\n",
|
|
wr->devname);
|
|
}
|
|
memset(wr->chans[channo].sreadchunk, WAN_TDMV_IDLE_FLAG, ZT_CHUNKSIZE);
|
|
memset(wr->chans[channo].swritechunk, WAN_TDMV_IDLE_FLAG, ZT_CHUNKSIZE);
|
|
wr->chans[channo].readchunk = wr->chans[channo].sreadchunk;
|
|
wr->chans[channo].writechunk = wr->chans[channo].swritechunk;
|
|
wr->channelized = WAN_TRUE;
|
|
wr->hwec = ec_enable;
|
|
wp_tdmv_remora_check_mtu(card, active_ch, &wr->max_rxtx_len);
|
|
return channo;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
** wp_tdmv_unreg() -
|
|
**
|
|
** OK
|
|
*/
|
|
static int wp_tdmv_remora_unreg(void* pcard, unsigned long ts_map)
|
|
{
|
|
sdla_t *card = (sdla_t*)pcard;
|
|
sdla_fe_t *fe = &card->fe;
|
|
wan_tdmv_t *wan_tdmv = &card->wan_tdmv;
|
|
wp_tdmv_remora_t *wr = NULL;
|
|
int channo = 0;
|
|
|
|
WAN_ASSERT(wan_tdmv->sc == NULL);
|
|
wr = wan_tdmv->sc;
|
|
|
|
for(channo = 0; channo < wr->max_timeslots; channo++){
|
|
if (wan_test_bit(channo, &wr->reg_module_map)){
|
|
DEBUG_EVENT(
|
|
"%s: Unregistering TDMV %s iface from module %d!\n",
|
|
card->devname,
|
|
WP_REMORA_DECODE_TYPE(fe->rm_param.mod[channo].type),
|
|
channo+1);
|
|
wan_clear_bit(channo, &wr->reg_module_map);
|
|
wan_clear_bit(channo, &wr->echo_off_map);
|
|
memset(wr->chans[channo].sreadchunk,
|
|
WAN_TDMV_IDLE_FLAG,
|
|
ZT_CHUNKSIZE);
|
|
memset(wr->chans[channo].swritechunk,
|
|
WAN_TDMV_IDLE_FLAG,
|
|
ZT_CHUNKSIZE);
|
|
wr->chans[channo].readchunk =
|
|
wr->chans[channo].sreadchunk;
|
|
wr->chans[channo].writechunk =
|
|
wr->chans[channo].swritechunk;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
** wp_tdmv_remove() -
|
|
**
|
|
** OK
|
|
*/
|
|
static int wp_tdmv_remora_remove(void* pcard)
|
|
{
|
|
sdla_t *card = (sdla_t*)pcard;
|
|
wan_tdmv_t *wan_tdmv = &card->wan_tdmv;
|
|
wp_tdmv_remora_t *wr = NULL;
|
|
|
|
if (!card->wan_tdmv.sc){
|
|
return 0;
|
|
}
|
|
|
|
wr = wan_tdmv->sc;
|
|
/* Release span, possibly delayed */
|
|
if (wr && wr->reg_module_map){
|
|
DEBUG_EVENT(
|
|
"%s: Some interfaces are not unregistered (%08lX)!\n",
|
|
card->devname,
|
|
wr->reg_module_map);
|
|
return -EINVAL;
|
|
}
|
|
if (wr && wr->usecount){
|
|
DEBUG_EVENT("%s: ERROR: Wanpipe is still used by Asterisk!\n",
|
|
card->devname);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (wr){
|
|
wan_clear_bit(WP_TDMV_RUNNING, &wr->flags);
|
|
wan_clear_bit(WP_TDMV_UP, &wr->flags);
|
|
wan_tdmv->sc = NULL;
|
|
WAN_LIST_REMOVE(wan_tdmv, next);
|
|
wp_tdmv_release(wr);
|
|
}else{
|
|
wan_tdmv->sc = NULL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int wp_tdmv_remora_state(void* pcard, int state)
|
|
{
|
|
sdla_t *card = (sdla_t*)pcard;
|
|
wan_tdmv_t *wan_tdmv = &card->wan_tdmv;
|
|
wp_tdmv_remora_t *wr = NULL;
|
|
|
|
WAN_ASSERT(wan_tdmv->sc == NULL);
|
|
wr = (wp_tdmv_remora_t*)wan_tdmv->sc;
|
|
|
|
switch(state){
|
|
case WAN_CONNECTED:
|
|
DEBUG_TDMV("%s: TDMV Remora state is CONNECTED!\n",
|
|
wr->devname);
|
|
wan_set_bit(WP_TDMV_UP, &wr->flags);
|
|
break;
|
|
|
|
case WAN_DISCONNECTED:
|
|
DEBUG_TDMV("%s: TDMV Remora state is DISCONNECTED!\n",
|
|
wr->devname);
|
|
wan_clear_bit(WP_TDMV_UP, &wr->flags);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************
|
|
** wp_tdmv_running() -
|
|
**
|
|
** OK
|
|
*/
|
|
static int wp_tdmv_remora_running(void* pcard)
|
|
{
|
|
sdla_t *card = (sdla_t*)pcard;
|
|
wan_tdmv_t *wan_tdmv = &card->wan_tdmv;
|
|
wp_tdmv_remora_t *wr = NULL;
|
|
|
|
wr = wan_tdmv->sc;
|
|
if (wr && wr->usecount){
|
|
DEBUG_EVENT("%s: WARNING: Wanpipe is still used by Asterisk!\n",
|
|
card->devname);
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************
|
|
** wp_tdmv_remora_is_rbsbits() -
|
|
**
|
|
** OK
|
|
*/
|
|
static int wp_tdmv_remora_is_rbsbits(wan_tdmv_t *wan_tdmv)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************
|
|
** wp_tdmv_rx_chan() -
|
|
**
|
|
** OK
|
|
*/
|
|
static int wp_tdmv_remora_rx_chan(wan_tdmv_t *wan_tdmv, int channo,
|
|
unsigned char *rxbuf,
|
|
unsigned char *txbuf)
|
|
{
|
|
wp_tdmv_remora_t *wr = wan_tdmv->sc;
|
|
#ifdef CONFIG_PRODUCT_WANPIPE_TDM_VOICE_ECHOMASTER
|
|
wan_tdmv_rxtx_pwr_t *pwr_rxtx = NULL;
|
|
#endif
|
|
sdla_t *card;
|
|
|
|
WAN_ASSERT2(wr == NULL, -EINVAL);
|
|
WAN_ASSERT2(channo < 0, -EINVAL);
|
|
WAN_ASSERT2(channo > 31, -EINVAL);
|
|
|
|
if (!IS_TDMV_UP(wr)){
|
|
return -EINVAL;
|
|
}
|
|
card = wr->card;
|
|
|
|
#ifdef CONFIG_PRODUCT_WANPIPE_TDM_VOICE_ECHOMASTER
|
|
pwr_rxtx = &wan_tdmv->chan_pwr[channo];
|
|
#endif
|
|
|
|
#if 0
|
|
DEBUG_EVENT("Module %d: RX: %02X %02X %02X %02X %02X %02X %02X %02X\n",
|
|
channo,
|
|
rxbuf[0],
|
|
rxbuf[1],
|
|
rxbuf[2],
|
|
rxbuf[3],
|
|
rxbuf[4],
|
|
rxbuf[5],
|
|
rxbuf[6],
|
|
rxbuf[7]
|
|
);
|
|
#endif
|
|
wr->chans[channo].readchunk = rxbuf;
|
|
wr->chans[channo].writechunk = txbuf;
|
|
|
|
#ifdef CONFIG_PRODUCT_WANPIPE_TDM_VOICE_ECHOMASTER
|
|
wp_tdmv_echo_check(wan_tdmv, &wr->chans[channo], channo);
|
|
#endif
|
|
|
|
if ((!card->wandev.ec_enable || card->wandev.ec_enable_map == 0) &&
|
|
!wan_test_bit(channo, &wr->echo_off_map)) {
|
|
|
|
/*Echo spike starts at 25bytes*/
|
|
#ifdef CONFIG_PRODUCT_WANPIPE_TDM_VOICE_ECHOMASTER
|
|
if(pwr_rxtx->current_state != ECHO_ABSENT){
|
|
#endif
|
|
|
|
if (wan_test_bit(AFT_TDM_SW_RING_BUF,&card->u.aft.chip_cfg_status)) {
|
|
/* Updated for SWRING buffer
|
|
* Sets up the spike at 3 bytes */
|
|
zt_ec_chunk(
|
|
&wr->chans[channo],
|
|
wr->chans[channo].readchunk,
|
|
wr->chans[channo].writechunk);
|
|
} else {
|
|
/* This should be used without SWRING Echo spike starts at 9 bytes*/
|
|
zt_ec_chunk(
|
|
&wr->chans[channo],
|
|
wr->chans[channo].readchunk,
|
|
wr->ec_chunk1[channo]);
|
|
memcpy(
|
|
wr->ec_chunk1[channo],
|
|
wr->chans[channo].writechunk,
|
|
ZT_CHUNKSIZE);
|
|
}
|
|
|
|
#if 0
|
|
/*Echo spike starts at bytes*/
|
|
zt_ec_chunk(
|
|
&wr->chans[channo],
|
|
wr->chans[channo].readchunk,
|
|
wr->ec_chunk1[channo]);
|
|
memcpy(
|
|
wr->ec_chunk1[channo],
|
|
wr->ec_chunk2[channo],
|
|
ZT_CHUNKSIZE);
|
|
|
|
memcpy(
|
|
wr->ec_chunk2[channo],
|
|
wr->chans[channo].writechunk,
|
|
ZT_CHUNKSIZE);
|
|
#endif
|
|
|
|
#ifdef CONFIG_PRODUCT_WANPIPE_TDM_VOICE_ECHOMASTER
|
|
} /*if(pwr_rxtx->current_state != ECHO_ABSENT) */
|
|
#endif
|
|
} /* if (!wan_test_bit(channo, &wr->echo_off_map)) */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int wp_tdmv_remora_rx_tx_span(void *pcard)
|
|
{
|
|
sdla_t *card = (sdla_t*)pcard;
|
|
sdla_fe_t *fe = &card->fe;
|
|
wan_tdmv_t *wan_tdmv = &card->wan_tdmv;
|
|
wp_tdmv_remora_t *wr = NULL;
|
|
u_int16_t x;
|
|
|
|
WAN_ASSERT(wan_tdmv->sc == NULL);
|
|
wr = wan_tdmv->sc;
|
|
|
|
wr->intcount++;
|
|
for (x = 0; x < wr->max_timeslots; x++) {
|
|
if (!wan_test_bit(x, &wr->reg_module_map)){
|
|
continue;
|
|
}
|
|
if (fe->rm_param.mod[x].type == MOD_TYPE_FXS){
|
|
#if defined(REG_WRITE_SHADOW)
|
|
if (wr->mod[x].fxs.lasttxhook_update){
|
|
WRITE_RM_REG(x, 64, wr->mod[x].fxs.lasttxhook);
|
|
wr->mod[x].fxs.lasttxhook_update = 0;
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
if (wr->mod[x].fxs.lasttxhook == 0x4) {
|
|
/* RINGing, prepare for OHT */
|
|
wr->mod[x].fxs.ohttimer = OHT_TIMER << 3;
|
|
if (fe->fe_cfg.cfg.remora.reversepolarity){
|
|
/* OHT mode when idle */
|
|
fe->rm_param.mod[x].u.fxs.idletxhookstate = 0x6;
|
|
}else{
|
|
fe->rm_param.mod[x].u.fxs.idletxhookstate = 0x2;
|
|
}
|
|
} else {
|
|
if (wr->mod[x].fxs.ohttimer) {
|
|
wr->mod[x].fxs.ohttimer-= ZT_CHUNKSIZE;
|
|
if (!wr->mod[x].fxs.ohttimer) {
|
|
if (fe->fe_cfg.cfg.remora.reversepolarity){
|
|
/* Switch to active */
|
|
fe->rm_param.mod[x].u.fxs.idletxhookstate = 0x5;
|
|
}else{
|
|
fe->rm_param.mod[x].u.fxs.idletxhookstate = 0x1;
|
|
}
|
|
if ((wr->mod[x].fxs.lasttxhook == 0x2) || (wr->mod[x].fxs.lasttxhook == 0x6)) {
|
|
/* Apply the change if appropriate */
|
|
if (fe->fe_cfg.cfg.remora.reversepolarity){
|
|
wr->mod[x].fxs.lasttxhook = 0x5;
|
|
}else{
|
|
wr->mod[x].fxs.lasttxhook = 0x1;
|
|
}
|
|
WRITE_RM_REG(x, 64, wr->mod[x].fxs.lasttxhook);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} else if (fe->rm_param.mod[x].type == MOD_TYPE_FXO) {
|
|
|
|
if (wr->mod[x].fxo.echotune){
|
|
DEBUG_EVENT("%s: Module %d: Setting echo registers: \n",
|
|
fe->name, x);
|
|
|
|
/* Set the ACIM register */
|
|
WRITE_RM_REG(x, 30, wr->mod[x].fxo.echoregs.acim);
|
|
|
|
/* Set the digital echo canceller registers */
|
|
WRITE_RM_REG(x, 45, wr->mod[x].fxo.echoregs.coef1);
|
|
WRITE_RM_REG(x, 46, wr->mod[x].fxo.echoregs.coef2);
|
|
WRITE_RM_REG(x, 47, wr->mod[x].fxo.echoregs.coef3);
|
|
WRITE_RM_REG(x, 48, wr->mod[x].fxo.echoregs.coef4);
|
|
WRITE_RM_REG(x, 49, wr->mod[x].fxo.echoregs.coef5);
|
|
WRITE_RM_REG(x, 50, wr->mod[x].fxo.echoregs.coef6);
|
|
WRITE_RM_REG(x, 51, wr->mod[x].fxo.echoregs.coef7);
|
|
WRITE_RM_REG(x, 52, wr->mod[x].fxo.echoregs.coef8);
|
|
|
|
DEBUG_EVENT("%s: Module %d: Set echo registers successfully\n",
|
|
fe->name, x);
|
|
wr->mod[x].fxo.echotune = 0;
|
|
}
|
|
#if defined(REG_WRITE_SHADOW)
|
|
if (wr->reg0shadow_update[x]){
|
|
/* Read first shadow reg */
|
|
WRITE_RM_REG(x, 5, wr->reg0shadow[x]);
|
|
wr->reg0shadow_update[x] = 0;
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef PULSE_DIALING
|
|
/*
|
|
** Alex 31 Mar, 2006
|
|
** Check for HOOK status every interrupt
|
|
** (in pulse mode is very critical) */
|
|
wp_tdmv_remora_check_hook(fe, x);
|
|
#endif
|
|
}
|
|
|
|
x = wr->intcount % MAX_REMORA_MODULES;
|
|
if (wan_test_bit(x, &wr->reg_module_map)) {
|
|
#if defined(REG_SHADOW)
|
|
if (fe->rm_param.mod[x].type == MOD_TYPE_FXS) {
|
|
/* Read first shadow reg */
|
|
wr->reg0shadow[x] = READ_RM_REG(x, 68);
|
|
/* Read second shadow reg */
|
|
wr->reg1shadow[x] = READ_RM_REG(x, 64);
|
|
/* Read third shadow reg */
|
|
wr->reg2shadow[x] = READ_RM_REG(x, 8);
|
|
}else if (fe->rm_param.mod[x].type == MOD_TYPE_FXO) {
|
|
/* Read first shadow reg */
|
|
wr->reg0shadow[x] = READ_RM_REG(x, 5);
|
|
/* Read second shadow reg */
|
|
wr->reg1shadow[x] = READ_RM_REG(x, 29);
|
|
/* Read third shadow reg */
|
|
wr->reg2shadow[x] = READ_RM_REG(x, 34);
|
|
}
|
|
#endif
|
|
|
|
#ifndef PULSE_DIALING
|
|
wp_tdmv_remora_check_hook(fe, x);
|
|
#endif
|
|
if (!(wr->intcount & 0xf0)){
|
|
if (fe->rm_param.mod[x].type == MOD_TYPE_FXS) {
|
|
wp_tdmv_remora_proslic_recheck_sanity(wr, x);
|
|
}else if (fe->rm_param.mod[x].type == MOD_TYPE_FXO) {
|
|
wp_tdmv_remora_voicedaa_recheck_sanity(wr, x);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(wr->intcount % 10000)) {
|
|
/* Accept an alarm once per 10 seconds */
|
|
for (x = 0; x < wr->max_timeslots; x++)
|
|
if (wan_test_bit(x, &wr->reg_module_map) &&
|
|
(fe->rm_param.mod[x].type == MOD_TYPE_FXS)) {
|
|
if (wr->mod[x].fxs.palarms){
|
|
wr->mod[x].fxs.palarms--;
|
|
}
|
|
}
|
|
}
|
|
|
|
zt_receive(&wr->span);
|
|
zt_transmit(&wr->span);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int wp_tdmv_remora_ec_span(void *pcard)
|
|
{
|
|
sdla_t *card = (sdla_t*)pcard;
|
|
wan_tdmv_t *wan_tdmv = &card->wan_tdmv;
|
|
wp_tdmv_remora_t *wr = NULL;
|
|
|
|
WAN_ASSERT(wan_tdmv->sc == NULL);
|
|
wr = wan_tdmv->sc;
|
|
|
|
zt_ec_span(&wr->span);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static void wp_tdmv_remora_dtmf (void* card_id, wan_event_t *event)
|
|
{
|
|
sdla_t *card = (sdla_t*)card_id;
|
|
wan_tdmv_t *wan_tdmv = &card->wan_tdmv;
|
|
wp_tdmv_remora_t *wr = NULL;
|
|
|
|
WAN_ASSERT1(wan_tdmv->sc == NULL);
|
|
wr = wan_tdmv->sc;
|
|
|
|
if (event->type == WAN_EVENT_EC_DTMF){
|
|
DEBUG_EVENT("%s: Received DTMF Event at TDM RM (%d:%c:%s:%s)!\n",
|
|
card->devname,
|
|
event->channel,
|
|
event->digit,
|
|
(event->dtmf_port == WAN_EC_CHANNEL_PORT_ROUT)?"ROUT":"SOUT",
|
|
(event->dtmf_type == WAN_EC_TONE_PRESENT)?"PRESENT":"STOP");
|
|
}else if (event->type == WAN_EVENT_RM_DTMF){
|
|
DEBUG_EVENT("%s: Received DTMF Event at TDM RM (%d:%c)!\n",
|
|
card->devname,
|
|
event->channel,
|
|
event->digit);
|
|
}
|
|
|
|
if (!(wr->dtmfmask & (1 << (event->channel-1)))){
|
|
DEBUG_TDMV("%s: DTMF is not enabled for the channel %d\n",
|
|
card->devname,
|
|
event->channel);
|
|
return;
|
|
}
|
|
if (event->dtmf_type == WAN_EC_TONE_PRESENT){
|
|
wr->dtmfactive |= (1 << event->channel);
|
|
zt_qevent_lock(
|
|
&wr->span.chans[event->channel-1],
|
|
(ZT_EVENT_DTMFDOWN | event->digit));
|
|
}else{
|
|
wr->dtmfactive &= ~(1 << event->channel);
|
|
zt_qevent_lock(
|
|
&wr->span.chans[event->channel-1],
|
|
(ZT_EVENT_DTMFUP | event->digit));
|
|
}
|
|
return;
|
|
}
|