wanpipe/patches/kdrivers/src/net/sdla_bri_tdmv.c

1476 lines
37 KiB
C

/***************************************************************************
* sdla_bri_tdmv.c WANPIPE(tm) Multiprotocol WAN Link Driver.
* AFT BRI support.
*
* Author: David Rokhvarg <davidr@sangoma.com>
*
* Copyright: (c) 1984-2007 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.
*
* ============================================================================
* June 5, 2007 David Rokhvarg Initial version.
* Sep 06, 2008 Moises Silva DAHDI support.
******************************************************************************
*/
/*******************************************************************************
** 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_bri.h>
# include <zapcompat.h> /* Map of Zaptel -> DAHDI definitions */
#else
# include <zapcompat.h> /* Map of Zaptel -> DAHDI definitions */
# include <linux/wanpipe_includes.h>
# include <linux/wanpipe_defines.h>
# include <linux/wanpipe.h>
# include <linux/sdla_bri.h>
# include <linux/if_wanpipe_common.h>
# include <linux/sdla_aft_te1.h>
# include <linux/sdla_tdmv.h>
#endif
/*******************************************************************************
** DEFINES AND MACROS
*******************************************************************************/
#define DEBUG_TDMV_BRI if(0)DEBUG_EVENT
#define REG_SHADOW
#define REG_WRITE_SHADOW
#undef PULSE_DIALING
/* 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_bri_ {
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_BRI_LINES];
int spanno;
struct zt_span span;
#ifdef DAHDI_ISSUES
#ifdef DAHDI_22
struct dahdi_echocan_state ec[MAX_BRI_LINES]; /* echocan state for each channel */
#endif
struct zt_chan *chans_ptrs[MAX_BRI_LINES];
#endif
struct zt_chan chans[MAX_BRI_LINES];
unsigned long reg_module_map; /* Registered modules */
u32 timeslot_map;
/* 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_BRI_LINES */
int max_rxtx_len;
int channelized;
unsigned long echo_off_map;
int rxsig_state[MAX_BRI_LINES];
int txsig_state[MAX_BRI_LINES]; /* not used */
u_int8_t dtmfsupport;
unsigned int dtmfactive;
unsigned int dtmfmask;
unsigned int dtmfmutemask;
/* BRI D-chan */
unsigned int dchan;
netdevice_t *dchan_dev;
} wp_tdmv_bri_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_bri_check_mtu(void* pcard, unsigned long timeslot_map, int *mtu);
static int wp_tdmv_bri_create(void* pcard, wan_tdmv_conf_t *tdmv_conf);
static int wp_tdmv_bri_remove(void* pcard);
static int wp_tdmv_bri_reg(
void *pcard,
wan_tdmv_if_conf_t *tdmv_conf,
unsigned int active_ch,
u8 ec_enable,
netdevice_t *dev);
static int wp_tdmv_bri_unreg(void* pcard, unsigned long ts_map);
static int wp_tdmv_bri_software_init(wan_tdmv_t *wan_tdmv);
static int wp_tdmv_bri_state(void* pcard, int state);
static int wp_tdmv_bri_running(void* pcard);
static int wp_tdmv_bri_is_rbsbits(wan_tdmv_t *wan_tdmv);
static int wp_tdmv_bri_rx_tx_span(void *pcard);
static int wp_tdmv_bri_rx_chan(wan_tdmv_t*, int,unsigned char*,unsigned char*);
static int wp_tdmv_bri_ec_span(void *pcard);
static void wp_tdmv_bri_dtmf (void* card_id, wan_event_t *event);
#ifdef DAHDI_22
static int wp_tdmv_bri_hwec_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec);
static void wp_tdmv_bri_hwec_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec);
#else
static int wp_bri_zap_hwec(struct zt_chan *chan, int enable);
#endif
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);
#if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN) && defined(ZT_DCHAN_TX)
static int wp_tdmv_tx_dchan(struct zt_chan *chan, int len);
#endif
static int wp_tdmv_rx_dchan(wan_tdmv_t*, int, unsigned char*, unsigned int);
static int wp_tdmv_span_buf_rotate(void *pcard, u32, unsigned long, int);
static void wp_tdmv_report_alarms(void* pcard, unsigned long te_alarm);
#ifdef DAHDI_22
/*
*******************************************************************************
** DAHDI HWEC STRUCTURES
*******************************************************************************
*/
static const struct dahdi_echocan_features wp_tdmv_bri_ec_features = {
.NLP_automatic = 1,
.CED_tx_detect = 1,
.CED_rx_detect = 1,
};
static const struct dahdi_echocan_ops wp_tdmv_bri_ec_ops = {
.name = "WANPIPE_HWEC",
.echocan_free = wp_tdmv_bri_hwec_free,
};
#endif
/*******************************************************************************
** FUNCTION DEFINITIONS
*******************************************************************************/
static int
#if defined(__FreeBSD__) || defined(__OpenBSD__)
wp_bri_zap_ioctl(struct zt_chan *chan, unsigned int cmd, caddr_t data)
#else
wp_bri_zap_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data)
#endif
{
#if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN) && defined(ZT_DCHAN_TX)
wp_tdmv_bri_t *wp = NULL;
#endif
int err = -ENOTTY;
DEBUG_TDMV_BRI("%s(): line: %d\n", __FUNCTION__, __LINE__);
switch(cmd)
{
#if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN) && defined(ZT_DCHAN_TX)
case ZT_DCHAN_TX:
DEBUG_TDMV_BRI("chan: 0x%p\n", chan);
if(chan){
DEBUG_TDMV_BRI("chan->pvt: 0x%p\n", chan->pvt);
}
WAN_ASSERT(chan == NULL || chan->pvt == NULL);
wp = chan->pvt;
if (wp->dchan_dev && wp->dchan_dev->hard_start_xmit){
wp_tdmv_tx_dchan(chan, (int)data);
err=0;
}else{
DEBUG_TDMV_BRI("%s(): line: %d\n", __FUNCTION__, __LINE__);
DEBUG_TDMV_BRI("wp->dchan_dev: 0x%p\n", wp->dchan_dev);
if(wp->dchan_dev){
DEBUG_TDMV_BRI("wp->dchan_dev->hard_start_xmit: 0x%p\n",
wp->dchan_dev->hard_start_xmit);
}
err=-EOPNOTSUPP;
}
break;
#endif
default:
DEBUG_EVENT("%s(): uknown cmd!\n", __FUNCTION__);
err = -ENOTTY;
break;
}
return err;
}
static int wp_bri_zap_open(struct zt_chan *chan)
{
wp_tdmv_bri_t *wr = NULL;
sdla_t *card;
BRI_FUNC();
WAN_ASSERT2(chan == NULL, -ENODEV);
WAN_ASSERT2(chan->pvt == NULL, -ENODEV);
wr = chan->pvt;
WAN_ASSERT2(wr->card == NULL, -ENODEV);
card = wr->card;
wr->usecount++;
wanpipe_open(card);
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_bri_zap_close(struct zt_chan *chan)
{
sdla_t *card = NULL;
wp_tdmv_bri_t* wr = NULL;
sdla_fe_t *fe = NULL;
BRI_FUNC();
WAN_ASSERT2(chan == NULL, -ENODEV);
WAN_ASSERT2(chan->pvt == NULL, -ENODEV);
wr = chan->pvt;
WAN_ASSERT2(wr->card == NULL, -ENODEV);
card = wr->card;
wanpipe_close(card);
fe = &card->fe;
wr->usecount--;
wan_clear_bit(WP_TDMV_RUNNING, &wr->flags);
return 0;
}
static int wp_bri_zap_watchdog(struct zt_span *span, int event)
{
BRI_FUNC();
#if 0
printk("TDM: Restarting DMA\n");
wctdm_restart_dma(span->pvt);
#endif
return 0;
}
#ifdef DAHDI_22
/******************************************************************************
** wp_tdmv_bri_hwec_create() -
**
** OK
*/
static int wp_tdmv_bri_hwec_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec)
{
wp_tdmv_bri_t *wr = NULL;
sdla_t *card = NULL;
int err = -ENODEV;
if (ecp->param_count > 0) {
DEBUG_TDMV("[TDMV] Wanpipe echo canceller does not support parameters; failing request\n");
return -EINVAL;
}
BRI_FUNC();
WAN_ASSERT2(chan == NULL, -ENODEV);
WAN_ASSERT2(chan->pvt == NULL, -ENODEV);
wr = chan->pvt;
WAN_ASSERT2(wr->card == NULL, -ENODEV);
card = wr->card;
*ec = &wr->ec[chan->chanpos-1];
(*ec)->ops = &wp_tdmv_bri_ec_ops;
(*ec)->features = wp_tdmv_bri_ec_features;
if (card->wandev.ec_enable) {
DEBUG_EVENT("[TDMV_BRI]: %s: %s(): channel %d\n",
wr->devname, __FUNCTION__, chan->chanpos);
if(chan->chanpos == 1 || chan->chanpos == 2) {
err = card->wandev.ec_enable(card, 1, chan->chanpos);
} else {
DEBUG_EVENT("[TDMV_BRI]: %s: %s(): Warning: invalid fe_channel %d!!\n",
wr->devname, __FUNCTION__, chan->chanpos);
err = 0;
}
} else {
DEBUG_EVENT("[TDMV_BRI]: %s: %s(): card->wandev.ec_enable == NULL!!!!!!\n",
wr->devname, __FUNCTION__);
}
return err;
}
/******************************************************************************
** wp_tdmv_bri_hwec_free() -
**
** OK
*/
static void wp_tdmv_bri_hwec_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec)
{
wp_tdmv_bri_t *wr = NULL;
sdla_t *card = NULL;
memset(ec, 0, sizeof(*ec));
if(chan == NULL) return;
if(chan->pvt == NULL) return;
wr = chan->pvt;
if(wr->card == NULL) return;
card = wr->card;
if (card->wandev.ec_enable) {
DEBUG_EVENT("[TDMV_BRI]: %s: %s(): channel %d\n",
wr->devname, __FUNCTION__, chan->chanpos);
if(chan->chanpos == 1 || chan->chanpos == 2) {
card->wandev.ec_enable(card, 0, chan->chanpos);
} else {
DEBUG_EVENT("[TDMV_BRI]: %s: %s(): Warning: invalid fe_channel %d!!\n",
wr->devname, __FUNCTION__, chan->chanpos);
}
} else {
DEBUG_EVENT("[TDMV_BRI]: %s: %s(): card->wandev.ec_enable == NULL!!!!!!\n",
wr->devname, __FUNCTION__);
}
}
#else
/******************************************************************************
** wp_remora_zap_hwec() -
**
** OK
*/
static int wp_bri_zap_hwec(struct zt_chan *chan, int enable)
{
wp_tdmv_bri_t *wr = NULL;
sdla_t *card = NULL;
int err = -ENODEV;
sdla_fe_t *fe = NULL;
BRI_FUNC();
WAN_ASSERT2(chan == NULL, -ENODEV);
WAN_ASSERT2(chan->pvt == NULL, -ENODEV);
wr = chan->pvt;
WAN_ASSERT2(wr->card == NULL, -ENODEV);
card = wr->card;
fe = &card->fe;
if (card->wandev.ec_enable){
DEBUG_EVENT("[TDMV_BRI]: %s: %s(): channel %d\n",
wr->devname, __FUNCTION__, chan->chanpos);
if(chan->chanpos == 1 || chan->chanpos == 2){
err = card->wandev.ec_enable(card, enable, chan->chanpos);
}else{
DEBUG_EVENT("[TDMV_BRI]: %s: %s(): Warning: invalid fe_channel %d!!\n",
wr->devname, __FUNCTION__, chan->chanpos);
err = 0;
}
}else{
DEBUG_EVENT("[TDMV_BRI]: %s: %s(): card->wandev.ec_enable == NULL!!!!!!\n",
wr->devname, __FUNCTION__);
}
return err;
}
#endif
/******************************************************************************
** wp_tdmv_rbsbits_poll() -
**
** DONE
*/
static int wp_tdmv_rbsbits_poll(wan_tdmv_t *wan_tdmv, void *card1)
{
wp_tdmv_bri_t *wp = NULL;
BRI_FUNC();
WAN_ASSERT(wan_tdmv->sc == NULL);
wp = wan_tdmv->sc;
return 0;
}
/******************************************************************************
** wp_tdmv_bri_init() -
**
** OK
*/
int wp_tdmv_bri_init(wan_tdmv_iface_t *iface)
{
BRI_FUNC();
WAN_ASSERT(iface == NULL);
memset(iface, 0, sizeof(wan_tdmv_iface_t));
iface->check_mtu = wp_tdmv_bri_check_mtu;
iface->create = wp_tdmv_bri_create;
iface->remove = wp_tdmv_bri_remove;
iface->reg = wp_tdmv_bri_reg;
iface->unreg = wp_tdmv_bri_unreg;
iface->software_init = wp_tdmv_bri_software_init;
iface->state = wp_tdmv_bri_state;
iface->running = wp_tdmv_bri_running;
iface->is_rbsbits = wp_tdmv_bri_is_rbsbits;
iface->rx_tx_span = wp_tdmv_bri_rx_tx_span;
iface->rx_chan = wp_tdmv_bri_rx_chan;
iface->ec_span = wp_tdmv_bri_ec_span;
iface->rbsbits_poll = wp_tdmv_rbsbits_poll; //?????
iface->rx_dchan = wp_tdmv_rx_dchan;
iface->buf_rotate = wp_tdmv_span_buf_rotate;
return 0;
}
static int wp_tdmv_bri_software_init(wan_tdmv_t *wan_tdmv)
{
sdla_t *card = NULL;
sdla_fe_t *fe = NULL;
wp_tdmv_bri_t *wr = wan_tdmv->sc;
int x = 0, num = 0;
BRI_FUNC();
WAN_ASSERT(wr == NULL);
WAN_ASSERT(wr->card == NULL);
card = wr->card;
fe = &card->fe;
if (wan_test_bit(WP_TDMV_REGISTER, &wr->flags)){
WP_DELAY(1000);
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, "WPBRI/%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->span.deflaw = ZT_LAW_ALAW;//FIXME: hardcoded
wr->dtmfsupport = card->u.aft.tdmv_hw_dtmf;
for (x = 0; x < MAX_BRI_TIMESLOTS; x++) {
sprintf(wr->chans[x].name, "WPBRI/%d/%d", wr->num, x);
wr->chans[x].sigcap = ZT_SIG_EM | ZT_SIG_CLEAR | ZT_SIG_FXSLS |
ZT_SIG_FXSGS | ZT_SIG_FXSKS | ZT_SIG_FXOLS |
ZT_SIG_FXOGS | ZT_SIG_FXOKS | ZT_SIG_CAS |
ZT_SIG_SF;
switch(x)
{
case 0:
case 1:
DEBUG_EVENT("%s: Port %d Configure B-chan %d for voice (%s, type %s)!\n",
wr->devname,
WAN_FE_LINENO(fe) + 1,
x + 1,
wr->chans[x].name,
WP_BRI_DECODE_MOD_TYPE(fe->bri_param.mod[WAN_FE_LINENO(fe)].type));
break;
case 2:
DEBUG_EVENT("%s: Port %d Configure BRI D-chan (%s, type %s)!\n",
wr->devname,
WAN_FE_LINENO(fe) + 1,
wr->chans[x].name,
WP_BRI_DECODE_MOD_TYPE(fe->bri_param.mod[WAN_FE_LINENO(fe)].type));/* fe->bri_param.mod[2], there is no [2] */
break;
}/* switch() */
wr->chans[x].chanpos = x+1;
wr->chans[x].pvt = wr;
num++;
//wr->rxsig_state[x] = ZT_RXSIG_INITIAL;
}/* for() */
wr->span.pvt = wr;
#ifdef DAHDI_ISSUES
wr->span.chans = wr->chans_ptrs;
#else
wr->span.chans = wr->chans;
#endif
wr->span.channels = MAX_BRI_TIMESLOTS;/* this is the number of b-chans (2) and the d-chan on one BRI line. */;
wr->span.linecompat = ZT_CONFIG_AMI | ZT_CONFIG_CCS; /* <--- this is really BS */
wr->span.open = wp_bri_zap_open;
wr->span.close = wp_bri_zap_close;
//wr->span.flags = ZT_FLAG_RBS;
wr->span.ioctl = wp_bri_zap_ioctl;
wr->span.watchdog = wp_bri_zap_watchdog;
/* Set this pointer only if card has hw echo canceller module */
if (card->wandev.ec_dev){
#ifdef DAHDI_22
wr->span.echocan_create = wp_tdmv_bri_hwec_create;
#else
wr->span.echocan = wp_bri_zap_hwec;
#endif
}
#if defined(__LINUX__)
init_waitqueue_head(&wr->span.maintq);
#endif
WP_DELAY(1000);
if (zt_register(&wr->span, 0)) {
BRI_FUNC();
DEBUG_EVENT("%s: Unable to register span with zaptel\n",
wr->devname);
WP_DELAY(1000);
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_bri_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){
DEBUG_EVENT("ADBG> Updating event callback dtmf\n");
card->wandev.event_callback.dtmf = wp_tdmv_bri_dtmf;
}
WP_DELAY(1000);
wr->span.alarms = ZT_ALARM_NONE;//FIXME: report real line state, also report alarms on line connect/disconnect.
zt_alarm_notify(&wr->span);
return 0;
}
/******************************************************************************
** wp_tdmv_release() -
**
** OK
*/
static void wp_tdmv_release(wp_tdmv_bri_t *wr)
{
BRI_FUNC();
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_bri_t *wan_remora_search(sdla_t * card)
{
BRI_FUNC();
return NULL;
}
/******************************************************************************
** wp_tdmv_bri_check_mtu() -
**
** OK
*/
static int wp_tdmv_bri_check_mtu(void* pcard, unsigned long timeslot_map, int *mtu)
{
BRI_FUNC();
*mtu = ZT_CHUNKSIZE;
return 0;
}
/******************************************************************************
** wp_tdmv_bri_create() -
*tdmv_*
** OK
*/
static int wp_tdmv_bri_create(void* pcard, wan_tdmv_conf_t *tdmv_conf)
{
sdla_t *card = (sdla_t*)pcard;
wp_tdmv_bri_t *wr = NULL;
wan_tdmv_t *tmp = NULL;
#ifdef DAHDI_ISSUES
int i;
#endif
BRI_FUNC();
WAN_ASSERT(card == NULL);
WAN_ASSERT(tdmv_conf->span_no == 0);
wr = wan_remora_search(card);
if (wr){
DEBUG_EVENT("%s: AFT remora card already configured!\n",
card->devname);
return -EINVAL;
}
memset(&card->wan_tdmv, 0x0, sizeof(wan_tdmv_t));
/* 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.bri_param.max_fe_channels;
card->wan_tdmv.spanno = tdmv_conf->span_no;
card->wandev.te_report_alarms = wp_tdmv_report_alarms;
wr = wan_malloc(sizeof(wp_tdmv_bri_t));
if (wr == NULL){
return -ENOMEM;
}
memset(wr, 0x0, sizeof(wp_tdmv_bri_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.bri_param.max_fe_channels;
wr->max_rxtx_len = 0;
wan_spin_lock_init(&wr->lock, "wan_britdmv_lock");
wan_spin_lock_init(&wr->tx_rx_lock, "wan_britdmv_txrx_lock");
#ifdef DAHDI_ISSUES
for (i = 0; i < sizeof(wr->chans)/sizeof(wr->chans[0]); i++) {
wr->chans_ptrs[i] = &wr->chans[i];
}
#endif
/* BRI signalling is selected with hw HDLC (dchan is not 0) */
wr->dchan = 3;/* MUST be 3! */
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_bri_reg() - calculate 'channo' based on 'activ_ch' for the
** interface.
**
**
** Returns: 0-31 - Return TDM Voice channel number.
** -EINVAL - otherwise
** OK
*/
static int wp_tdmv_bri_reg(
void *pcard,
wan_tdmv_if_conf_t *tdmv_conf,
unsigned int active_ch,
u8 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_bri_t *wr = NULL;
int i, channo = 0;
BRI_FUNC();
WAN_ASSERT(wan_tdmv->sc == NULL);
wr = wan_tdmv->sc;
DEBUG_BRI("%s(): FE line no: %d, active_ch: 0x%X\n",
__FUNCTION__, WAN_FE_LINENO(fe), active_ch);
if(WAN_FE_LINENO(fe) >= MAX_BRI_LINES){
DEBUG_EVENT(
"%s: %s(): Error: invalid FE line number (%d)!\n",
card->devname, __FUNCTION__, WAN_FE_LINENO(fe));
return -EINVAL;
}
if (wan_test_bit(WP_TDMV_REGISTER, &wr->flags)){
DEBUG_EVENT(
"%s: Error: Master device has already been configured!\n",
card->devname);
return -EINVAL;
}
DEBUG_BRI("%s(): wr->max_timeslots: %d\n", __FUNCTION__, wr->max_timeslots);
/* The Zaptel Channel Number for BRI must be adjusted based on
the module number to start from 0 */
active_ch = active_ch>>(WAN_FE_LINENO(fe)*2);
for(i = 0; i < wr->max_timeslots; i++){
if (wan_test_bit(i, &active_ch)){
if (tdmv_conf->tdmv_echo_off){
wan_set_bit(i, &wr->echo_off_map);
}
channo = i;
break;
}
}
DEBUG_BRI("1 %s(): channo: %d, wr->timeslot_map: 0x%X\n",
__FUNCTION__, channo, wr->timeslot_map);
if(channo > 2){
DEBUG_EVENT(
"%s: Error: TDMV iface %s failed to configure for channo %d! Must be between 0 and 2 (including).\n",
card->devname,
wan_netif_name(dev),
channo);
return -EINVAL;
}
/* for BRI only bits 0 and 1 can be set! */
wan_set_bit(channo, &wr->timeslot_map);
if (i == wr->max_timeslots){
DEBUG_EVENT("%s: Error: TDMV iface %s failed to configure for 0x%08X timeslots!\n",
card->devname,
wan_netif_name(dev),
active_ch);
return -EINVAL;
}
DEBUG_EVENT("%s: Registering TDMV to %s module %d!\n",
card->devname,
WP_BRI_DECODE_MOD_TYPE(fe->bri_param.mod[WAN_FE_LINENO(fe)].type),
WAN_FE_LINENO(fe) + 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;
wp_tdmv_bri_check_mtu(card, active_ch, &wr->max_rxtx_len);
DEBUG_BRI("%s(): card->u.aft.tdmv_dchan: %d, channo: %d, wr->dchan: %d\n",
__FUNCTION__, card->u.aft.tdmv_dchan, channo, wr->dchan);
if(wr->dchan != 3){
DEBUG_EVENT("%s:%s: Error: 'dchan' (%d) not equal 3!\n",
card->devname, wan_netif_name(dev), wr->dchan);
return -EINVAL;
}
if((channo + 1) == wr->dchan){ /* 3 */
DEBUG_BRI("%s(): registering BRI D-chan 'dev' pointer\n", __FUNCTION__);
wr->dchan_dev = dev;
card->u.aft.tdmv_dchan = BRI_DCHAN_LOGIC_CHAN;
}
return channo;
}
/******************************************************************************
** wp_tdmv_unreg() -
**
** OK
*/
static int wp_tdmv_bri_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_bri_t *wr = NULL;
int channo = 0;
BRI_FUNC();
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_BRI_DECODE_MOD_TYPE(fe->bri_param.mod[0].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_bri_remove(void* pcard)
{
sdla_t *card = (sdla_t*)pcard;
wan_tdmv_t *wan_tdmv = &card->wan_tdmv;
wp_tdmv_bri_t *wr = NULL;
BRI_FUNC();
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;
card->wandev.te_report_alarms = NULL;
WAN_LIST_REMOVE(wan_tdmv, next);
wp_tdmv_release(wr);
}else{
wan_tdmv->sc = NULL;
}
return 0;
}
static int wp_tdmv_span_buf_rotate(void *pcard, u32 buf_sz, unsigned long mask, int circ_buf_len)
{
sdla_t *card = (sdla_t*)pcard;
wan_tdmv_t *wan_tdmv = &card->wan_tdmv;
wp_tdmv_bri_t *wp = NULL;
int x;
unsigned int rx_offset, tx_offset;
void *ptr;
WAN_ASSERT(wan_tdmv->sc == NULL);
wp = wan_tdmv->sc;
#define BRI_NUM_OF_BCHANNELS 2
for (x = 0; x < BRI_NUM_OF_BCHANNELS; x ++) {
/*for (x = 0; x < 32; x ++) {*/
if (wan_test_bit(x,&wp->timeslot_map)) {
rx_offset = buf_sz * card->u.aft.tdm_rx_dma_toggle[x];
tx_offset = buf_sz * card->u.aft.tdm_tx_dma_toggle[x];
card->u.aft.tdm_rx_dma_toggle[x]++;
if (card->u.aft.tdm_rx_dma_toggle[x] >= circ_buf_len) {
card->u.aft.tdm_rx_dma_toggle[x]=0;
}
card->u.aft.tdm_tx_dma_toggle[x]++;
if (card->u.aft.tdm_tx_dma_toggle[x] >= circ_buf_len) {
card->u.aft.tdm_tx_dma_toggle[x]=0;
}
wan_spin_lock(&wp->chans[x].lock);
ptr=(void*)((((unsigned long)wp->chans[x].readchunk) & ~(mask)) + rx_offset);
wp->chans[x].readchunk = ptr;
ptr=(void*)((((unsigned long)wp->chans[x].writechunk) & ~(mask)) + tx_offset);
wp->chans[x].writechunk = ptr;
wan_spin_unlock(&wp->chans[x].lock);
#if defined(__LINUX__)
prefetch(wp->chans[x].readchunk);
prefetch(wp->chans[x].writechunk);
#endif
if (card->wandev.rtp_len && card->wandev.rtp_tap) {
card->wandev.rtp_tap(card,x,
wp->chans[x].readchunk,
wp->chans[x].writechunk,
ZT_CHUNKSIZE);
}
}
}
return 0;
}
static int wp_tdmv_bri_state(void* pcard, int state)
{
sdla_t *card = (sdla_t*)pcard;
wan_tdmv_t *wan_tdmv = &card->wan_tdmv;
wp_tdmv_bri_t *wr = NULL;
BRI_FUNC();
WAN_ASSERT(wan_tdmv->sc == NULL);
wr = (wp_tdmv_bri_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_bri_running(void* pcard)
{
sdla_t *card = (sdla_t*)pcard;
wan_tdmv_t *wan_tdmv = &card->wan_tdmv;
wp_tdmv_bri_t *wr = NULL;
BRI_FUNC();
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_bri_is_rbsbits() -
**
** OK
*/
static int wp_tdmv_bri_is_rbsbits(wan_tdmv_t *wan_tdmv)
{
return 0;
}
/******************************************************************************
** wp_tdmv_rx_chan() -
**
** OK
*/
static int wp_tdmv_bri_rx_chan(wan_tdmv_t *wan_tdmv, int channo,
unsigned char *rxbuf,
unsigned char *txbuf)
{
wp_tdmv_bri_t *wr = wan_tdmv->sc;
#ifdef CONFIG_PRODUCT_WANPIPE_TDM_VOICE_ECHOMASTER
wan_tdmv_rxtx_pwr_t *pwr_rxtx = NULL;
#endif
//DEBUG_TDMV_BRI("channo: %d\n", channo);
//return 0;
WAN_ASSERT2(wr == NULL, -EINVAL);
WAN_ASSERT2(channo < 0, -EINVAL);
WAN_ASSERT2(channo > 31, -EINVAL);
if (!IS_TDMV_UP(wr)){
return -EINVAL;
}
#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 (!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 0
/* Echo spike starts at 16 bytes */
zt_ec_chunk(
&wr->chans[channo],
wr->chans[channo].readchunk,
wr->chans[channo].writechunk);
#endif
#if 1
/*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);
#endif
#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_bri_rx_tx_span(void *pcard)
{
sdla_t *card = (sdla_t*)pcard;
wan_tdmv_t *wan_tdmv = &card->wan_tdmv;
wp_tdmv_bri_t *wr = NULL;
/*BRI_FUNC();*/
WAN_ASSERT(wan_tdmv->sc == NULL);
wr = wan_tdmv->sc;
wr->intcount++;
zt_receive(&wr->span);
zt_transmit(&wr->span);
return 0;
}
static int wp_tdmv_bri_ec_span(void *pcard)
{
sdla_t *card = (sdla_t*)pcard;
wan_tdmv_t *wan_tdmv = &card->wan_tdmv;
wp_tdmv_bri_t *wr = NULL;
//BRI_FUNC();
WAN_ASSERT(wan_tdmv->sc == NULL);
wr = wan_tdmv->sc;
zt_ec_span(&wr->span);
return 0;
}
static void wp_tdmv_bri_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_bri_t *wr = NULL;
BRI_FUNC();
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 BRI (%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 BRI (%d:%c)!\n",
card->devname,
event->channel,
event->digit);
}
if (!(wr->dtmfmask & (1 << (event->channel-1)))){
DEBUG_EVENT("%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);
#ifdef DAHDI_ISSUES
zt_qevent_lock(
wr->span.chans[event->channel-1],
(ZT_EVENT_DTMFDOWN | event->digit));
#else
zt_qevent_lock(
&wr->span.chans[event->channel-1],
(ZT_EVENT_DTMFDOWN | event->digit));
#endif
}else{
wr->dtmfactive &= ~(1 << event->channel);
#ifdef DAHDI_ISSUES
zt_qevent_lock(
wr->span.chans[event->channel-1],
(ZT_EVENT_DTMFUP | event->digit));
#else
zt_qevent_lock(
&wr->span.chans[event->channel-1],
(ZT_EVENT_DTMFUP | event->digit));
#endif
}
return;
}
#if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN) && defined(ZT_DCHAN_TX)
static int wp_tdmv_tx_dchan(struct zt_chan *chan, int len)
{
wp_tdmv_bri_t *wp = NULL;
netskb_t *skb = NULL;
wan_smp_flag_t smp_flags;
unsigned char *data = NULL;
int err = 0;
WAN_ASSERT2(chan == NULL, -ENODEV);
WAN_ASSERT2(chan->pvt == NULL, -ENODEV);
wp = chan->pvt;
WAN_ASSERT(wp->dchan_dev == NULL);
if (len <= 2){
return -EINVAL;
}
len -= 2; /* Remove checksum */
skb = wan_skb_alloc(len+1);
if (skb == NULL){
return -ENOMEM;
}
data = wan_skb_put(skb, len);
wan_spin_lock_irq(&chan->lock, &smp_flags);
memcpy(data, chan->writebuf[chan->inwritebuf], len);
wan_spin_unlock_irq(&chan->lock, &smp_flags);
#if 0
{
int i;
DEBUG_EVENT("%s: TX DCHAN len: %d\n", chan->name, len);
/*
for(i = 0; i < len; i++){
_DEBUG_EVENT("%02X ", data[i]);
}
_DEBUG_EVENT("\n");
*/
}
#endif
if (skb){
err = wp->dchan_dev->hard_start_xmit(skb, wp->dchan_dev);
if (err){
wan_skb_free(skb);
}
}
return err;
}
#endif
/******************************************************************************
** wp_tdmv_rx_dchan() -
**
** OK
*/
static int wp_tdmv_rx_dchan(wan_tdmv_t *wan_tdmv, int channo,
unsigned char *rxbuf, unsigned int len)
{
wp_tdmv_bri_t *wp = wan_tdmv->sc;
struct zt_chan *chan = NULL, *ms = NULL;
wan_smp_flag_t smp_flags;
unsigned char *buf = NULL;
int oldbuf;
int i, left;
DEBUG_TDMV("%s(): channo: %d, wp->dchan: %d, len: %d\n", __FUNCTION__, channo, wp->dchan, len);
WAN_ASSERT(wp == NULL);
WAN_ASSERT(channo != wp->dchan-1);
chan = &wp->chans[wp->dchan-1];
WAN_ASSERT(chan == NULL || chan->master == NULL);
ms = chan->master;
BRI_FUNC();
if (!IS_TDMV_UP(wp)){
DEBUG_TDMV("%s: Asterisk is not running!\n",
wp->devname);
return -EINVAL;
}
if (!(ms->flags & ZT_FLAG_HDLC)){
DEBUG_TDMV("%s: ERROR: %s not defined as D-CHAN! Or received HDLC data before 'ztcfg' was run.\n",
wp->devname, ms->name);
return -EINVAL;
}
if (ms->inreadbuf < 0){
return -EINVAL;
}
if (ms->inreadbuf >= ZT_MAX_NUM_BUFS){
DEBUG_EVENT("%s: RX buffer (%s) is out of range (%d-%d)!\n",
wp->devname, ms->name, ms->inreadbuf,ZT_MAX_NUM_BUFS);
return -EINVAL;
}
/* FIXME wan_spin_lock_irqsave(&wp->tx_rx_lock, smp_flags); */
wan_spin_lock_irq(&chan->lock, &smp_flags);
buf = ms->readbuf[ms->inreadbuf];
left = ms->blocksize - ms->readidx[ms->inreadbuf];
if (len + 2 > left) {
DEBUG_EVENT("%s: ERROR: Not ehough space for RX HDLC packet (%d:%d)!\n",
wp->devname, len+2, left);
wan_spin_unlock_irq(&chan->lock, &smp_flags);
return -EINVAL;
}
for(i = 0; i < len; i++){
buf[ms->readidx[ms->inreadbuf]++] = rxbuf[i];
}
/* Add extra 2 bytes for checksum */
buf[ms->readidx[ms->inreadbuf]++] = 0x00;
buf[ms->readidx[ms->inreadbuf]++] = 0x00;
oldbuf = ms->inreadbuf;
ms->readn[ms->inreadbuf] = ms->readidx[ms->inreadbuf];
ms->inreadbuf = (ms->inreadbuf + 1) % ms->numbufs;
if (ms->inreadbuf == ms->outreadbuf) {
/* Whoops, we're full, and have no where else
to store into at the moment. We'll drop it
until there's a buffer available */
ms->inreadbuf = -1;
/* Enable the receiver in case they've got POLICY_WHEN_FULL */
ms->rxdisable = 0;
}
if (ms->outreadbuf < 0) { /* start out buffer if not already */
ms->outreadbuf = oldbuf;
}
/* FIXME wan_spin_unlock_irq(&wp->tx_rx_lock, &smp_flags); */
wan_spin_unlock_irq(&chan->lock, &smp_flags);
if (!ms->rxdisable) { /* if receiver enabled */
DEBUG_TDMV("%s: HDLC block is ready!\n",
wp->devname);
/* Notify a blocked reader that there is data available
to be read, unless we're waiting for it to be full */
#if defined(__LINUX__)
wake_up_interruptible(&ms->readbufq);
wake_up_interruptible(&ms->sel);
if (ms->iomask & ZT_IOMUX_READ)
wake_up_interruptible(&ms->eventbufq);
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
wakeup(&ms->readbufq);
wakeup(&ms->sel);
if (ms->iomask & ZT_IOMUX_READ)
wakeup(&ms->eventbufq);
#endif
}
return 0;
}
/******************************************************************************
** wp_tdmv_report_alarms() -
**
** DONE
*/
static void wp_tdmv_report_alarms(void* pcard, unsigned long te_alarm)
{
sdla_t *card = (sdla_t*)pcard;
wan_tdmv_t *wan_tdmv = &card->wan_tdmv;
wp_tdmv_bri_t *wp = wan_tdmv->sc;
/* The sc pointer can be NULL, on shutdown. In this
* case don't generate error, just get out */
if (!wp){
return;
}
if(te_alarm == 0){
wp->span.alarms = ZT_ALARM_NONE;
}else{
wp->span.alarms = ZT_ALARM_RED;
}
zt_alarm_notify(&wp->span);
return;
}