/*************************************************************************** * sdla_tdmv.c WANPIPE(tm) Multiprotocol WAN Link Driver. * TDM voice board configuration. * * Author: Alex Feldman * Nenad Corbic * David Rokvargh * * Copyright: (c) 1995-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. * ============================================================================ * Jul 22, 2001 Nenad Corbic Initial version. * Oct 01, 2001 Gideon Hack Modifications for interrupt usage. * Aug 9, 2005 David Rokhvarg Added Echo Detection and Control (EDAC). ****************************************************************************** */ /* **********CONFIGURING******************************************************************** INCLUDE FILES ****************************************************************************** */ #if defined(__FreeBSD__) || defined(__OpenBSD__) # include # include # include # include # include # include # include /* WANPIPE TDM Voice definitions */ # include #elif (defined __WINDOWS__) # include #else # include # include # include # include # include /* WANPIPE TDM Voice definitions */ #endif /* ****************************************************************************** DEFINES AND MACROS ****************************************************************************** */ #define DEBUG_ECHO if(0) DEBUG_EVENT #define WP_MAX_CARDS 32 #define FIXME_MSG(func) DEBUG_EVENT("(%s): FIXME: line %d\n", func, __LINE__) #define DBG_FUNC_START(func) DEBUG_EVENT("(DEBUG)(TDM Voice): %s - Start\n", func) #define DBG_FUNC_END(func) DEBUG_EVENT("(DEBUG)(TDM Voice): %s - End\n", func) /* flags bits */ #define WP_TDMV_REGISTER 1 /*0x01*/ #define WP_TDMV_RUNNING 2 /*0x02*/ #define WP_TDMV_UP 3 /*0x04*/ #define WP_TDMV_SIG_ENABLE 4 /*0x08*/ #define WP_TDMV_SIG_POLL 5 /*0x10*/ #define WP_TDMV_RBS_READY 6 /*0x20*/ #define WP_TDMV_RBS_BUSY 7 /*0x40*/ #define WP_TDMV_RBS_UPDATE 8 /*0x80*/ #define IS_TDMV_RUNNING(wp) wan_test_bit(WP_TDMV_RUNNING, &(wp)->flags) #define IS_TDMV_UP(wp) wan_test_bit(WP_TDMV_UP, &(wp)->flags) #define IS_TDMV_SIG_POLL(wp) wan_test_bit(WP_TDMV_SIG_POLL, &(wp)->flags) #define IS_TDMV_SIG_ENABLE(wp) wan_test_bit(WP_TDMV_SIG_ENABLE, &(wp)->flags) #define IS_TDMV_UP_RUNNING(wp) (IS_TDMV_UP(wp) && IS_TDMV_RUNNING(wp)) #define IS_TDMV_SIG(wp) (IS_TDMV_SIG_POLL(wp) && IS_TDMV_SIG_ENABLE(wp)) #define IS_TDMV_RBS_READY(wp) wan_test_bit(WP_TDMV_RBS_READY, &(wp)->flags) #define WP_TDMV_ENABLE 0x01 #define WP_TDMV_DISABLE 0x02 #if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN_ZAPTEL) # define IS_CHAN_HARDHDLC(chan) (((chan)->flags & ZT_FLAG_NOSTDTXRX) || ((chan)->flags & ZT_FLAG_HDLC)) #else # define IS_CHAN_HARDHDLC(chan) ((chan)->flags & ZT_FLAG_HDLC) #endif #if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN) # if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN_ZAPTEL) # undef ZT_DCHAN_TX # endif #else #warning "TDM VOICE DCHAN DISABLED" #endif #if defined(__FreeBSD__) extern short *__zt_mulaw; #endif #if 0 static unsigned char wp_tdmv_ulaw[] = { 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 49, 51, 53, 55, 57, 59, 61, 63, 66, 70, 74, 78, 84, 92, 104, 254, 231, 219, 211, 205, 201, 197, 193, 190, 188, 186, 184, 182, 180, 178, 176, 175, 174, 173, 172, 171, 170, 169, 168, 167, 166, 165, 164, 163, 162, 161, 160, 159, 159, 158, 158, 157, 157, 156, 156, 155, 155, 154, 154, 153, 153, 152, 152, 151, 151, 150, 150, 149, 149, 148, 148, 147, 147, 146, 146, 145, 145, 144, 144, 143, 143, 143, 143, 142, 142, 142, 142, 141, 141, 141, 141, 140, 140, 140, 140, 139, 139, 139, 139, 138, 138, 138, 138, 137, 137, 137, 137, 136, 136, 136, 136, 135, 135, 135, 135, 134, 134, 134, 134, 133, 133, 133, 133, 132, 132, 132, 132, 131, 131, 131, 131, 130, 130, 130, 130, 129, 129, 129, 129, 128, 128, 128, 128, }; #endif /* ****************************************************************************** STRUCTURES AND TYPEDEFS ****************************************************************************** */ typedef struct wp_tdmv_pvt_area { sdla_t* card; char* devname; wan_spinlock_t lock; wan_spinlock_t tx_rx_lock; int ise1; int num; int spanno; int flags; int lbo; int lcode; int frame; int usecount; int sync; int blinktimer; int alarmtimer; int loopupcnt; int loopdowncnt; #ifdef FANCY_ALARM int alarmpos; #endif /* T1 signalling */ struct zt_span span; /* Span */ struct zt_chan chans[31]; /* Channels */ unsigned char ec_chunk1[31][ZT_CHUNKSIZE]; unsigned char ec_chunk2[31][ZT_CHUNKSIZE]; unsigned long config_tsmap; unsigned long timeslot_map; int max_timeslots; /* T1: 24, E1: 31 */ int timeslots; /* configured timeslots */ unsigned long sig_timeslot_map; int sig_timeslots; unsigned long rbs_tx_status; unsigned long rbs_tx1_status; unsigned char rbs_tx[31]; unsigned char rbs_tx1[31]; unsigned char rbs_rx[31]; unsigned long rbs_rx_pending; u32 intcount; u32 rbscount; unsigned int brt_ctrl; unsigned char hwec; unsigned char echo_off; unsigned long echo_off_map; unsigned int max_rxtx_len; unsigned int channelized; /* WAN_TRUE or WAN_FALSE */ unsigned char *tx_unchan; /* tx data pointer for unchannelized mode */ /* AHDLC: Hardware HDLC arguments */ unsigned int dchan_map; netdevice_t *dchan_dev; } wp_tdmv_softc_t; /* ******************************************************************************* ** GLOBAL VARIABLES ******************************************************************************* */ static int wp_card_no = 0; WAN_LIST_HEAD(, wan_tdmv_) wan_tdmv_head = WAN_LIST_HEAD_INITIALIZER(&wan_tdmv_head); /* ******************************************************************************* ** FUNCTION PROTOTYPES ******************************************************************************* */ static int wp_tdmv_check_mtu(void* pcard, unsigned long timeslot_map, int *mtu); static int wp_tdmv_create(void* pcard, wan_tdmv_conf_t*); static int wp_tdmv_remove(void* pcard); static int wp_tdmv_reg(void* pcard, wan_tdmv_if_conf_t*, unsigned int, unsigned char,netdevice_t*); static int wp_tdmv_unreg(void* pcard, unsigned long ts_map); static int wp_tdmv_software_init(wan_tdmv_t *wan_tdmv); static int wp_tdmv_startup(struct zt_span *span); static int wp_tdmv_shutdown(struct zt_span *span); static int wp_tdmv_maint(struct zt_span *span, int cmd); static int wp_tdmv_chanconfig(struct zt_chan *chan, int sigtype); static int wp_tdmv_spanconfig(struct zt_span *span, struct zt_lineconfig *lc); static int wp_tdmv_open(struct zt_chan *chan); static int wp_tdmv_close(struct zt_chan *chan); static void wp_tdmv_release(wp_tdmv_softc_t *wp); #if defined(__FreeBSD__) || defined(__OpenBSD__) static int wp_tdmv_ioctl(struct zt_chan*, unsigned int, caddr_t); #else static int wp_tdmv_ioctl(struct zt_chan*, unsigned int, unsigned long); #endif static int wp_tdmv_hwec(struct zt_chan *chan, int enable); #if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN) && defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN_ZAPTEL) static void wp_tdmv_tx_hdlc_hard(struct zt_chan *chan); #endif static int wp_tdmv_state(void* pcard, int state); static int wp_tdmv_running(void* pcard); /* RBS functions */ static int wp_tdmv_sigctrl(sdla_t* card, wp_tdmv_softc_t *wp, int channel, int status); static int wp_tdmv_rbsbits(struct zt_chan *chan, int bits); static int wp_tdmv_tx_rbsbits(wp_tdmv_softc_t *wp); static int wp_tdmv_is_rbsbits(wan_tdmv_t *wan_tdmv); static int wp_tdmv_rbsbits_poll(wan_tdmv_t *wan_tdmv, void *card1); static void wp_tdmv_report_rbsbits(void* pcard, int channel, unsigned char status); static void wp_tdmv_report_alarms(void* pcard, unsigned long te_alarm); /* Rx/Tx functions */ static int wp_tdmv_rx_tx(void* pcard, netskb_t* skb); static int wp_tdmv_rx_tx_span(void *pcard); static int wp_tdmv_span_buf_rotate(void *pcard, u32, unsigned long); static int wp_tdmv_ec_span(void *pcard); static int wp_tdmv_rx_chan(wan_tdmv_t*, int, unsigned char*, unsigned char*); #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 #if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN) static int wp_tdmv_rx_dchan(wan_tdmv_t*, int, unsigned char*, unsigned int); #endif static int wp_tdmv_rxcopy(wan_tdmv_t *wan_tdmv, unsigned char* rxbuf, int max_len); static int wp_tdmv_rxprep(wan_tdmv_t* wan_tdmv, unsigned char*, int); static int wp_tdmv_txcopy(wan_tdmv_t* wan_tdmv, unsigned char*, int); static int wp_tdmv_txprep(wan_tdmv_t* wan_tdmv, unsigned char*, int); static inline void start_alarm(wp_tdmv_softc_t* wp); static inline void stop_alarm(wp_tdmv_softc_t* wp); static int wp_tdmv_init(void* pcard, wanif_conf_t *conf); /****************************************************************************** ** wp_tdmv_te1_init() - ** ** OK */ int wp_tdmv_te1_init(wan_tdmv_iface_t *iface) { WAN_ASSERT(iface == NULL); iface->check_mtu = wp_tdmv_check_mtu; iface->create = wp_tdmv_create; iface->remove = wp_tdmv_remove; iface->reg = wp_tdmv_reg; iface->unreg = wp_tdmv_unreg; iface->software_init = wp_tdmv_software_init; iface->state = wp_tdmv_state; iface->running = wp_tdmv_running; iface->rx_tx = wp_tdmv_rx_tx; iface->rx_chan = wp_tdmv_rx_chan; #if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN) iface->rx_dchan = wp_tdmv_rx_dchan; #endif iface->rx_tx_span = wp_tdmv_rx_tx_span; iface->is_rbsbits = wp_tdmv_is_rbsbits; //???? iface->rbsbits_poll = wp_tdmv_rbsbits_poll; //????? iface->init = wp_tdmv_init; iface->buf_rotate = wp_tdmv_span_buf_rotate; iface->ec_span = wp_tdmv_ec_span; return 0; } static int wp_tdmv_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 = GET_TE_CHANNEL_RANGE(&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_create() - ** ** OK */ static int wp_tdmv_create(void* pcard, wan_tdmv_conf_t *tdmv_conf) { sdla_t *card = (sdla_t*)pcard; wp_tdmv_softc_t *wp = NULL; wan_tdmv_t *tmp = NULL; WAN_ASSERT(card == NULL); WAN_ASSERT(tdmv_conf->span_no == 0); 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; } } card->wan_tdmv.max_timeslots = GET_TE_CHANNEL_RANGE(&card->fe); card->wan_tdmv.spanno = tdmv_conf->span_no; card->wandev.te_report_rbsbits = wp_tdmv_report_rbsbits; card->wandev.te_report_alarms = wp_tdmv_report_alarms; wp = wan_kmalloc(sizeof(wp_tdmv_softc_t)); if (wp == NULL){ return -ENOMEM; } memset(wp, 0x0, sizeof(wp_tdmv_softc_t)); card->wan_tdmv.sc = wp; wp->spanno = tdmv_conf->span_no-1; wp->num = wp_card_no++; wp->card = card; wp->devname = card->devname; wp->lcode = WAN_FE_LCODE(&card->fe); wp->frame = WAN_FE_FRAME(&card->fe); wp->lbo = WAN_TE1_LBO(&card->fe); wp->ise1 = IS_E1_FEMEDIA(&card->fe) ? 1 : 0; wp->max_timeslots = IS_E1_FEMEDIA(&card->fe) ? 31: 24; wp->max_rxtx_len = 0; wan_spin_lock_init(&wp->lock); wan_spin_lock_init(&wp->tx_rx_lock); /* AHDLC */ if (tdmv_conf->dchan){ /* PRI signalling is selected with hw HDLC (dchan is not 0) */ wp->dchan_map = tdmv_conf->dchan; if (wp->ise1) { wan_clear_bit(0,&wp->dchan_map); } } 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_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; wan_tdmv_t *wan_tdmv = &card->wan_tdmv; wp_tdmv_softc_t *wp = NULL; int i, channo = 0, cnt = 0; WAN_ASSERT(wan_tdmv->sc == NULL); wp = wan_tdmv->sc; if (wan_test_bit(WP_TDMV_REGISTER, &wp->flags)){ DEBUG_EVENT("%s: Error: Master device has already been configured!\n", card->devname); return -EINVAL; } /* * T1: 1-24 * E1: 0-31 */ for(i = 0; imax_timeslots; i++){ if (wan_test_bit(i, &active_ch)){ wan_set_bit(i, &wp->timeslot_map); if (tdmv_conf->tdmv_echo_off){ wan_set_bit(i, &wp->echo_off_map); } channo = i; cnt++; } } if (!cnt){ DEBUG_EVENT("%s: Error: TDMV iface %s configured with 0 timeslots!\n", card->devname, wan_netif_name(dev)); return -EINVAL; } if (cnt > 1){ /* Unchannelized implementation */ DEBUG_EVENT("%s: TDMV Mode :Unchannelized!\n", wp->devname); channo = 0; #if 0 if (is_last != WAN_TRUE){ DEBUG_EVENT("%s: Error: Unchannelized interface must be the Master If (slots=%i)!\n", wp->devname,cnt); return -EINVAL; } #endif if (tdmv_conf->tdmv_echo_off){ DEBUG_EVENT("%s: TDMV Echo Ctrl:Off\n", wp->devname); } wp->channelized = WAN_FALSE; if (wp->dchan_map){ if (dev == NULL){ DEBUG_EVENT("%s: ERROR: Device pointer is NULL for D-chan!\n", wp->devname); return -EINVAL; } wp->dchan_dev = dev; } }else{ /* Channelized implementation */ if (wan_test_bit(channo, &wp->dchan_map)){ if (dev == NULL){ DEBUG_EVENT("%s: ERROR: Device pointer is NULL for D-chan!\n", wp->devname); return -EINVAL; } wp->dchan_dev = dev; } if (tdmv_conf->tdmv_echo_off){ DEBUG_EVENT("%s: TDMV Echo Ctrl:Off\n", wp->devname); } memset(wp->chans[channo].sreadchunk, WAN_TDMV_IDLE_FLAG, ZT_CHUNKSIZE); memset(wp->chans[channo].swritechunk, WAN_TDMV_IDLE_FLAG, ZT_CHUNKSIZE); wp->chans[channo].readchunk = wp->chans[channo].sreadchunk; wp->chans[channo].writechunk = wp->chans[channo].swritechunk; wp->channelized = WAN_TRUE; } wp->hwec = ec_enable; wp_tdmv_check_mtu(card, active_ch, &wp->max_rxtx_len); return channo; } /****************************************************************************** ** wp_tdmv_del() - ** ** OK */ static int wp_tdmv_unreg(void* pcard, unsigned long ts_map) { sdla_t *card = (sdla_t*)pcard; wan_tdmv_t *wan_tdmv = &card->wan_tdmv; wp_tdmv_softc_t *wp = NULL; int channo = 0; WAN_ASSERT(wan_tdmv->sc == NULL); wp = wan_tdmv->sc; for(channo = 0; channo < wp->max_timeslots; channo++){ if (wan_test_bit(channo, &wp->timeslot_map)){ wan_clear_bit(channo, &wp->timeslot_map); wan_clear_bit(channo, &wp->echo_off_map); memset(wp->chans[channo].sreadchunk, WAN_TDMV_IDLE_FLAG, ZT_CHUNKSIZE); memset(wp->chans[channo].swritechunk, WAN_TDMV_IDLE_FLAG, ZT_CHUNKSIZE); wp->chans[channo].readchunk = wp->chans[channo].sreadchunk; wp->chans[channo].writechunk = wp->chans[channo].swritechunk; } } return 0; } /****************************************************************************** ** wp_tdmv_running() - ** ** OK */ static int wp_tdmv_running(void* pcard) { sdla_t *card = (sdla_t*)pcard; wan_tdmv_t *wan_tdmv = &card->wan_tdmv; wp_tdmv_softc_t *wp = NULL; wp = wan_tdmv->sc; if (wp && wp->usecount){ DEBUG_EVENT("%s: WARNING: Wanpipe is still used by Asterisk!\n", card->devname); return -EINVAL; } return 0; } /****************************************************************************** ** wp_tdmv_remove() - ** ** OK */ static int wp_tdmv_remove(void* pcard) { sdla_t *card = (sdla_t*)pcard; wan_tdmv_t *wan_tdmv = &card->wan_tdmv; wp_tdmv_softc_t *wp = NULL; wan_smp_flag_t flags; if (!card->wan_tdmv.sc){ return 0; } wp = wan_tdmv->sc; DEBUG_TDMV("Removing TDM VOICE interfaces\n"); /* Release span, possibly delayed */ if (wp && wp->timeslot_map){ DEBUG_EVENT("%s: Some interfaces are not unregistered (%08lX)!\n", card->devname, wp->timeslot_map); return -EINVAL; } if (wp && wp->usecount){ DEBUG_EVENT("%s: ERROR: Wanpipe is still used by Asterisk!\n", card->devname); return -EINVAL; } wan_spin_lock_irq(&wp->lock, &flags); card->wandev.te_report_rbsbits = NULL; card->wandev.te_report_alarms = NULL; wan_spin_unlock_irq(&wp->lock, &flags); if (wp){ wan_clear_bit(WP_TDMV_RUNNING, &wp->flags); wan_clear_bit(WP_TDMV_UP, &wp->flags); wan_tdmv->sc = NULL; wp_tdmv_sigctrl(card, wp, 0, WP_TDMV_DISABLE); WAN_LIST_REMOVE(wan_tdmv, next); wp_tdmv_release(wp); }else{ wan_tdmv->sc = NULL; } return 0; } static int wp_tdmv_state(void* pcard, int state) { sdla_t* card = (sdla_t*)pcard; wan_tdmv_t* wan_tdmv = &card->wan_tdmv; wp_tdmv_softc_t* wp = NULL; WAN_ASSERT(wan_tdmv->sc == NULL); wp = (wp_tdmv_softc_t*)wan_tdmv->sc; switch(state){ case WAN_CONNECTED: wan_set_bit(WP_TDMV_UP, &wp->flags); break; case WAN_DISCONNECTED: wan_clear_bit(WP_TDMV_UP, &wp->flags); wp->rbs_rx_pending = wp->sig_timeslot_map; break; } return 0; } static int wp_tdmv_sigctrl(sdla_t* card, wp_tdmv_softc_t *wp, int channel, int status) { WAN_ASSERT(wp == NULL); WAN_ASSERT(card == NULL); if (status == WP_TDMV_ENABLE && IS_TDMV_RBS_READY(wp)){ return 0; }else if (status == WP_TDMV_DISABLE && !IS_TDMV_RBS_READY(wp)){ return 0; } DEBUG_TDMV("%s: %s signalling mode for all channels!\n", wp->devname, (status==WP_TDMV_ENABLE)?"Enable":"Disable"); if (card->wandev.fe_iface.set_fe_sigctrl){ if (wan_test_bit(WP_TDMV_SIG_POLL, &wp->flags)){ card->wandev.fe_iface.set_fe_sigctrl( &card->fe, WAN_TE_SIG_POLL, ENABLE_ALL_CHANNELS, (status == WP_TDMV_ENABLE)?WAN_ENABLE:WAN_DISABLE); }else{ card->wandev.fe_iface.set_fe_sigctrl( &card->fe, WAN_TE_SIG_INTR, ENABLE_ALL_CHANNELS, (status == WP_TDMV_ENABLE)?WAN_ENABLE:WAN_DISABLE); } } switch(status){ case WP_TDMV_ENABLE: wan_set_bit(WP_TDMV_SIG_ENABLE, &wp->flags); wan_set_bit(WP_TDMV_RBS_READY, &wp->flags); break; case WP_TDMV_DISABLE: wan_clear_bit(WP_TDMV_RBS_READY, &wp->flags); wan_clear_bit(WP_TDMV_SIG_ENABLE, &wp->flags); break; default: DEBUG_EVENT("%s: Unknown signalling mode (%d)\n", wp->devname, status); return -EINVAL; } return 0; } /****************************************************************************** ** wp_tdmv_report_rbsbits() - Report A,B bit status changes to TDM Voice ** requests. ** ** DONE */ static void wp_tdmv_report_rbsbits(void* pcard, int channel, unsigned char status) { sdla_t* card = (sdla_t*)pcard; wan_tdmv_t* wan_tdmv = &card->wan_tdmv; wp_tdmv_softc_t* wp = NULL; int rxs = 0, i = 0; WAN_ASSERT1(wan_tdmv->sc == NULL); wp = (wp_tdmv_softc_t*)wan_tdmv->sc; if (!wan_test_bit(channel-1, &wp->timeslot_map)){ return; } if (status & WAN_RBS_SIG_A) rxs |= ZT_ABIT; if (status & WAN_RBS_SIG_B) rxs |= ZT_BBIT; if (status & WAN_RBS_SIG_C) rxs |= ZT_CBIT; if (status & WAN_RBS_SIG_D) rxs |= ZT_DBIT; #if 0 if (wp->ise1){ FIXME_MSG("wp_tdmv_report_rbsbits"); /* Read 5 registers at a time, loading 10 channels at a time */ for (i = (x *5); i < (x * 5) + 5; i++) { /* FIXME a = __t1_get_reg(wp, 0x31 + i); */ /* Get high channel in low bits */ rxs = (a & 0xf); if (!(wp->chans[i+15].sig & ZT_SIG_CLEAR)) { if (wp->chans[i+15].rxsig != rxs) zt_rbsbits(&wp->chans[i+15], rxs); } rxs = (a >> 4) & 0xf; if (!(wp->chans[i].sig & ZT_SIG_CLEAR)) { if (wp->chans[i].rxsig != rxs) zt_rbsbits(&wp->chans[i], rxs); } } } #endif for(i=0; i < wp->span.channels;i++){ if (wp->chans[i].chanpos == channel) break; } if (i == wp->span.channels){ return; } if (!(wp->chans[i].sig & ZT_SIG_CLEAR) && (wp->chans[i].rxsig != rxs)){ zt_rbsbits(&wp->chans[i], rxs); DEBUG_TDMV( "[TDMV] %s: %s:%02d(%d) RX RBS: A:%1d B:%1d C:%1d D:%1d\n", wp->devname, (wp->ise1) ? "E1" : "T1", channel, wp->chans[i].channo, (rxs & ZT_ABIT) ? 1 : 0, (rxs & ZT_BBIT) ? 1 : 0, (rxs & ZT_CBIT) ? 1 : 0, (rxs & ZT_DBIT) ? 1 : 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_softc_t *wp = NULL; int alarms = 0, prev_alarms; int x,j; /* The sc pointer can be NULL, on shutdown. In this * case don't generate error, just get out */ wp = wan_tdmv->sc; if (!wp){ return; } /* And consider only carrier alarms */ wp->span.alarms &= (ZT_ALARM_RED | ZT_ALARM_BLUE | ZT_ALARM_NOTOPEN); prev_alarms = wp->span.alarms; if (wp->ise1){ /* XXX Implement me XXX */ }else{ /* Detect loopup code if we're not sending one */ if (!wp->span.mainttimer && (te_alarm & WAN_TE_BIT_LOOPUP_CODE)){ /* Loop-up code detected */ if ((wp->loopupcnt++ > 80) && (wp->span.maintstat != ZT_MAINT_REMOTELOOP)){ card->wandev.fe_iface.set_fe_lbmode( &wp->card->fe, WAN_TE1_DDLB_MODE, WAN_TE1_DEACTIVATE_LB); card->wandev.fe_iface.set_fe_lbmode( &wp->card->fe, WAN_TE1_LINELB_MODE, WAN_TE1_ACTIVATE_LB); wp->span.maintstat = ZT_MAINT_REMOTELOOP; } }else{ wp->loopupcnt = 0; } /* Same for loopdown code */ if (!wp->span.mainttimer && (te_alarm & WAN_TE_BIT_LOOPDOWN_CODE)){ /* Loop-down code detected */ if ((wp->loopdowncnt++ > 80) && (wp->span.maintstat == ZT_MAINT_REMOTELOOP)){ card->wandev.fe_iface.set_fe_lbmode( &wp->card->fe, WAN_TE1_DDLB_MODE, WAN_TE1_DEACTIVATE_LB); card->wandev.fe_iface.set_fe_lbmode( &wp->card->fe, WAN_TE1_LINELB_MODE, WAN_TE1_DEACTIVATE_LB); wp->span.maintstat = ZT_MAINT_NONE; } }else{ wp->loopdowncnt = 0; } } if (wp->span.lineconfig & ZT_CONFIG_NOTOPEN) { for (x=0,j=0;x < wp->span.channels;x++){ if ((wp->chans[x].flags & ZT_FLAG_OPEN) || (wp->chans[x].flags & ZT_FLAG_NETDEV)){ j++; } } if (!j){ alarms |= ZT_ALARM_NOTOPEN; } } /* Note: The alarm checking should match Alarm checking in sdla_te1.c and sdla_8te1.c */ if (wp->ise1) { if (te_alarm & WAN_TE_BIT_RED_ALARM) alarms |= ZT_ALARM_RED; if (te_alarm & WAN_TE_BIT_OOF_ALARM) alarms |= ZT_ALARM_RED; } else { /* Check actual alarm status */ if (te_alarm & WAN_TE_BIT_RED_ALARM) alarms |= ZT_ALARM_RED; if (te_alarm & WAN_TE_BIT_OOF_ALARM) alarms |= ZT_ALARM_RED; } /* Keep track of recovering */ if ((!alarms) && wp->span.alarms) wp->alarmtimer = ZT_ALARMSETTLE_TIME; #if 0 /* If receiving alarms, go into Yellow alarm state */ if (alarms && (!wp->span.alarms)) { DEBUG_TDMV("%s: Going into yellow alarm\n", wp->devname); if (card->wandev.fe_iface.set_fe_alarm){ card->wandev.fe_iface.set_fe_alarm(&card->fe, WAN_TE_BIT_YEL_ALARM); } } #endif if (wp->span.alarms != alarms) { /* FIXME: Alarm status changed!!! */ DEBUG_TDMV("%s: Alarm status changed %X!\n", wp->devname,alarms); } /* ** if (wp->alarmtimer) ** alarms |= ZT_ALARM_RECOVER; */ if (te_alarm & WAN_TE_BIT_YEL_ALARM) alarms |= ZT_ALARM_YELLOW; wp->span.alarms = alarms; zt_alarm_notify(&wp->span); return; } /****************************************************************************** ** wp_tdmv_txcopy() - ** ** OK */ static int wp_tdmv_txcopy(wan_tdmv_t *wan_tdmv, unsigned char* txbuf, int max_len) { wp_tdmv_softc_t *wp = wan_tdmv->sc; int x = 0, y = 0, offset = 0; WAN_ASSERT(wp == NULL); for (y=0;yspan.channels;x++){ if (!wan_test_bit(x,&wp->timeslot_map)){ continue; } txbuf[offset++] = wp->chans[x].writechunk[y]; } } return (offset+1); } /****************************************************************************** ** wp_tdmv_txprep() - ** ** OK */ static int wp_tdmv_txprep(wan_tdmv_t *wan_tdmv, unsigned char* txbuf, int max_len) { wp_tdmv_softc_t *wp = wan_tdmv->sc; WAN_ASSERT2(wp == NULL, 0); zt_transmit(&wp->span); return wp_tdmv_txcopy(wan_tdmv, txbuf, max_len); } /****************************************************************************** ** wp_tdmv_rxcopy() - ** ** OK */ static int wp_tdmv_rxcopy(wan_tdmv_t *wan_tdmv, unsigned char* rxbuf, int max_len) { wp_tdmv_softc_t *wp = wan_tdmv->sc; int x, y, offset = 0; int channels = wp->span.channels; unsigned char value; WAN_ASSERT(wp == NULL); for (y=0;ytimeslot_map)){ value = rxbuf[offset++]; }else{ value = WAN_TDMV_IDLE_FLAG; /* 0xFE; */ } wp->chans[x].readchunk[y] = value; } } return offset; } /****************************************************************************** ** wp_tdmv_rxprep() - ** ** OK */ static int wp_tdmv_rxprep(wan_tdmv_t *wan_tdmv, unsigned char* rxbuf, int max_len) { wp_tdmv_softc_t *wp = wan_tdmv->sc; int x, offset = 0, channels = wp->span.channels; WAN_ASSERT2(wp == NULL, 0); offset = wp_tdmv_rxcopy(wan_tdmv, rxbuf, max_len); if (!wp->echo_off_map){ for (x = 0; x < channels; x++){ #ifdef CONFIG_PRODUCT_WANPIPE_TDM_VOICE_ECHOMASTER wan_tdmv_rxtx_pwr_t *pwr_rxtx = &wan_tdmv->chan_pwr[x]; wp_tdmv_echo_check(wan_tdmv, &wp->chans[0], x); if(pwr_rxtx->current_state != ECHO_ABSENT){ #endif zt_ec_chunk( &wp->chans[x], wp->chans[x].readchunk, wp->chans[x].writechunk); #if 0 zt_ec_chunk( &wp->chans[x], wp->chans[x].readchunk, wp->ec_chunk1[x]); memcpy( wp->ec_chunk1[x], wp->chans[x].writechunk, ZT_CHUNKSIZE); #endif #ifdef CONFIG_PRODUCT_WANPIPE_TDM_VOICE_ECHOMASTER }/* if() */ #endif }/* for() */ }/* if() */ zt_receive(&wp->span); return (offset); } /****************************************************************************** ** wp_tdmv_rx_tx() - ** ** OK */ static int wp_tdmv_rx_tx(void* pcard, netskb_t* skb) { sdla_t *card = (sdla_t*)pcard; wan_tdmv_t *wan_tdmv = &card->wan_tdmv; wp_tdmv_softc_t *wp = NULL; int len; WAN_ASSERT(wan_tdmv->sc == NULL); WAN_ASSERT(skb == NULL); wp = wan_tdmv->sc; if (wan_skb_len(skb) != wp->max_rxtx_len){ if (WAN_NET_RATELIMIT()) { DEBUG_EVENT("%s: Internal Error[%s:%d]: Wrong buffer lenght %d (%d)!\n", wp->devname, __FUNCTION__,__LINE__, wan_skb_len(skb), wp->max_rxtx_len); } return 0; } wp->intcount ++; len = wp_tdmv_rxprep( wan_tdmv, wan_skb_data(skb), wan_skb_len(skb)); len = wp_tdmv_txprep( wan_tdmv, wan_skb_data(skb), wan_skb_len(skb)); if (wan_test_bit(WP_TDMV_RBS_UPDATE, &wp->flags)){ if (card->wandev.fe_iface.report_rbsbits){ card->wandev.fe_iface.report_rbsbits(&card->fe); } wan_clear_bit(WP_TDMV_RBS_UPDATE, &wp->flags); wan_clear_bit(WP_TDMV_RBS_BUSY, &wp->flags); } return wp->max_rxtx_len; } /* ** STATIC FUNCTION DEFINITIONS ** */ /****************************************************************************** ** wp_tdmv_software_init() - ** ** OK */ static int wp_tdmv_software_init(wan_tdmv_t *wan_tdmv) { sdla_t *card = NULL; wp_tdmv_softc_t *wp = wan_tdmv->sc; int x = 0; WAN_ASSERT(wp == NULL); WAN_ASSERT(wp->card == NULL); card = wp->card; if (wan_test_bit(WP_TDMV_REGISTER, &wp->flags)){ DEBUG_EVENT("%s: Wanpipe device is already registered to Zaptel span # %d!\n", wp->devname, wp->span.spanno); return 0; } #if 0 if (wp->ise1 && wp->channelized == WAN_FALSE){ /* Timeslot map for E1 includes ts0. TDM voice does never ** use ts0, so i will shift map 1 bit right to get ** everything alignment as T1 */ wp->timeslot_map = wp->timeslot_map >> 1; wp->echo_off_map = wp->echo_off_map >> 1; } #endif if (wp->ise1){ sprintf(wp->span.name, "WPE1/%d", wp->num); }else{ sprintf(wp->span.name, "WPT1/%d", wp->num); } sprintf(wp->span.desc, "%s card %d", wp->devname, wp->num); wp->span.spanconfig = wp_tdmv_spanconfig; wp->span.chanconfig = wp_tdmv_chanconfig; wp->span.startup = wp_tdmv_startup; wp->span.shutdown = wp_tdmv_shutdown; wp->span.rbsbits = wp_tdmv_rbsbits; wp->span.maint = wp_tdmv_maint; wp->span.open = wp_tdmv_open; wp->span.close = wp_tdmv_close; wp->span.channels = wp->max_timeslots; wp->span.chans = wp->chans; wp->span.flags = ZT_FLAG_RBS; wp->span.linecompat = ZT_CONFIG_AMI | ZT_CONFIG_B8ZS | ZT_CONFIG_D4 | ZT_CONFIG_ESF; wp->span.ioctl = wp_tdmv_ioctl; /* Set this pointer only if card has hw echo canceller module */ if (wp->hwec == WANOPT_YES && card->wandev.ec_dev){ /* Initialize it only if HWEC option is enabled */ wp->span.echocan = wp_tdmv_hwec; } #if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN) && defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN_ZAPTEL) if (wp->dchan_map){ DEBUG_EVENT("%s: Enable Zaptel HW DCHAN interface\n", wp->devname); wp->span.hdlc_hard_xmit = wp_tdmv_tx_hdlc_hard; } #endif wp->span.pvt = wp; if (wp->ise1){ wp->span.deflaw = ZT_LAW_ALAW; card->fe.fe_cfg.tdmv_law = WAN_TDMV_ALAW; wp->span.linecompat = ZT_CONFIG_HDB3 | ZT_CONFIG_CCS | ZT_CONFIG_CRC4; }else{ wp->span.deflaw = ZT_LAW_MULAW; card->fe.fe_cfg.tdmv_law = WAN_TDMV_MULAW; wp->span.linecompat = ZT_CONFIG_AMI | ZT_CONFIG_B8ZS | ZT_CONFIG_D4 | ZT_CONFIG_ESF; } #if !defined(__FreeBSD__) && !defined(__OpenBSD__) init_waitqueue_head(&wp->span.maintq); #endif for (x=0;xspan.channels;x++) { sprintf(wp->chans[x].name, "%s/%d", wp->span.name, x+1); if (wan_test_bit(x,&wp->timeslot_map)){ DEBUG_TEST("%s: Configure channel %d for voice (%s)!\n", wp->devname, x + 1, wp->chans[x].name); wp->chans[x].sigcap = ZT_SIG_EM | ZT_SIG_CLEAR | ZT_SIG_EM_E1 | ZT_SIG_FXSLS | ZT_SIG_FXSGS | ZT_SIG_FXSKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_FXOKS | ZT_SIG_CAS | ZT_SIG_DACS_RBS #if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN) && defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN_ZAPTEL) | ZT_SIG_HARDHDLC #endif ; }else{ wp->chans[x].sigcap = ZT_SIG_NONE; } wp->chans[x].pvt = wp; wp->chans[x].chanpos = x + 1; wp->chans[x].rxsig = 0x00; } if (zt_register(&wp->span, 0)) { DEBUG_EVENT("%s: Unable to register span with zaptel\n", wp->devname); return -EINVAL; } if (wp->span.spanno != wp->spanno +1){ DEBUG_EVENT("\n"); DEBUG_EVENT("WARNING: Span number %d is already used by another device!\n", wp->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", wp->devname,wp->span.spanno); DEBUG_EVENT("\n"); wp->spanno = wp->span.spanno-1; }else{ DEBUG_EVENT("%s: Wanpipe device is registered to Zaptel span # %d!\n", wp->devname, wp->span.spanno); } if (wp->channelized == WAN_FALSE && wp->dchan_map){ /* Apr 15, 2005 * For unchannelized mode, if HW HDLC protocol is selected * by using dchan configuration option, remove dchan timeslot * from timeslot and echo map. */ if (wp->ise1) { wp->dchan_map=wp->dchan_map>>1; wan_clear_bit(0,&wp->dchan_map); } wp->timeslot_map &= ~wp->dchan_map; wp->echo_off_map &= ~wp->dchan_map; } wp_tdmv_check_mtu(card, wp->timeslot_map, &wp->max_rxtx_len); wan_set_bit(WP_TDMV_REGISTER, &wp->flags); if (!wan_tdmv->sig_intr_enable){ wan_set_bit(WP_TDMV_SIG_POLL, &wp->flags); /* These function is available if poll used for signalling detection */ card->tdmv_iface.is_rbsbits = wp_tdmv_is_rbsbits; card->tdmv_iface.rbsbits_poll = wp_tdmv_rbsbits_poll; } return 0; } /****************************************************************************** ** wp_tdmv_startup() - ** ** OK */ static int wp_tdmv_startup(struct zt_span *span) { wp_tdmv_softc_t* wp = NULL; int i; WAN_ASSERT2(span == NULL, -ENODEV); WAN_ASSERT2(span->pvt == NULL, -ENODEV); wp = span->pvt; /* initialize the start value for the entire chunk of last ec buffer */ for(i = 0; i < span->channels; i++){ memset(wp->ec_chunk1[i], ZT_LIN2X(0,&span->chans[i]),ZT_CHUNKSIZE); memset(wp->ec_chunk2[i], ZT_LIN2X(0,&span->chans[i]),ZT_CHUNKSIZE); } if (!(span->flags & ZT_FLAG_RUNNING)) { /* Only if we're not already going */ span->flags |= ZT_FLAG_RUNNING; } wan_set_bit(WP_TDMV_RUNNING, &wp->flags); return 0; } /****************************************************************************** ** wp_tdmv_shutdown() - ** ** OK */ static int wp_tdmv_shutdown(struct zt_span *span) { wp_tdmv_softc_t* wp = NULL; wan_smp_flag_t flags; WAN_ASSERT2(span == NULL, -ENODEV); WAN_ASSERT2(span->pvt == NULL, -ENODEV); wp = span->pvt; wan_clear_bit(WP_TDMV_RUNNING, &wp->flags); wan_spin_lock_irq(&wp->lock, &flags); span->flags &= ~ZT_FLAG_RUNNING; wan_spin_unlock_irq(&wp->lock, &flags); return 0; } /****************************************************************************** ** wp_tdmv_maint() - ** ** OK */ static int wp_tdmv_maint(struct zt_span *span, int cmd) { wp_tdmv_softc_t *wp = span->pvt; sdla_t *card = wp->card; int res = 0; wan_smp_flag_t flags; WAN_ASSERT2(span == NULL, -ENODEV); WAN_ASSERT2(span->pvt == NULL, -ENODEV); wp = span->pvt; wan_spin_lock_irq(&wp->lock, &flags); if (wp->ise1) { #if 0 /* FIXME: Support E1 */ switch(cmd) { case ZT_MAINT_NONE: DEBUG_EVENT("%s: E1: Set to normal mode (no local/remote loops)\n", wp->card->devname); /* FIXME __t1_set_reg(wp,0xa8,0); */ /* no loops */ break; case ZT_MAINT_LOCALLOOP: DEBUG_EVENT("%s: E1: Set to local loopback mode\n", wp->card->devname); /* FIXME __t1_set_reg(wp,0xa8,0x40); */ /* local loop */ break; case ZT_MAINT_REMOTELOOP: DEBUG_EVENT("%s: E1: Set to remote loopback mode\n", wp->card->devname); /* FIXME __t1_set_reg(wp,0xa8,0x80);*/ /* remote loop */ break; case ZT_MAINT_LOOPUP: case ZT_MAINT_LOOPDOWN: case ZT_MAINT_LOOPSTOP: res = -ENOSYS; break; default: DEBUG_EVENT("%s: E1: Unknown maintenance mode (%d)\n", wp->card->devname, cmd); res = -EINVAL; break; } #endif }else{ switch(cmd) { case ZT_MAINT_NONE: DEBUG_EVENT("%s: T1: Set to normal mode (no local/remote loop)\n", wp->card->devname); card->wandev.fe_iface.set_fe_lbmode( &wp->card->fe, WAN_TE1_DDLB_MODE, WAN_TE1_DEACTIVATE_LB); card->wandev.fe_iface.set_fe_lbmode( &wp->card->fe, WAN_TE1_LINELB_MODE, WAN_TE1_DEACTIVATE_LB); break; case ZT_MAINT_LOCALLOOP: DEBUG_EVENT("%s: T1: Set to local loopback mode (local/no remote loop)\n", wp->card->devname); card->wandev.fe_iface.set_fe_lbmode( &wp->card->fe, WAN_TE1_LINELB_MODE, WAN_TE1_DEACTIVATE_LB); card->wandev.fe_iface.set_fe_lbmode( &wp->card->fe, WAN_TE1_DDLB_MODE, WAN_TE1_ACTIVATE_LB); break; case ZT_MAINT_REMOTELOOP: DEBUG_EVENT("%s: T1: Set to remote loopback mode (no local/remote loop)\n", wp->card->devname); card->wandev.fe_iface.set_fe_lbmode( &wp->card->fe, WAN_TE1_LINELB_MODE, WAN_TE1_ACTIVATE_LB); card->wandev.fe_iface.set_fe_lbmode( &wp->card->fe, WAN_TE1_LINELB_MODE, WAN_TE1_DEACTIVATE_LB); break; case ZT_MAINT_LOOPUP: DEBUG_EVENT("%s: T1: Send loopup code\n", wp->card->devname); card->wandev.fe_iface.set_fe_lbmode( &wp->card->fe, WAN_TE1_TX_LB_MODE, WAN_TE1_ACTIVATE_LB); break; case ZT_MAINT_LOOPDOWN: DEBUG_EVENT("%s: T1: Send loopdown code\n", wp->card->devname); card->wandev.fe_iface.set_fe_lbmode( &wp->card->fe, WAN_TE1_TX_LB_MODE, WAN_TE1_DEACTIVATE_LB); break; case ZT_MAINT_LOOPSTOP: DEBUG_EVENT("%s: T1: Stop sending loop code\n", wp->card->devname); /* FIXME __t1_set_reg(wp,0x30,0); */ /* stop sending loopup code */ break; default: DEBUG_EVENT("%s: T1: Unknown maintenance mode (%d)\n", wp->card->devname, cmd); res = -EINVAL; } } wan_spin_unlock_irq(&wp->lock, &flags); return res; } static void wp_tdmv_set_clear(wp_tdmv_softc_t* wp) { WAN_ASSERT1(wp == NULL); /* FIXME: Add action for this function */ } /****************************************************************************** ** sigstr() - ** ** OK */ #if 0 static char *wp_tdmv_sigstr(int sig) { switch (sig) { case ZT_SIG_FXSLS: return "FXSLS"; case ZT_SIG_FXSKS: return "FXSKS"; case ZT_SIG_FXSGS: return "FXSGS"; case ZT_SIG_FXOLS: return "FXOLS"; case ZT_SIG_FXOKS: return "FXOKS"; case ZT_SIG_FXOGS: return "FXOGS"; case ZT_SIG_EM: return "E&M"; case ZT_SIG_CLEAR: return "Clear"; case ZT_SIG_HDLCRAW: return "HDLCRAW"; case ZT_SIG_HDLCFCS: return "HDLCFCS"; case ZT_SIG_HDLCNET: return "HDLCNET"; case ZT_SIG_SLAVE: return "Slave"; case ZT_SIG_CAS: return "CAS"; case ZT_SIG_DACS: return "DACS"; case ZT_SIG_SF: return "SF (ToneOnly)"; case ZT_SIG_NONE: default: return "Unconfigured"; } } #endif static int wp_tdmv_chanconfig(struct zt_chan *chan, int sigtype) { sdla_t *card; wp_tdmv_softc_t *wp = NULL; WAN_ASSERT2(chan == NULL, -ENODEV); WAN_ASSERT2(chan->pvt == NULL, -ENODEV); wp = chan->pvt; card = (sdla_t*)wp->card; DEBUG_TDMV("%s: Configuring chan %d..\n", wp->devname, chan->chanpos); if (chan->span->flags & ZT_FLAG_RUNNING){ wp_tdmv_set_clear(wp); } if (!wan_test_and_set_bit(chan->chanpos, &wp->config_tsmap)){ wp->timeslots++; } if (!(sigtype & ZT_SIG_CLEAR)){ /* Set Signalling channel map */ if (!wan_test_and_set_bit(chan->chanpos-1, &wp->sig_timeslot_map)){ wp->sig_timeslots ++; wan_set_bit(chan->chanpos-1, &wp->rbs_rx_pending); } wp_tdmv_sigctrl(card, wp, chan->chanpos, WP_TDMV_ENABLE); } return 0; } /****************************************************************************** ** wp_tdmv_spanconfig() - ** ** OK */ static int wp_tdmv_spanconfig(struct zt_span *span, struct zt_lineconfig *lc) { wp_tdmv_softc_t *wp = NULL; sdla_t *card = NULL; int err = 0; WAN_ASSERT2(span == NULL, -ENODEV); WAN_ASSERT2(span->pvt == NULL, -ENODEV); wp = span->pvt; card = (sdla_t*)wp->card; DEBUG_TDMV("%s: Configuring span device..\n", wp->devname); switch(wp->lcode){ case WAN_LCODE_AMI: span->lineconfig |= ZT_CONFIG_AMI; break; case WAN_LCODE_B8ZS: span->lineconfig |= ZT_CONFIG_B8ZS; break; case WAN_LCODE_HDB3: span->lineconfig |= ZT_CONFIG_HDB3; break; } switch(wp->frame){ case WAN_FR_ESF: span->lineconfig |= ZT_CONFIG_ESF; break; case WAN_FR_D4: span->lineconfig |= ZT_CONFIG_D4; break; case WAN_FR_CRC4: span->lineconfig |= ZT_CONFIG_CRC4; break; } if (wp->ise1){ if (lc->lineconfig & ZT_CONFIG_CCS){ span->lineconfig |= ZT_CONFIG_CCS; card->fe.fe_cfg.cfg.te_cfg.sig_mode = WAN_TE1_SIG_CCS; }else{ card->fe.fe_cfg.cfg.te_cfg.sig_mode = WAN_TE1_SIG_CAS; } if (card->wandev.fe_iface.reconfig){ card->wandev.fe_iface.reconfig(&card->fe); } } span->txlevel = 0; switch(wp->lbo){ case WAN_T1_LBO_0_DB: DEBUG_TE1("%s: LBO 0 dB\n", card->devname); span->txlevel = 0; break; case WAN_T1_LBO_75_DB: DEBUG_TE1("%s: LBO 7.5 dB\n", card->devname); span->txlevel = 5; break; case WAN_T1_LBO_15_DB: DEBUG_TE1("%s: LBO 15 dB\n", card->devname); span->txlevel = 6; break; case WAN_T1_LBO_225_DB: DEBUG_TE1("%s: LBO 22.5 dB\n", card->devname); span->txlevel = 7; break; case WAN_T1_0_110: DEBUG_TE1("%s: LBO 0-110 ft.\n", card->devname); span->txlevel = 0; break; case WAN_T1_110_220: DEBUG_TE1("%s: LBO 110-220 ft.\n", card->devname); span->txlevel = 1; break; case WAN_T1_220_330: DEBUG_TE1("%s: LBO 220-330 ft.\n", card->devname); span->txlevel = 2; break; case WAN_T1_330_440: DEBUG_TE1("%s: LBO 330-440 ft.\n", card->devname); span->txlevel = 3; break; case WAN_T1_440_550: DEBUG_TE1("%s: LBO 440-550 ft.\n", card->devname); span->txlevel = 3; break; case WAN_T1_550_660: DEBUG_TE1("%s: LBO 550-660 ft.\n", card->devname); span->txlevel = 4; break; } span->rxlevel = 0; /* Do we want to SYNC on receive or not */ wp->sync = lc->sync; /* */ /* If already running, apply changes immediately */ if (span->flags & ZT_FLAG_RUNNING){ err = wp_tdmv_startup(span); } return err; } /****************************************************************************** ** wp_tdmv_rbsbits() - Set A,B bits according TDM Voice requests. ** ** DONE */ static int wp_tdmv_rbsbits(struct zt_chan *chan, int bits) { wp_tdmv_softc_t *wp = NULL; sdla_t *card = NULL; unsigned char ABCD_bits = 0x00; /* Byte offset */ WAN_ASSERT2(chan == NULL, 0); if ((wp = chan->pvt) == NULL) return 0; WAN_ASSERT2(wp->card == NULL, 0); card = (sdla_t*)wp->card; if (!wan_test_bit(chan->chanpos-1, &wp->timeslot_map)){ return 0; } if (!wan_test_bit(WP_TDMV_SIG_ENABLE, &wp->flags)){ return 0; } if (bits & ZT_ABIT) ABCD_bits |= WAN_RBS_SIG_A; if (bits & ZT_BBIT) ABCD_bits |= WAN_RBS_SIG_B; if (bits & ZT_CBIT) ABCD_bits |= WAN_RBS_SIG_C; if (bits & ZT_DBIT) ABCD_bits |= WAN_RBS_SIG_D; if (IS_CHAN_HARDHDLC(chan)){ return 0; } DEBUG_TDMV( "[TDMV] %s: %s:%02d(%d) TX RBS: A:%1d B:%1d C:%1d D:%1d\n", wp->devname, (wp->ise1) ? "E1" : "T1", chan->chanpos, chan->channo, (ABCD_bits & WAN_RBS_SIG_A) ? 1 : 0, (ABCD_bits & WAN_RBS_SIG_B) ? 1 : 0, (ABCD_bits & WAN_RBS_SIG_C) ? 1 : 0, (ABCD_bits & WAN_RBS_SIG_D) ? 1 : 0); if (wan_test_and_set_bit(chan->chanpos-1, &wp->rbs_tx_status)){ if (ABCD_bits == wp->rbs_tx[chan->chanpos-1]){ return 0; } if (wan_test_and_set_bit(chan->chanpos-1, &wp->rbs_tx1_status)){ if (ABCD_bits == wp->rbs_tx1[chan->chanpos-1]){ return 0; } DEBUG_EVENT("%s: Critical Error: TX RBS for channel %d\n", wp->devname, chan->chanpos); } wp->rbs_tx1[chan->chanpos-1] = ABCD_bits; }else{ wp->rbs_tx[chan->chanpos-1] = ABCD_bits; } #if 0 wan_set_bit(7, &ABCD_bits); if (wan_test_and_set_bit(7, &wp->rbs_tx[chan->chanpos-1])){ if (ABCD_bits == wp->rbs_tx[chan->chanpos-1]){ return 0; } if (wan_test_and_set_bit(7, &wp->rbs_tx1[chan->chanpos-1])){ if (ABCD_bits == wp->rbs_tx1[chan->chanpos-1]){ return 0; } DEBUG_EVENT("%s: Critical Error: TX RBS for channel %d\n", wp->devname, chan->chanpos); } wp->rbs_tx1[chan->chanpos-1] = ABCD_bits; }else{ wp->rbs_tx[chan->chanpos-1] = ABCD_bits; } #endif return 0; } /****************************************************************************** ** wp_tdmv_is_rbsbits() - ** ** Returns: 1 - start RBS poll routine, 0 - otherwise */ static int wp_tdmv_is_rbsbits(wan_tdmv_t *wan_tdmv) { wp_tdmv_softc_t *wp = NULL; WAN_ASSERT(wan_tdmv->sc == NULL); wp = wan_tdmv->sc; /* Do not read signalling bits if Asterisk not configured to */ if (!IS_TDMV_SIG(wp)){ return 0; } if (wan_test_and_set_bit(WP_TDMV_RBS_BUSY, &wp->flags)){ /* RBS read still in progress or not ready*/ return 0; } if (wp->rbs_tx_status || wp->rbs_tx1_status){ return 1; } if (!IS_TDMV_UP(wp)){ wan_clear_bit(WP_TDMV_RBS_BUSY, &wp->flags); return 0; } /* Increment RX/TX interrupt counter */ wp->rbscount++; /* RBS_POLL ** Update RBS bits now (we don't have to do very often) */ if (!(wp->rbscount & 0xF)){ return 1; } /* Wait for the next time */ wan_clear_bit(WP_TDMV_RBS_BUSY, &wp->flags); return 0; } /****************************************************************************** ** wp_tdmv_rbsbits_poll() - ** ** DONE */ static int wp_tdmv_rbsbits_poll(wan_tdmv_t *wan_tdmv, void *card1) { sdla_t *card = (sdla_t*)card1; wp_tdmv_softc_t *wp = NULL; int i, x; WAN_ASSERT(wan_tdmv->sc == NULL); wp = wan_tdmv->sc; /* TX rbsbits */ if (wp->rbs_tx_status || wp->rbs_tx1_status){ wp_tdmv_tx_rbsbits(wp); } if (!IS_TDMV_UP(wp)){ wan_clear_bit(WP_TDMV_RBS_BUSY, &wp->flags); return 0; } if (wp->rbs_rx_pending){ DEBUG_TEST("%s: %s:%d: Reading RBS (pending)\n", wp->devname, __FUNCTION__,__LINE__); for(i=0; i < wp->max_timeslots;i++){ if (wan_test_bit(i, &wp->rbs_rx_pending)){ wan_clear_bit(i, &wp->rbs_rx_pending); card->wandev.fe_iface.read_rbsbits( &card->fe, i+1, WAN_TE_RBS_UPDATE|WAN_TE_RBS_REPORT); } } wan_set_bit(WP_TDMV_RBS_UPDATE, &wp->flags); return 0; } /* RX rbsbits */ DEBUG_TEST("%s: %s:%d: Reading RBS (%s)\n", wp->devname, __FUNCTION__,__LINE__, (wp->rbscount % 1000) ? "Normal" : "Sanity"); if (wp->rbscount % 1000 == 0){ for(x = 0; x < wp->max_timeslots; x++){ if (wan_test_bit(x, &wp->sig_timeslot_map)){ card->wandev.fe_iface.read_rbsbits( &card->fe, x+1, WAN_TE_RBS_UPDATE); } } }else{ if (card->wandev.fe_iface.check_rbsbits == NULL){ DEBUG_EVENT("%s: Internal Error [%s:%d]!\n", wp->devname, __FUNCTION__,__LINE__); return -EINVAL; } card->wandev.fe_iface.check_rbsbits( &card->fe, 1, wp->sig_timeslot_map, 0); card->wandev.fe_iface.check_rbsbits( &card->fe, 9, wp->sig_timeslot_map, 0); card->wandev.fe_iface.check_rbsbits( &card->fe, 17, wp->sig_timeslot_map, 0); if (wp->ise1){ card->wandev.fe_iface.check_rbsbits( &card->fe, 25, wp->sig_timeslot_map, 0); } } wan_set_bit(WP_TDMV_RBS_UPDATE, &wp->flags); return 0; } /****************************************************************************** ** wp_tdmv_tx_rbsbits() - ** ** DONE */ static int wp_tdmv_tx_rbsbits(wp_tdmv_softc_t *wp) { sdla_t *card; int x; WAN_ASSERT2(wp->card == NULL, 0); card = (sdla_t*)wp->card; for(x=0;xmax_timeslots;x++){ if (wan_test_bit(x, &wp->rbs_tx_status)){ card->wandev.fe_iface.set_rbsbits( &wp->card->fe, x+1, wp->rbs_tx[x]); wan_clear_bit(x, &wp->rbs_tx_status); if (wan_test_bit(x, &wp->rbs_tx1_status)){ card->wandev.fe_iface.set_rbsbits( &wp->card->fe, x+1, wp->rbs_tx1[x]); wan_clear_bit(x, &wp->rbs_tx1_status); } } } #if 0 for(x=0;xmax_timeslots;x++){ if (wan_test_bit(7, &wp->rbs_tx[x])){ card->wandev.fe_iface.set_rbsbits( &wp->card->fe, x+1, wp->rbs_tx[x]); wan_clear_bit(7, &wp->rbs_tx[x]); if (wan_test_bit(7, &wp->rbs_tx1[x])){ card->wandev.fe_iface.set_rbsbits( &wp->card->fe, x+1, wp->rbs_tx1[x]); wan_clear_bit(7, &wp->rbs_tx1[x]); } } } #endif return 0; } /****************************************************************************** ** wp_tdmv_ioctl() - ** ** OK */ static int #if defined(__FreeBSD__) || defined(__OpenBSD__) wp_tdmv_ioctl(struct zt_chan *chan, unsigned int cmd, caddr_t data) #else wp_tdmv_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) #endif { int err = -ENOTTY; wp_tdmv_softc_t *wp=NULL; #ifdef CONFIG_PRODUCT_WANPIPE_TDM_VOICE_ECHOMASTER wp_tdmv_softc_t *echo_detect_wp = NULL; int echo_detect_chan = 0; #endif wp = chan->pvt; switch(cmd) { #if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN) && defined(ZT_DCHAN_TX) case ZT_DCHAN_TX: 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{ err=-EOPNOTSUPP; } break; #endif #ifdef CONFIG_PRODUCT_WANPIPE_TDM_VOICE_ECHOMASTER case SANGOMA_GET_ED_STATE: DEBUG_ECHO("%s():SANGOMA_GET_ED_STATE\n", __FUNCTION__); WAN_ASSERT(chan == NULL || chan->pvt == NULL); echo_detect_wp = chan->pvt; echo_detect_chan = chan->chanpos - 1; DEBUG_ECHO("on span: %d, chanpos: %d\n", echo_detect_wp->spanno, echo_detect_chan); if(echo_detect_chan > 30 || echo_detect_chan < 0){ err=-EOPNOTSUPP; }else{ wan_tdmv_t *wan_tdmv = &echo_detect_wp->card->wan_tdmv; wan_tdmv_rxtx_pwr_t *pwr_rxtx = &wan_tdmv->chan_pwr[echo_detect_chan]; DEBUG_ECHO("%s():using %s table.\n", __FUNCTION__, (chan->xlaw == __zt_mulaw ? "MULAW" : "ALAW")); /* This will be used when reporting Echo Cancellation state from Asterisk CLI. */ chan->echo_detect_struct.echo_state = pwr_rxtx->current_state; DEBUG_ECHO("echo_state:%s\n", TDMV_SAMPLE_STATE_DECODE(chan->echo_detect_struct.echo_state)); chan->echo_detect_struct.echo_present_samples_number = pwr_rxtx->echo_present_samples_number_history; chan->echo_detect_struct.echo_absent_samples_number = pwr_rxtx->echo_absent_samples_number_history; } err = 0; break; #endif /* CONFIG_PRODUCT_WANPIPE_TDM_VOICE_ECHOMASTER */ default: /*DEBUG_EVENT("%s(): uknown cmd!\n", __FUNCTION__);*/ err = -ENOTTY; break; } return err; } /****************************************************************************** ** wp_tdmv_hwec() - ** ** OK */ static int wp_tdmv_hwec(struct zt_chan *chan, int enable) { wp_tdmv_softc_t *wp = NULL; sdla_t *card = NULL; int channel = chan->chanpos; int err = -EINVAL; WAN_ASSERT2(chan == NULL, -ENODEV); WAN_ASSERT2(chan->pvt == NULL, -ENODEV); wp = chan->pvt; WAN_ASSERT2(wp->card == NULL, -ENODEV); card = wp->card; if (card->wandev.ec_enable){ DEBUG_TDMV("[TDMV]: %s: %s HW echo canceller on channel %d\n", wp->devname, (enable) ? "Enable" : "Disable", channel); if (!wp->ise1){ channel--; } /* 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 || IS_CHAN_HARDHDLC(chan)) { err = card->wandev.ec_enable(card, enable, channel); } else { err = 0; } } return err; } /****************************************************************************** ** wp_tdmv_open() - ** ** OK */ static int wp_tdmv_open(struct zt_chan *chan) { wp_tdmv_softc_t *wp = NULL; sdla_t *card = NULL; WAN_ASSERT2(chan == NULL, -ENODEV); WAN_ASSERT2(chan->pvt == NULL, -ENODEV); wp = chan->pvt; WAN_ASSERT2(wp->card == NULL, -ENODEV); card = wp->card; wp->usecount++; DEBUG_TDMV("%s: Open (usecount=%d, channo=%d, chanpos=%d)...\n", wp->devname, wp->usecount, chan->channo, chan->chanpos); return 0; } /****************************************************************************** ** wp_tdmv_close() - ** ** OK */ static int wp_tdmv_close(struct zt_chan *chan) { wp_tdmv_softc_t* wp = NULL; WAN_ASSERT2(chan == NULL, -ENODEV); WAN_ASSERT2(chan->pvt == NULL, -ENODEV); wp = chan->pvt; wp->usecount--; DEBUG_TDMV("%s: Close (usecount=%d, channo=%d, chanpos=%d)...\n", wp->devname, wp->usecount, chan->channo, chan->chanpos); return 0; } /****************************************************************************** ** wp_tdmv_release() - ** ** OK */ static void wp_tdmv_release(wp_tdmv_softc_t *wp) { WAN_ASSERT1(wp == NULL); if (wan_test_bit(WP_TDMV_REGISTER, &wp->flags)){ DEBUG_EVENT("%s: Unregister Wanpipe device from Zaptel!\n", wp->devname); wan_clear_bit(WP_TDMV_SIG_POLL, &wp->flags); wan_clear_bit(WP_TDMV_REGISTER, &wp->flags); zt_unregister(&wp->span); wan_clear_bit(WP_TDMV_REGISTER, &wp->flags); } wan_free(wp); } static inline void start_alarm(wp_tdmv_softc_t* wp) { WAN_ASSERT1(wp == NULL); #ifdef FANCY_ALARM wp->alarmpos = 0; #endif wp->blinktimer = 0; } static inline void stop_alarm(wp_tdmv_softc_t* wp) { WAN_ASSERT1(wp == NULL); #ifdef FANCY_ALARM wp->alarmpos = 0; #endif wp->blinktimer = 0; } /************************************************************************************ * Channelized code for rx/tx * *********************************************************************************/ #if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN) /****************************************************************************** ** 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_softc_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; WAN_ASSERT(wp == NULL); if (!wan_test_bit(channo, &wp->dchan_map)) { DEBUG_EVENT("%s: Internal Error: DCHAN Mismatch channo=%i 0x%08X\n", wp->devname,channo,wp->dchan_map); return -EINVAL; } chan = &wp->chans[channo]; WAN_ASSERT(chan == NULL || chan->master == NULL); ms = chan->master; if (!IS_TDMV_UP(wp)){ DEBUG_TDMV("%s: Asterisk is not running!\n", wp->devname); return -EINVAL; } if (!IS_CHAN_HARDHDLC(ms)){ DEBUG_TDMV("%s: ERROR: %s not defined as D-CHAN!\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; } #endif #if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN) && defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN_ZAPTEL) static void wp_tdmv_tx_hdlc_hard(struct zt_chan *chan) { wp_tdmv_softc_t *wp = NULL; netskb_t *skb = NULL; unsigned char *data = NULL; int size = 0, err = 0, res = 0; WAN_ASSERT_VOID(chan == NULL); WAN_ASSERT_VOID(chan->pvt == NULL); wp = chan->pvt; WAN_ASSERT_VOID(wp->dchan_dev == NULL); size = chan->writen[chan->outwritebuf] - chan->writeidx[chan->outwritebuf]-2; skb = wan_skb_alloc(size+1); if (skb == NULL){ return; } data = wan_skb_put(skb, size); res = zt_hdlc_getbuf(chan, data, &size); if (res == 0){ DEBUG_EVENT("%s: ERROR: TX HW DCHAN %d bytes (res %d)\n", wp->devname, size, res); } err = wp->dchan_dev->hard_start_xmit(skb, wp->dchan_dev); if (err){ wan_skb_free(skb); } return; } #endif #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_softc_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("TX DCHAN: "); 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_chan() - ** ** OK */ static int wp_tdmv_rx_chan(wan_tdmv_t *wan_tdmv, int channo, unsigned char *rxbuf, unsigned char *txbuf) { wp_tdmv_softc_t *wp = wan_tdmv->sc; sdla_t *card; WAN_ASSERT2(wp == NULL, -EINVAL); WAN_ASSERT2(channo < 0, -EINVAL); WAN_ASSERT2(channo > 31, -EINVAL); if (!IS_TDMV_UP(wp)){ return -EINVAL; } card = wp->card; if (wp->channelized == WAN_TRUE){ #ifdef CONFIG_PRODUCT_WANPIPE_TDM_VOICE_ECHOMASTER wan_tdmv_rxtx_pwr_t *pwr_rxtx = &wan_tdmv->chan_pwr[channo]; #endif wp->chans[channo].readchunk = rxbuf; wp->chans[channo].writechunk = txbuf; #ifdef CONFIG_PRODUCT_WANPIPE_TDM_VOICE_ECHOMASTER wp_tdmv_echo_check(wan_tdmv, &wp->chans[channo], channo); #endif /* If using hwec do not even call ec chunk */ if ((!card->wandev.ec_enable || card->wandev.ec_enable_map == 0) && !wan_test_bit(channo, &wp->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( &wp->chans[channo], wp->chans[channo].readchunk, wp->chans[channo].writechunk); #endif #if 1 /*Echo spike starts at 9 bytes*/ zt_ec_chunk( &wp->chans[channo], wp->chans[channo].readchunk, wp->ec_chunk1[channo]); memcpy( wp->ec_chunk1[channo], wp->chans[channo].writechunk, ZT_CHUNKSIZE); #endif #if 0 /*Echo spike starts at bytes*/ zt_ec_chunk( &wp->chans[channo], wp->chans[channo].readchunk, wp->ec_chunk1[channo]); memcpy( wp->ec_chunk1[channo], wp->ec_chunk2[channo], ZT_CHUNKSIZE); memcpy( wp->ec_chunk2[channo], wp->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, &wp->echo_off_map)) */ }else{ int x, channels = wp->span.channels; wp->tx_unchan = txbuf; wp_tdmv_rxcopy(wan_tdmv, rxbuf, wp->max_rxtx_len); if (!wp->echo_off_map){ for (x = 0; x < channels; x++){ #if 0 /* This code never runs. Instead wp_tdmv_rx_chan() ** is called for A101/A102. All Echo Detection is ** done there for A101/A102. */ wp_tdmv_echo_check(wan_tdmv, &wp->chans[0], x); #endif zt_ec_chunk( &wp->chans[x], wp->chans[x].readchunk, wp->chans[x].writechunk); #if 0 zt_ec_chunk( &wp->chans[x], wp->chans[x].readchunk, wp->ec_chunk1[x]); memcpy( wp->ec_chunk1[x], wp->chans[x].writechunk, ZT_CHUNKSIZE); #endif } }/* if() */ }/* if() */ return 0; } static int wp_tdmv_span_buf_rotate(void *pcard, u32 buf_sz, unsigned long mask) { sdla_t *card = (sdla_t*)pcard; wan_tdmv_t *wan_tdmv = &card->wan_tdmv; wp_tdmv_softc_t *wp = NULL; int x; unsigned int rx_offset, tx_offset; void *ptr; WAN_ASSERT(wan_tdmv->sc == NULL); wp = wan_tdmv->sc; rx_offset = buf_sz * card->u.aft.tdm_rx_dma_toggle; tx_offset = buf_sz * card->u.aft.tdm_tx_dma_toggle; for (x = 0; x < 32; x ++) { if (wan_test_bit(x,&wp->timeslot_map)) { if (card->u.aft.tdmv_dchan && (card->u.aft.tdmv_dchan-1) == x) { continue; } 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 } } return 0; } static int wp_tdmv_ec_span(void *pcard) { sdla_t *card = (sdla_t*)pcard; wan_tdmv_t *wan_tdmv = &card->wan_tdmv; wp_tdmv_softc_t *wp = NULL; WAN_ASSERT(wan_tdmv->sc == NULL); wp = wan_tdmv->sc; zt_ec_span(&wp->span); return 0; } /****************************************************************************** ** wp_tdmv_rx_tx_span() - ** ** OK */ static int wp_tdmv_rx_tx_span(void *pcard) { sdla_t *card = (sdla_t*)pcard; wan_tdmv_t *wan_tdmv = &card->wan_tdmv; wp_tdmv_softc_t *wp = NULL; WAN_ASSERT(wan_tdmv->sc == NULL); wp = wan_tdmv->sc; wp->intcount++; zt_receive(&wp->span); zt_transmit(&wp->span); if (wp->channelized == WAN_FALSE){ wp_tdmv_txcopy(wan_tdmv, wp->tx_unchan, wp->max_rxtx_len); } if (wan_test_bit(WP_TDMV_RBS_UPDATE, &wp->flags)){ DEBUG_TEST("%s: %s:%d: Updating RBS status \n", wp->devname, __FUNCTION__,__LINE__); if (card->wandev.fe_iface.report_rbsbits){ card->wandev.fe_iface.report_rbsbits(&card->fe); } wan_clear_bit(WP_TDMV_RBS_UPDATE, &wp->flags); wan_clear_bit(WP_TDMV_RBS_BUSY, &wp->flags); } return 0; } /****************************************************************************** ** wp_tdmv_init() - * ** OK */ static int wp_tdmv_init(void* pcard, wanif_conf_t *conf) { return -EINVAL; }