2010-07-30 19:46:05 -04:00
/*
* Copyright ( c ) 2010 , Moises Silva < moy @ sangoma . com >
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
*
* * Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
*
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
*
* * Neither the name of the original author ; nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission .
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
* LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL ,
* EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO ,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR
* PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING
* NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
# include <libpri.h>
# include <poll.h>
# include "private/ftdm_core.h"
2010-07-31 17:33:20 -04:00
# define PRI_SPAN(p) (((p) >> 8) & 0xff)
# define PRI_CHANNEL(p) ((p) & 0xff)
2010-07-30 19:46:05 -04:00
typedef enum {
PRITAP_RUNNING = ( 1 < < 0 ) ,
} pritap_flags_t ;
2010-07-31 17:33:20 -04:00
typedef struct {
void * callref ;
ftdm_number_t callingnum ;
ftdm_number_t callingani ;
ftdm_number_t callednum ;
ftdm_channel_t * fchan ;
char callingname [ 80 ] ;
int proceeding : 1 ;
int inuse : 1 ;
} passive_call_t ;
2010-07-30 19:46:05 -04:00
typedef struct pritap {
int32_t flags ;
struct pri * pri ;
int debug ;
ftdm_channel_t * dchan ;
ftdm_span_t * span ;
ftdm_span_t * peerspan ;
2010-07-31 17:33:20 -04:00
ftdm_mutex_t * pcalls_lock ;
passive_call_t pcalls [ FTDM_MAX_CHANNELS_PHYSICAL_SPAN ] ;
2010-07-30 19:46:05 -04:00
} pritap_t ;
static FIO_IO_UNLOAD_FUNCTION ( ftdm_pritap_unload )
{
return FTDM_SUCCESS ;
}
static FIO_CHANNEL_GET_SIG_STATUS_FUNCTION ( pritap_get_channel_sig_status )
{
* status = FTDM_SIG_STATE_UP ;
return FTDM_SUCCESS ;
}
static FIO_SPAN_GET_SIG_STATUS_FUNCTION ( pritap_get_span_sig_status )
{
* status = FTDM_SIG_STATE_UP ;
return FTDM_SUCCESS ;
}
static FIO_CHANNEL_OUTGOING_CALL_FUNCTION ( pritap_outgoing_call )
{
ftdm_log ( FTDM_LOG_ERROR , " Cannot dial on PRI tapping line! \n " ) ;
return FTDM_FAIL ;
}
static void s_pri_error ( struct pri * pri , char * s )
{
ftdm_log ( FTDM_LOG_ERROR , " %s " , s ) ;
}
static void s_pri_message ( struct pri * pri , char * s )
{
ftdm_log ( FTDM_LOG_DEBUG , " %s " , s ) ;
}
static int parse_debug ( const char * in )
{
int flags = 0 ;
if ( ! in ) {
return 0 ;
}
if ( strstr ( in , " q921_raw " ) ) {
flags | = PRI_DEBUG_Q921_RAW ;
}
if ( strstr ( in , " q921_dump " ) ) {
flags | = PRI_DEBUG_Q921_DUMP ;
}
if ( strstr ( in , " q921_state " ) ) {
flags | = PRI_DEBUG_Q921_STATE ;
}
if ( strstr ( in , " config " ) ) {
flags | = PRI_DEBUG_CONFIG ;
}
if ( strstr ( in , " q931_dump " ) ) {
flags | = PRI_DEBUG_Q931_DUMP ;
}
if ( strstr ( in , " q931_state " ) ) {
flags | = PRI_DEBUG_Q931_STATE ;
}
if ( strstr ( in , " q931_anomaly " ) ) {
flags | = PRI_DEBUG_Q931_ANOMALY ;
}
if ( strstr ( in , " apdu " ) ) {
flags | = PRI_DEBUG_APDU ;
}
if ( strstr ( in , " aoc " ) ) {
flags | = PRI_DEBUG_AOC ;
}
if ( strstr ( in , " all " ) ) {
flags | = PRI_DEBUG_ALL ;
}
if ( strstr ( in , " none " ) ) {
flags = 0 ;
}
return flags ;
}
static ftdm_io_interface_t ftdm_pritap_interface ;
static ftdm_status_t ftdm_pritap_start ( ftdm_span_t * span ) ;
static FIO_API_FUNCTION ( ftdm_pritap_api )
{
char * mycmd = NULL , * argv [ 10 ] = { 0 } ;
int argc = 0 ;
if ( data ) {
mycmd = ftdm_strdup ( data ) ;
argc = ftdm_separate_string ( mycmd , ' ' , argv , ( sizeof ( argv ) / sizeof ( argv [ 0 ] ) ) ) ;
}
if ( argc > 2 ) {
if ( ! strcasecmp ( argv [ 0 ] , " debug " ) ) {
ftdm_span_t * span = NULL ;
if ( ftdm_span_find_by_name ( argv [ 1 ] , & span ) = = FTDM_SUCCESS ) {
pritap_t * pritap = span - > signal_data ;
if ( span - > start ! = ftdm_pritap_start ) {
stream - > write_function ( stream , " %s: -ERR invalid span. \n " , __FILE__ ) ;
goto done ;
}
pri_set_debug ( pritap - > pri , parse_debug ( argv [ 2 ] ) ) ;
stream - > write_function ( stream , " %s: +OK debug set. \n " , __FILE__ ) ;
goto done ;
} else {
stream - > write_function ( stream , " %s: -ERR invalid span. \n " , __FILE__ ) ;
goto done ;
}
}
}
stream - > write_function ( stream , " %s: -ERR invalid command. \n " , __FILE__ ) ;
done :
ftdm_safe_free ( mycmd ) ;
return FTDM_SUCCESS ;
}
static FIO_IO_LOAD_FUNCTION ( ftdm_pritap_io_init )
{
memset ( & ftdm_pritap_interface , 0 , sizeof ( ftdm_pritap_interface ) ) ;
ftdm_pritap_interface . name = " pritap " ;
ftdm_pritap_interface . api = ftdm_pritap_api ;
* fio = & ftdm_pritap_interface ;
return FTDM_SUCCESS ;
}
static FIO_SIG_LOAD_FUNCTION ( ftdm_pritap_init )
{
pri_set_error ( s_pri_error ) ;
pri_set_message ( s_pri_message ) ;
return FTDM_SUCCESS ;
}
static ftdm_state_map_t pritap_state_map = {
{
{
ZSD_INBOUND ,
ZSM_UNACCEPTABLE ,
{ FTDM_CHANNEL_STATE_DOWN , FTDM_END } ,
{ FTDM_CHANNEL_STATE_RING , FTDM_END }
} ,
{
ZSD_INBOUND ,
ZSM_UNACCEPTABLE ,
{ FTDM_CHANNEL_STATE_RING , FTDM_END } ,
{ FTDM_CHANNEL_STATE_HANGUP , FTDM_CHANNEL_STATE_TERMINATING , FTDM_CHANNEL_STATE_PROGRESS , FTDM_CHANNEL_STATE_PROGRESS_MEDIA , FTDM_CHANNEL_STATE_UP , FTDM_END }
} ,
{
ZSD_INBOUND ,
ZSM_UNACCEPTABLE ,
2010-07-31 17:33:20 -04:00
{ FTDM_CHANNEL_STATE_HANGUP , FTDM_END } ,
{ FTDM_CHANNEL_STATE_TERMINATING , FTDM_END } ,
2010-07-30 19:46:05 -04:00
} ,
{
ZSD_INBOUND ,
ZSM_UNACCEPTABLE ,
2010-07-31 17:33:20 -04:00
{ FTDM_CHANNEL_STATE_TERMINATING , FTDM_END } ,
2010-07-30 19:46:05 -04:00
{ FTDM_CHANNEL_STATE_DOWN , FTDM_END } ,
} ,
{
ZSD_INBOUND ,
ZSM_UNACCEPTABLE ,
2010-07-31 17:33:20 -04:00
{ FTDM_CHANNEL_STATE_PROGRESS , FTDM_END } ,
{ FTDM_CHANNEL_STATE_HANGUP , FTDM_CHANNEL_STATE_TERMINATING , FTDM_CHANNEL_STATE_PROGRESS_MEDIA , FTDM_CHANNEL_STATE_UP , FTDM_END } ,
} ,
{
ZSD_INBOUND ,
ZSM_UNACCEPTABLE ,
{ FTDM_CHANNEL_STATE_PROGRESS_MEDIA , FTDM_END } ,
{ FTDM_CHANNEL_STATE_HANGUP , FTDM_CHANNEL_STATE_TERMINATING , FTDM_CHANNEL_STATE_UP , FTDM_END } ,
2010-07-30 19:46:05 -04:00
} ,
{
ZSD_INBOUND ,
ZSM_UNACCEPTABLE ,
{ FTDM_CHANNEL_STATE_UP , FTDM_END } ,
{ FTDM_CHANNEL_STATE_HANGUP , FTDM_CHANNEL_STATE_TERMINATING , FTDM_END } ,
} ,
}
} ;
static __inline__ void state_advance ( ftdm_channel_t * ftdmchan )
{
ftdm_status_t status ;
ftdm_sigmsg_t sig ;
2010-07-31 17:33:20 -04:00
ftdm_channel_t * peerchan = ftdmchan - > call_data ;
2010-07-30 19:46:05 -04:00
2010-07-31 17:33:20 -04:00
ftdm_log_chan ( ftdmchan , FTDM_LOG_DEBUG , " processing state %s \n " , ftdm_channel_state2str ( ftdmchan - > state ) ) ;
2010-07-30 19:46:05 -04:00
memset ( & sig , 0 , sizeof ( sig ) ) ;
sig . chan_id = ftdmchan - > chan_id ;
sig . span_id = ftdmchan - > span_id ;
sig . channel = ftdmchan ;
switch ( ftdmchan - > state ) {
case FTDM_CHANNEL_STATE_DOWN :
2010-12-15 12:56:49 -05:00
{
2010-07-31 17:33:20 -04:00
ftdmchan - > call_data = NULL ;
2010-12-15 12:56:49 -05:00
ftdm_channel_close ( & ftdmchan ) ;
2010-07-30 19:46:05 -04:00
2010-07-31 17:33:20 -04:00
peerchan - > call_data = NULL ;
2010-12-15 12:56:49 -05:00
ftdm_channel_close ( & peerchan ) ;
2010-07-30 19:46:05 -04:00
}
break ;
2010-07-31 17:33:20 -04:00
case FTDM_CHANNEL_STATE_PROGRESS :
2010-07-30 19:46:05 -04:00
case FTDM_CHANNEL_STATE_PROGRESS_MEDIA :
2010-07-31 17:33:20 -04:00
case FTDM_CHANNEL_STATE_UP :
case FTDM_CHANNEL_STATE_HANGUP :
2010-07-30 19:46:05 -04:00
break ;
case FTDM_CHANNEL_STATE_RING :
{
sig . event_id = FTDM_SIGEVENT_START ;
if ( ( status = ftdm_span_send_signal ( ftdmchan - > span , & sig ) ! = FTDM_SUCCESS ) ) {
ftdm_set_state_locked ( ftdmchan , FTDM_CHANNEL_STATE_HANGUP ) ;
}
}
break ;
case FTDM_CHANNEL_STATE_TERMINATING :
{
2010-07-31 17:33:20 -04:00
if ( ftdmchan - > last_state ! = FTDM_CHANNEL_STATE_HANGUP ) {
sig . event_id = FTDM_SIGEVENT_STOP ;
status = ftdm_span_send_signal ( ftdmchan - > span , & sig ) ;
}
2010-07-30 19:46:05 -04:00
ftdm_set_state_locked ( ftdmchan , FTDM_CHANNEL_STATE_DOWN ) ;
}
2010-07-31 17:33:20 -04:00
break ;
2010-07-30 19:46:05 -04:00
default :
{
ftdm_log_chan ( ftdmchan , FTDM_LOG_DEBUG , " ignoring state change from %s to %s \n " , ftdm_channel_state2str ( ftdmchan - > last_state ) , ftdm_channel_state2str ( ftdmchan - > state ) ) ;
}
break ;
}
return ;
}
static __inline__ void pritap_check_state ( ftdm_span_t * span )
{
if ( ftdm_test_flag ( span , FTDM_SPAN_STATE_CHANGE ) ) {
uint32_t j ;
ftdm_clear_flag_locked ( span , FTDM_SPAN_STATE_CHANGE ) ;
for ( j = 1 ; j < = span - > chan_count ; j + + ) {
if ( ftdm_test_flag ( ( span - > channels [ j ] ) , FTDM_CHANNEL_STATE_CHANGE ) ) {
ftdm_mutex_lock ( span - > channels [ j ] - > mutex ) ;
ftdm_clear_flag ( ( span - > channels [ j ] ) , FTDM_CHANNEL_STATE_CHANGE ) ;
state_advance ( span - > channels [ j ] ) ;
ftdm_channel_complete_state ( span - > channels [ j ] ) ;
ftdm_mutex_unlock ( span - > channels [ j ] - > mutex ) ;
}
}
}
}
static int pri_io_read ( struct pri * pri , void * buf , int buflen )
{
int res ;
ftdm_status_t zst ;
pritap_t * pritap = pri_get_userdata ( pri ) ;
ftdm_size_t len = buflen ;
if ( ( zst = ftdm_channel_read ( pritap - > dchan , buf , & len ) ) ! = FTDM_SUCCESS ) {
if ( zst = = FTDM_FAIL ) {
ftdm_log ( FTDM_LOG_CRIT , " span %d D channel read fail! [%s] \n " , pritap - > span - > span_id , pritap - > dchan - > last_error ) ;
} else {
ftdm_log ( FTDM_LOG_CRIT , " span %d D channel read timeout! \n " , pritap - > span - > span_id ) ;
}
return - 1 ;
}
res = ( int ) len ;
memset ( & ( ( unsigned char * ) buf ) [ res ] , 0 , 2 ) ;
res + = 2 ;
return res ;
}
static int pri_io_write ( struct pri * pri , void * buf , int buflen )
{
pritap_t * pritap = pri_get_userdata ( pri ) ;
ftdm_size_t len = buflen - 2 ;
if ( ftdm_channel_write ( pritap - > dchan , buf , buflen , & len ) ! = FTDM_SUCCESS ) {
ftdm_log ( FTDM_LOG_CRIT , " span %d D channel write failed! [%s] \n " , pritap - > span - > span_id , pritap - > dchan - > last_error ) ;
return - 1 ;
}
return ( int ) buflen ;
}
2010-07-31 17:33:20 -04:00
static int tap_pri_get_crv ( struct pri * ctrl , q931_call * call )
{
int callmode = 0 ;
int crv = pri_get_crv ( ctrl , call , & callmode ) ;
crv < < = 3 ;
crv | = ( callmode & 0x7 ) ;
return crv ;
}
static passive_call_t * tap_pri_get_pcall_bycrv ( pritap_t * pritap , int crv )
{
int i ;
int tstcrv ;
ftdm_mutex_lock ( pritap - > pcalls_lock ) ;
for ( i = 0 ; i < ftdm_array_len ( pritap - > pcalls ) ; i + + ) {
tstcrv = pritap - > pcalls [ i ] . callref ? tap_pri_get_crv ( pritap - > pri , pritap - > pcalls [ i ] . callref ) : 0 ;
if ( pritap - > pcalls [ i ] . callref & & tstcrv = = crv ) {
if ( ! pritap - > pcalls [ i ] . inuse ) {
ftdm_log ( FTDM_LOG_ERROR , " Found crv %d in slot %d of span %s with call %p but is no longer in use! \n " ,
crv , i , pritap - > span - > name , pritap - > pcalls [ i ] . callref ) ;
continue ;
}
ftdm_mutex_unlock ( pritap - > pcalls_lock ) ;
return & pritap - > pcalls [ i ] ;
}
}
ftdm_mutex_unlock ( pritap - > pcalls_lock ) ;
return NULL ;
}
static passive_call_t * tap_pri_get_pcall ( pritap_t * pritap , void * callref )
{
int i ;
int crv ;
ftdm_mutex_lock ( pritap - > pcalls_lock ) ;
for ( i = 0 ; i < ftdm_array_len ( pritap - > pcalls ) ; i + + ) {
if ( pritap - > pcalls [ i ] . callref & & ! pritap - > pcalls [ i ] . inuse ) {
crv = tap_pri_get_crv ( pritap - > pri , pritap - > pcalls [ i ] . callref ) ;
/* garbage collection */
ftdm_log ( FTDM_LOG_DEBUG , " Garbage collecting callref %d/%p from span %s in slot %d \n " ,
crv , pritap - > pcalls [ i ] . callref , pritap - > span - > name , i ) ;
pri_passive_destroycall ( pritap - > pri , pritap - > pcalls [ i ] . callref ) ;
memset ( & pritap - > pcalls [ i ] , 0 , sizeof ( pritap - > pcalls [ 0 ] ) ) ;
}
if ( callref = = pritap - > pcalls [ i ] . callref ) {
pritap - > pcalls [ i ] . inuse = 1 ;
ftdm_mutex_unlock ( pritap - > pcalls_lock ) ;
return & pritap - > pcalls [ i ] ;
}
}
ftdm_mutex_unlock ( pritap - > pcalls_lock ) ;
return NULL ;
}
static void tap_pri_put_pcall ( pritap_t * pritap , void * callref )
{
int i ;
int crv ;
int tstcrv ;
if ( ! callref ) {
ftdm_log ( FTDM_LOG_ERROR , " Cannot put pcall for null callref in span %s \n " , pritap - > span - > name ) ;
return ;
}
ftdm_mutex_lock ( pritap - > pcalls_lock ) ;
crv = tap_pri_get_crv ( pritap - > pri , callref ) ;
for ( i = 0 ; i < ftdm_array_len ( pritap - > pcalls ) ; i + + ) {
if ( ! pritap - > pcalls [ i ] . callref ) {
continue ;
}
tstcrv = tap_pri_get_crv ( pritap - > pri , pritap - > pcalls [ i ] . callref ) ;
if ( tstcrv = = crv ) {
ftdm_log ( FTDM_LOG_DEBUG , " releasing slot %d in span %s used by callref %d/%p \n " , i ,
pritap - > span - > name , crv , pritap - > pcalls [ i ] . callref ) ;
if ( ! pritap - > pcalls [ i ] . inuse ) {
ftdm_log ( FTDM_LOG_ERROR , " slot %d in span %s used by callref %d/%p was released already? \n " ,
i , pritap - > span - > name , crv , pritap - > pcalls [ i ] . callref ) ;
}
pritap - > pcalls [ i ] . inuse = 0 ;
}
}
ftdm_mutex_unlock ( pritap - > pcalls_lock ) ;
}
static __inline__ ftdm_channel_t * tap_pri_get_fchan ( pritap_t * pritap , passive_call_t * pcall , int channel )
{
ftdm_channel_t * fchan = NULL ;
int chanpos = PRI_CHANNEL ( channel ) ;
if ( ! chanpos | | chanpos > pritap - > span - > chan_count ) {
ftdm_log ( FTDM_LOG_CRIT , " Invalid pri tap channel %d requested in span %s \n " , channel , pritap - > span - > name ) ;
return NULL ;
}
fchan = pritap - > span - > channels [ PRI_CHANNEL ( channel ) ] ;
if ( ftdm_test_flag ( fchan , FTDM_CHANNEL_INUSE ) ) {
ftdm_log ( FTDM_LOG_ERROR , " Channel %d requested in span %s is already in use! \n " , channel , pritap - > span - > name ) ;
return NULL ;
}
if ( ftdm_channel_open_chan ( fchan ) ! = FTDM_SUCCESS ) {
ftdm_log ( FTDM_LOG_ERROR , " Could not open tap channel %d requested in span %s \n " , channel , pritap - > span - > name ) ;
return NULL ;
}
memset ( & fchan - > caller_data , 0 , sizeof ( fchan - > caller_data ) ) ;
ftdm_set_string ( fchan - > caller_data . cid_num . digits , pcall - > callingnum . digits ) ;
if ( ! ftdm_strlen_zero ( pcall - > callingname ) ) {
ftdm_set_string ( fchan - > caller_data . cid_name , pcall - > callingname ) ;
} else {
ftdm_set_string ( fchan - > caller_data . cid_name , pcall - > callingnum . digits ) ;
}
ftdm_set_string ( fchan - > caller_data . ani . digits , pcall - > callingani . digits ) ;
ftdm_set_string ( fchan - > caller_data . dnis . digits , pcall - > callednum . digits ) ;
return fchan ;
}
2010-07-30 19:46:05 -04:00
static void handle_pri_passive_event ( pritap_t * pritap , pri_event * e )
{
2010-07-31 17:33:20 -04:00
passive_call_t * pcall = NULL ;
passive_call_t * peerpcall = NULL ;
ftdm_channel_t * fchan = NULL ;
ftdm_channel_t * peerfchan = NULL ;
int layer1 , transcap = 0 ;
int crv = 0 ;
pritap_t * peertap = pritap - > peerspan - > signal_data ;
2010-07-30 19:46:05 -04:00
switch ( e - > e ) {
case PRI_EVENT_RING :
2010-07-31 17:33:20 -04:00
/* we cannot use ftdm_channel_t because we still dont know which channel will be used
* ( ie , flexible channel was requested ) , thus , we need our own list of call references */
crv = tap_pri_get_crv ( pritap - > pri , e - > ring . call ) ;
ftdm_log ( FTDM_LOG_DEBUG , " Ring on channel %s:%d:%d with callref %d \n " ,
pritap - > span - > name , PRI_SPAN ( e - > ring . channel ) , PRI_CHANNEL ( e - > ring . channel ) , crv ) ;
pcall = tap_pri_get_pcall_bycrv ( pritap , crv ) ;
if ( pcall ) {
ftdm_log ( FTDM_LOG_WARNING , " There is a call with callref %d already, ignoring duplicated ring event \n " , crv ) ;
break ;
}
pcall = tap_pri_get_pcall ( pritap , NULL ) ;
if ( ! pcall ) {
ftdm_log ( FTDM_LOG_ERROR , " Failed to get a free passive PRI call slot for callref %d, this is a bug! \n " , crv ) ;
break ;
}
pcall - > callref = e - > ring . call ;
ftdm_set_string ( pcall - > callingnum . digits , e - > ring . callingnum ) ;
ftdm_set_string ( pcall - > callingani . digits , e - > ring . callingani ) ;
ftdm_set_string ( pcall - > callednum . digits , e - > ring . callednum ) ;
ftdm_set_string ( pcall - > callingname , e - > ring . callingname ) ;
2010-07-30 19:46:05 -04:00
break ;
case PRI_EVENT_PROGRESS :
2010-07-31 17:33:20 -04:00
crv = tap_pri_get_crv ( pritap - > pri , e - > ring . call ) ;
ftdm_log ( FTDM_LOG_DEBUG , " Progress on channel %s:%d:%d with callref %d \n " ,
pritap - > span - > name , PRI_SPAN ( e - > proceeding . channel ) , PRI_CHANNEL ( e - > proceeding . channel ) , crv ) ;
2010-07-30 19:46:05 -04:00
break ;
case PRI_EVENT_PROCEEDING :
2010-07-31 17:33:20 -04:00
crv = tap_pri_get_crv ( pritap - > pri , e - > proceeding . call ) ;
/* at this point we should know the real b chan that will be used and can therefore proceed to notify about the call, but
* only if a couple of call tests are passed first */
ftdm_log ( FTDM_LOG_DEBUG , " Proceeding on channel %s:%d:%d with callref %d \n " ,
pritap - > span - > name , PRI_SPAN ( e - > proceeding . channel ) , PRI_CHANNEL ( e - > proceeding . channel ) , crv ) ;
/* check that we already know about this call in the peer PRI (which was the one receiving the PRI_EVENT_RING event) */
if ( ! ( pcall = tap_pri_get_pcall_bycrv ( peertap , crv ) ) ) {
ftdm_log ( FTDM_LOG_DEBUG ,
" ignoring proceeding in channel %s:%d:%d for callref %d since we don't know about it " ,
pritap - > span - > name , PRI_SPAN ( e - > proceeding . channel ) , PRI_CHANNEL ( e - > proceeding . channel ) , crv ) ;
break ;
}
if ( pcall - > proceeding ) {
ftdm_log ( FTDM_LOG_DEBUG , " Ignoring duplicated proceeding with callref %d \n " , crv ) ;
break ;
}
peerpcall = tap_pri_get_pcall ( pritap , NULL ) ;
if ( ! peerpcall ) {
ftdm_log ( FTDM_LOG_ERROR , " Failed to get a free peer PRI passive call slot for callref %d in span %s, this is a bug! \n " ,
crv , pritap - > span - > name ) ;
break ;
}
peerpcall - > callref = e - > proceeding . call ;
/* check that the layer 1 and trans capability are supported */
layer1 = pri_get_layer1 ( peertap - > pri , pcall - > callref ) ;
transcap = pri_get_transcap ( peertap - > pri , pcall - > callref ) ;
if ( PRI_LAYER_1_ULAW ! = layer1 & & PRI_LAYER_1_ALAW ! = layer1 ) {
ftdm_log ( FTDM_LOG_NOTICE , " Not monitoring callref %d with unsupported layer 1 format %d \n " , crv , layer1 ) ;
break ;
}
if ( transcap ! = PRI_TRANS_CAP_SPEECH & & transcap ! = PRI_TRANS_CAP_3_1K_AUDIO & & transcap ! = PRI_TRANS_CAP_7K_AUDIO ) {
ftdm_log ( FTDM_LOG_NOTICE , " Not monitoring callref %d with unsupported capability %d \n " , crv , transcap ) ;
break ;
}
fchan = tap_pri_get_fchan ( pritap , pcall , e - > proceeding . channel ) ;
if ( ! fchan ) {
ftdm_log ( FTDM_LOG_ERROR , " Proceeding requested on odd/unavailable channel %s:%d:%d for callref %d \n " ,
pritap - > span - > name , PRI_SPAN ( e - > proceeding . channel ) , PRI_CHANNEL ( e - > proceeding . channel ) , crv ) ;
break ;
}
pcall - > fchan = fchan ;
peerfchan = tap_pri_get_fchan ( peertap , pcall , e - > proceeding . channel ) ;
if ( ! peerfchan ) {
ftdm_log ( FTDM_LOG_ERROR , " Proceeding requested on odd/unavailable channel %s:%d:%d for callref %d \n " ,
peertap - > span - > name , PRI_SPAN ( e - > proceeding . channel ) , PRI_CHANNEL ( e - > proceeding . channel ) , crv ) ;
break ;
}
peerpcall - > fchan = fchan ;
fchan - > call_data = peerfchan ;
peerfchan - > call_data = fchan ;
ftdm_set_state_locked ( fchan , FTDM_CHANNEL_STATE_RING ) ;
2010-07-30 19:46:05 -04:00
break ;
case PRI_EVENT_ANSWER :
2010-07-31 17:33:20 -04:00
crv = tap_pri_get_crv ( pritap - > pri , e - > answer . call ) ;
ftdm_log ( FTDM_LOG_DEBUG , " Answer on channel %s:%d:%d with callref %d \n " ,
pritap - > span - > name , PRI_SPAN ( e - > answer . channel ) , PRI_CHANNEL ( e - > answer . channel ) , crv ) ;
if ( ! ( pcall = tap_pri_get_pcall_bycrv ( pritap , crv ) ) ) {
ftdm_log ( FTDM_LOG_DEBUG ,
" ignoring answer in channel %s:%d:%d for callref %d since we don't know about it " ,
pritap - > span - > name , PRI_SPAN ( e - > proceeding . channel ) , PRI_CHANNEL ( e - > proceeding . channel ) , crv ) ;
break ;
}
ftdm_log_chan ( pcall - > fchan , FTDM_LOG_NOTICE , " Tapped call was answered in state %s \n " , ftdm_channel_state2str ( pcall - > fchan - > state ) ) ;
2010-07-30 19:46:05 -04:00
break ;
2010-07-31 17:33:20 -04:00
case PRI_EVENT_HANGUP_REQ :
crv = tap_pri_get_crv ( pritap - > pri , e - > hangup . call ) ;
ftdm_log ( FTDM_LOG_DEBUG , " Hangup on channel %s:%d:%d with callref %d \n " ,
pritap - > span - > name , PRI_SPAN ( e - > answer . channel ) , PRI_CHANNEL ( e - > answer . channel ) , crv ) ;
if ( ! ( pcall = tap_pri_get_pcall_bycrv ( pritap , crv ) ) ) {
ftdm_log ( FTDM_LOG_DEBUG ,
" ignoring hangup in channel %s:%d:%d for callref %d since we don't know about it " ,
pritap - > span - > name , PRI_SPAN ( e - > proceeding . channel ) , PRI_CHANNEL ( e - > proceeding . channel ) , crv ) ;
break ;
}
fchan = pcall - > fchan ;
ftdm_set_state_locked ( fchan , FTDM_CHANNEL_STATE_TERMINATING ) ;
2010-07-30 19:46:05 -04:00
break ;
case PRI_EVENT_HANGUP_ACK :
2010-07-31 17:33:20 -04:00
crv = tap_pri_get_crv ( pritap - > pri , e - > hangup . call ) ;
ftdm_log ( FTDM_LOG_DEBUG , " Hangup ack on channel %s:%d:%d with callref %d \n " ,
pritap - > span - > name , PRI_SPAN ( e - > answer . channel ) , PRI_CHANNEL ( e - > answer . channel ) , crv ) ;
tap_pri_put_pcall ( pritap , e - > hangup . call ) ;
tap_pri_put_pcall ( peertap , e - > hangup . call ) ;
2010-07-30 19:46:05 -04:00
break ;
default :
ftdm_log ( FTDM_LOG_DEBUG , " Ignoring passive event %s on span %s \n " , pri_event2str ( e - > gen . e ) , pritap - > span - > name ) ;
break ;
}
}
static void * ftdm_pritap_run ( ftdm_thread_t * me , void * obj )
{
ftdm_span_t * span = ( ftdm_span_t * ) obj ;
pritap_t * pritap = span - > signal_data ;
pri_event * event = NULL ;
struct pollfd dpoll = { 0 , 0 , 0 } ;
int rc = 0 ;
ftdm_log ( FTDM_LOG_DEBUG , " Tapping PRI thread started on span %d \n " , span - > span_id ) ;
pritap - > span = span ;
ftdm_set_flag ( span , FTDM_SPAN_IN_THREAD ) ;
if ( ftdm_channel_open ( span - > span_id , pritap - > dchan - > chan_id , & pritap - > dchan ) ! = FTDM_SUCCESS ) {
ftdm_log ( FTDM_LOG_ERROR , " Failed to open D-channel for span %s \n " , span - > name ) ;
goto done ;
}
if ( ( pritap - > pri = pri_new_cb ( pritap - > dchan - > sockfd , PRI_NETWORK , PRI_SWITCH_NI2 , pri_io_read , pri_io_write , pritap ) ) ) {
pri_set_debug ( pritap - > pri , pritap - > debug ) ;
} else {
ftdm_log ( FTDM_LOG_CRIT , " Failed to create tapping PRI \n " ) ;
goto done ;
}
dpoll . fd = pritap - > dchan - > sockfd ;
while ( ftdm_running ( ) & & ! ftdm_test_flag ( span , FTDM_SPAN_STOP_THREAD ) ) {
pritap_check_state ( span ) ;
dpoll . revents = 0 ;
dpoll . events = POLLIN ;
rc = poll ( & dpoll , 1 , 10 ) ;
if ( rc < 0 ) {
if ( errno = = EINTR ) {
ftdm_log ( FTDM_LOG_DEBUG , " D-channel waiting interrupted, continuing ... \n " ) ;
continue ;
}
ftdm_log ( FTDM_LOG_ERROR , " poll failed: %s \n " , strerror ( errno ) ) ;
continue ;
}
pri_schedule_run ( pritap - > pri ) ;
if ( rc ) {
if ( dpoll . revents & POLLIN ) {
event = pri_read_event ( pritap - > pri ) ;
if ( event ) {
handle_pri_passive_event ( pritap , event ) ;
}
} else {
ftdm_log ( FTDM_LOG_WARNING , " nothing to read? \n " ) ;
}
}
pritap_check_state ( span ) ;
}
done :
ftdm_log ( FTDM_LOG_DEBUG , " Tapping PRI thread ended on span %d \n " , span - > span_id ) ;
ftdm_clear_flag ( span , FTDM_SPAN_IN_THREAD ) ;
ftdm_clear_flag ( pritap , PRITAP_RUNNING ) ;
return NULL ;
}
static ftdm_status_t ftdm_pritap_stop ( ftdm_span_t * span )
{
pritap_t * pritap = span - > signal_data ;
if ( ! ftdm_test_flag ( pritap , PRITAP_RUNNING ) ) {
return FTDM_FAIL ;
}
ftdm_set_flag ( span , FTDM_SPAN_STOP_THREAD ) ;
while ( ftdm_test_flag ( span , FTDM_SPAN_IN_THREAD ) ) {
ftdm_sleep ( 100 ) ;
}
2010-07-31 17:33:20 -04:00
ftdm_mutex_destroy ( & pritap - > pcalls_lock ) ;
2010-07-30 19:46:05 -04:00
return FTDM_SUCCESS ;
}
2010-08-01 03:02:53 -04:00
static ftdm_status_t ftdm_pritap_sig_read ( ftdm_channel_t * ftdmchan , void * data , ftdm_size_t size )
{
ftdm_status_t status ;
fio_codec_t codec_func ;
ftdm_channel_t * peerchan = ftdmchan - > call_data ;
int16_t chanbuf [ size ] ;
2010-08-01 04:31:06 -04:00
int16_t peerbuf [ size ] ;
2010-08-01 03:02:53 -04:00
int16_t mixedbuf [ size ] ;
int i = 0 ;
ftdm_size_t sizeread = size ;
if ( ! FTDM_IS_VOICE_CHANNEL ( ftdmchan ) | | ! ftdmchan - > call_data ) {
return FTDM_SUCCESS ;
}
if ( ftdmchan - > native_codec ! = peerchan - > native_codec ) {
ftdm_log_chan ( ftdmchan , FTDM_LOG_CRIT , " Invalid peer channel with format %d, ours = %d \n " ,
peerchan - > native_codec , ftdmchan - > native_codec ) ;
return FTDM_FAIL ;
}
memcpy ( chanbuf , data , size ) ;
2010-08-01 04:31:06 -04:00
status = peerchan - > fio - > read ( peerchan , peerbuf , & sizeread ) ;
2010-08-01 03:02:53 -04:00
if ( status ! = FTDM_SUCCESS ) {
ftdm_log_chan_msg ( ftdmchan , FTDM_LOG_ERROR , " Failed to read from peer channel! \n " ) ;
return FTDM_FAIL ;
}
if ( sizeread ! = size ) {
ftdm_log_chan ( ftdmchan , FTDM_LOG_ERROR , " read from peer channel only %d bytes! \n " , sizeread ) ;
return FTDM_FAIL ;
}
codec_func = peerchan - > native_codec = = FTDM_CODEC_ULAW ? fio_ulaw2slin : peerchan - > native_codec = = FTDM_CODEC_ALAW ? fio_alaw2slin : NULL ;
if ( codec_func ) {
sizeread = size ;
codec_func ( chanbuf , sizeof ( chanbuf ) , & sizeread ) ;
2010-08-01 04:31:06 -04:00
sizeread = size ;
codec_func ( peerbuf , sizeof ( peerbuf ) , & sizeread ) ;
2010-08-01 03:02:53 -04:00
}
for ( i = 0 ; i < size ; i + + ) {
mixedbuf [ i ] = ftdm_saturated_add ( chanbuf [ i ] , peerbuf [ i ] ) ;
}
codec_func = peerchan - > native_codec = = FTDM_CODEC_ULAW ? fio_slin2ulaw : peerchan - > native_codec = = FTDM_CODEC_ALAW ? fio_slin2alaw : NULL ;
if ( codec_func ) {
2010-08-01 04:31:06 -04:00
size = sizeof ( mixedbuf ) ;
codec_func ( mixedbuf , size , & size ) ;
2010-08-01 03:02:53 -04:00
}
2010-08-01 04:31:06 -04:00
memcpy ( data , mixedbuf , size ) ;
2010-08-01 03:02:53 -04:00
return FTDM_SUCCESS ;
}
2010-07-30 19:46:05 -04:00
static ftdm_status_t ftdm_pritap_start ( ftdm_span_t * span )
{
ftdm_status_t ret ;
pritap_t * pritap = span - > signal_data ;
if ( ftdm_test_flag ( pritap , PRITAP_RUNNING ) ) {
return FTDM_FAIL ;
}
2010-07-31 17:33:20 -04:00
ftdm_mutex_create ( & pritap - > pcalls_lock ) ;
2010-07-30 19:46:05 -04:00
ftdm_clear_flag ( span , FTDM_SPAN_STOP_THREAD ) ;
ftdm_clear_flag ( span , FTDM_SPAN_IN_THREAD ) ;
ftdm_set_flag ( pritap , PRITAP_RUNNING ) ;
ret = ftdm_thread_create_detached ( ftdm_pritap_run , span ) ;
if ( ret ! = FTDM_SUCCESS ) {
return ret ;
}
return ret ;
}
static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION ( ftdm_pritap_configure_span )
{
uint32_t i ;
const char * var , * val ;
const char * debug = NULL ;
ftdm_channel_t * dchan = NULL ;
pritap_t * pritap = NULL ;
ftdm_span_t * peerspan = NULL ;
unsigned paramindex = 0 ;
if ( span - > trunk_type > = FTDM_TRUNK_NONE ) {
ftdm_log ( FTDM_LOG_WARNING , " Invalid trunk type '%s' defaulting to T1. \n " , ftdm_trunk_type2str ( span - > trunk_type ) ) ;
span - > trunk_type = FTDM_TRUNK_T1 ;
}
for ( i = 1 ; i < = span - > chan_count ; i + + ) {
if ( span - > channels [ i ] - > type = = FTDM_CHAN_TYPE_DQ921 ) {
dchan = span - > channels [ i ] ;
}
}
if ( ! dchan ) {
ftdm_log ( FTDM_LOG_ERROR , " No d-channel specified in freetdm.conf! \n " , ftdm_trunk_type2str ( span - > trunk_type ) ) ;
return FTDM_FAIL ;
}
for ( paramindex = 0 ; ftdm_parameters [ paramindex ] . var ; paramindex + + ) {
var = ftdm_parameters [ paramindex ] . var ;
val = ftdm_parameters [ paramindex ] . val ;
ftdm_log ( FTDM_LOG_DEBUG , " Tapping PRI key=value, %s=%s \n " , var , val ) ;
if ( ! strcasecmp ( var , " debug " ) ) {
debug = val ;
} else if ( ! strcasecmp ( var , " peerspan " ) ) {
if ( ftdm_span_find_by_name ( val , & peerspan ) ! = FTDM_SUCCESS ) {
ftdm_log ( FTDM_LOG_ERROR , " Invalid tapping peer span %s \n " , val ) ;
break ;
}
} else {
ftdm_log ( FTDM_LOG_ERROR , " Unknown pri tapping parameter [%s] " , var ) ;
}
}
if ( ! peerspan ) {
ftdm_log ( FTDM_LOG_ERROR , " No valid peerspan was specified! \n " ) ;
return FTDM_FAIL ;
}
pritap = ftdm_calloc ( 1 , sizeof ( * pritap ) ) ;
if ( ! pritap ) {
return FTDM_FAIL ;
}
pritap - > debug = parse_debug ( debug ) ;
pritap - > dchan = dchan ;
pritap - > peerspan = peerspan ;
span - > start = ftdm_pritap_start ;
span - > stop = ftdm_pritap_stop ;
2010-08-01 03:02:53 -04:00
span - > sig_read = ftdm_pritap_sig_read ;
2010-07-30 19:46:05 -04:00
span - > signal_cb = sig_cb ;
span - > signal_data = pritap ;
span - > signal_type = FTDM_SIGTYPE_ISDN ;
span - > outgoing_call = pritap_outgoing_call ;
span - > get_channel_sig_status = pritap_get_channel_sig_status ;
span - > get_span_sig_status = pritap_get_span_sig_status ;
span - > state_map = & pritap_state_map ;
return FTDM_SUCCESS ;
}
/**
* \ brief FreeTDM pritap signaling and IO module definition
*/
ftdm_module_t ftdm_module = {
" pritap " ,
ftdm_pritap_io_init ,
ftdm_pritap_unload ,
ftdm_pritap_init ,
NULL ,
NULL ,
ftdm_pritap_configure_span ,
} ;
/* For Emacs:
* Local Variables :
* mode : c
* indent - tabs - mode : t
* tab - width : 4
* c - basic - offset : 4
* End :
* For VIM :
* vim : set softtabstop = 4 shiftwidth = 4 tabstop = 4 :
*/