freetdm: Added Analog E&M outbound call answer supervision

You must add answer-supervision=yes in your freetdm.conf.xml
         Also added dial-timeout parameter which was previously hard-coded
This commit is contained in:
Moises Silva 2012-12-18 22:50:49 -05:00
parent 59f44b6055
commit 2b4aa48049
5 changed files with 88 additions and 13 deletions

View File

@ -3775,11 +3775,14 @@ static switch_status_t load_config(void)
const char *dialplan = "XML"; const char *dialplan = "XML";
const char *tonegroup = NULL; const char *tonegroup = NULL;
char *digit_timeout = NULL; char *digit_timeout = NULL;
char *dial_timeout = NULL;
char *max_digits = NULL; char *max_digits = NULL;
char *dial_regex = NULL; char *dial_regex = NULL;
char *hold_music = NULL; char *hold_music = NULL;
char *fail_dial_regex = NULL; char *fail_dial_regex = NULL;
uint32_t span_id = 0, to = 0, max = 0; char str_false[] = "false";
char *answer_supervision = str_false;
uint32_t span_id = 0, to = 0, max = 0, dial_timeout_int = 0;
ftdm_span_t *span = NULL; ftdm_span_t *span = NULL;
analog_option_t analog_options = ANALOG_OPTION_NONE; analog_option_t analog_options = ANALOG_OPTION_NONE;
@ -3791,6 +3794,8 @@ static switch_status_t load_config(void)
tonegroup = val; tonegroup = val;
} else if (!strcasecmp(var, "digit_timeout") || !strcasecmp(var, "digit-timeout")) { } else if (!strcasecmp(var, "digit_timeout") || !strcasecmp(var, "digit-timeout")) {
digit_timeout = val; digit_timeout = val;
} else if (!strcasecmp(var, "dial-timeout")) {
dial_timeout = val;
} else if (!strcasecmp(var, "context")) { } else if (!strcasecmp(var, "context")) {
context = val; context = val;
} else if (!strcasecmp(var, "dialplan")) { } else if (!strcasecmp(var, "dialplan")) {
@ -3803,6 +3808,8 @@ static switch_status_t load_config(void)
hold_music = val; hold_music = val;
} else if (!strcasecmp(var, "max_digits") || !strcasecmp(var, "max-digits")) { } else if (!strcasecmp(var, "max_digits") || !strcasecmp(var, "max-digits")) {
max_digits = val; max_digits = val;
} else if (!strcasecmp(var, "answer-supervision")) {
answer_supervision = val;
} else if (!strcasecmp(var, "enable-analog-option")) { } else if (!strcasecmp(var, "enable-analog-option")) {
analog_options = enable_analog_option(val, analog_options); analog_options = enable_analog_option(val, analog_options);
} }
@ -3821,6 +3828,10 @@ static switch_status_t load_config(void)
to = atoi(digit_timeout); to = atoi(digit_timeout);
} }
if (dial_timeout) {
dial_timeout_int = atoi(dial_timeout);
}
if (max_digits) { if (max_digits) {
max = atoi(max_digits); max = atoi(max_digits);
} }
@ -3851,7 +3862,9 @@ static switch_status_t load_config(void)
if (ftdm_configure_span(span, "analog_em", on_analog_signal, if (ftdm_configure_span(span, "analog_em", on_analog_signal,
"tonemap", tonegroup, "tonemap", tonegroup,
"answer_supervision", answer_supervision,
"digit_timeout", &to, "digit_timeout", &to,
"dial_timeout", &dial_timeout_int,
"max_dialstr", &max, "max_dialstr", &max,
FTDM_TAG_END) != FTDM_SUCCESS) { FTDM_TAG_END) != FTDM_SUCCESS) {
LOAD_ERROR("Error starting FreeTDM span %d\n", span_id); LOAD_ERROR("Error starting FreeTDM span %d\n", span_id);

View File

@ -51,6 +51,8 @@ struct ftdm_analog_data {
uint32_t flags; uint32_t flags;
uint32_t max_dialstr; uint32_t max_dialstr;
uint32_t digit_timeout; uint32_t digit_timeout;
uint32_t dial_timeout;
ftdm_bool_t answer_supervision;
}; };
static void *ftdm_analog_em_run(ftdm_thread_t *me, void *obj); static void *ftdm_analog_em_run(ftdm_thread_t *me, void *obj);

View File

@ -120,8 +120,10 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_em_configure_span)
{ {
ftdm_analog_em_data_t *analog_data; ftdm_analog_em_data_t *analog_data;
const char *tonemap = "us"; const char *tonemap = "us";
uint32_t digit_timeout = 10; uint32_t digit_timeout = 2000;
uint32_t max_dialstr = 11; uint32_t max_dialstr = 11;
uint32_t dial_timeout = 0;
ftdm_bool_t answer_supervision = FTDM_FALSE;
const char *var, *val; const char *var, *val;
int *intval; int *intval;
@ -137,11 +139,22 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_em_configure_span)
memset(analog_data, 0, sizeof(*analog_data)); memset(analog_data, 0, sizeof(*analog_data));
while((var = va_arg(ap, char *))) { while((var = va_arg(ap, char *))) {
ftdm_log(FTDM_LOG_DEBUG, "Parsing analog em parameter '%s'\n", var);
if (!strcasecmp(var, "tonemap")) { if (!strcasecmp(var, "tonemap")) {
if (!(val = va_arg(ap, char *))) { if (!(val = va_arg(ap, char *))) {
break; break;
} }
tonemap = val; tonemap = val;
} else if (!strcasecmp(var, "answer_supervision")) {
if (!(val = va_arg(ap, char *))) {
break;
}
answer_supervision = ftdm_true(val);
} else if (!strcasecmp(var, "dial_timeout")) {
if (!(intval = va_arg(ap, int *))) {
break;
}
dial_timeout = *intval;
} else if (!strcasecmp(var, "digit_timeout")) { } else if (!strcasecmp(var, "digit_timeout")) {
if (!(intval = va_arg(ap, int *))) { if (!(intval = va_arg(ap, int *))) {
break; break;
@ -153,7 +166,7 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_em_configure_span)
} }
max_dialstr = *intval; max_dialstr = *intval;
} else { } else {
snprintf(span->last_error, sizeof(span->last_error), "Unknown parameter [%s]", var); ftdm_log(FTDM_LOG_ERROR, "Invalid parameter for analog em span: '%s'\n", var);
return FTDM_FAIL; return FTDM_FAIL;
} }
} }
@ -171,6 +184,8 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_em_configure_span)
span->start = ftdm_analog_em_start; span->start = ftdm_analog_em_start;
analog_data->digit_timeout = digit_timeout; analog_data->digit_timeout = digit_timeout;
analog_data->max_dialstr = max_dialstr; analog_data->max_dialstr = max_dialstr;
analog_data->dial_timeout = dial_timeout;
analog_data->answer_supervision = answer_supervision;
span->signal_cb = sig_cb; span->signal_cb = sig_cb;
span->signal_type = FTDM_SIGTYPE_ANALOG; span->signal_type = FTDM_SIGTYPE_ANALOG;
span->signal_data = analog_data; span->signal_data = analog_data;
@ -221,6 +236,9 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj)
ftdm_channel_t *closed_chan; ftdm_channel_t *closed_chan;
uint32_t state_counter = 0, elapsed = 0, collecting = 0, interval = 0, last_digit = 0, indicate = 0, dial_timeout = 30000; uint32_t state_counter = 0, elapsed = 0, collecting = 0, interval = 0, last_digit = 0, indicate = 0, dial_timeout = 30000;
ftdm_sigmsg_t sig; ftdm_sigmsg_t sig;
int cas_bits = 0;
uint32_t cas_answer = 0;
int cas_answer_ms = 500;
ftdm_log(FTDM_LOG_DEBUG, "ANALOG EM CHANNEL thread starting.\n"); ftdm_log(FTDM_LOG_DEBUG, "ANALOG EM CHANNEL thread starting.\n");
@ -259,6 +277,7 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj)
sig.channel = ftdmchan; sig.channel = ftdmchan;
assert(interval != 0); assert(interval != 0);
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "IO Interval: %u\n", interval);
while (ftdm_running() && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INTHREAD)) { while (ftdm_running() && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INTHREAD)) {
ftdm_wait_flag_t flags = FTDM_READ; ftdm_wait_flag_t flags = FTDM_READ;
@ -288,6 +307,11 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj)
ftdmchan->needed_tones[FTDM_TONEMAP_FAIL2] = 1; ftdmchan->needed_tones[FTDM_TONEMAP_FAIL2] = 1;
ftdmchan->needed_tones[FTDM_TONEMAP_FAIL3] = 1; ftdmchan->needed_tones[FTDM_TONEMAP_FAIL3] = 1;
dial_timeout = ((ftdmchan->dtmf_on + ftdmchan->dtmf_off) * strlen(ftdmchan->caller_data.dnis.digits)) + 2000; dial_timeout = ((ftdmchan->dtmf_on + ftdmchan->dtmf_off) * strlen(ftdmchan->caller_data.dnis.digits)) + 2000;
if (analog_data->dial_timeout) {
dial_timeout += analog_data->dial_timeout;
}
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Outbound dialing timeout: %dms\n", dial_timeout);
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Outbound CAS answer timeout: %dms\n", cas_answer_ms);
} }
} }
break; break;
@ -295,10 +319,25 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj)
if (state_counter > dial_timeout) { if (state_counter > dial_timeout) {
if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_WINK)) { if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_WINK)) {
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_BUSY); ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_BUSY);
} else { } else if (!analog_data->answer_supervision) {
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP); ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP);
} }
} }
cas_bits = 0;
ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_CAS_BITS, &cas_bits);
if (!(state_counter % 1000)) {
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "CAS bits: 0x%X\n", cas_bits);
}
if (cas_bits == 0xF) {
cas_answer += interval;
if (cas_answer >= cas_answer_ms) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Answering on CAS answer signal persistence!\n");
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP);
}
} else if (cas_answer) {
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Resetting cas answer to 0: 0x%X!\n", cas_bits);
cas_answer = 0;
}
} }
break; break;
case FTDM_CHANNEL_STATE_DIALTONE: case FTDM_CHANNEL_STATE_DIALTONE:
@ -515,7 +554,11 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj)
ftdm_log(FTDM_LOG_ERROR, "Failure indication detected!\n"); ftdm_log(FTDM_LOG_ERROR, "Failure indication detected!\n");
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_BUSY); ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_BUSY);
} else if (ftdmchan->detected_tones[FTDM_TONEMAP_RING]) { } else if (ftdmchan->detected_tones[FTDM_TONEMAP_RING]) {
if (!analog_data->answer_supervision) {
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP); ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP);
} else {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ringing, but not answering since answer supervision is enabled\n");
}
} }
ftdm_channel_clear_detected_tones(ftdmchan); ftdm_channel_clear_detected_tones(ftdmchan);
@ -617,7 +660,9 @@ static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *e
case FTDM_OOB_OFFHOOK: case FTDM_OOB_OFFHOOK:
{ {
if (ftdm_test_flag(event->channel, FTDM_CHANNEL_INTHREAD)) { if (ftdm_test_flag(event->channel, FTDM_CHANNEL_INTHREAD)) {
if (event->channel->state < FTDM_CHANNEL_STATE_UP) {
ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_UP); ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_UP);
}
} else { } else {
ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_DIALTONE); ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_DIALTONE);
ftdm_mutex_unlock(event->channel->mutex); ftdm_mutex_unlock(event->channel->mutex);

View File

@ -1098,6 +1098,7 @@ static __inline__ int handle_dtmf_event(ftdm_channel_t *fchan, zt_event_t zt_eve
*/ */
static __inline__ ftdm_status_t zt_channel_process_event(ftdm_channel_t *fchan, ftdm_oob_event_t *event_id, zt_event_t zt_event_id) static __inline__ ftdm_status_t zt_channel_process_event(ftdm_channel_t *fchan, ftdm_oob_event_t *event_id, zt_event_t zt_event_id)
{ {
ftdm_log_chan(fchan, FTDM_LOG_DEBUG, "Processing zap hardware event %d\n", zt_event_id);
switch(zt_event_id) { switch(zt_event_id) {
case ZT_EVENT_RINGEROFF: case ZT_EVENT_RINGEROFF:
{ {
@ -1132,16 +1133,30 @@ static __inline__ ftdm_status_t zt_channel_process_event(ftdm_channel_t *fchan,
break; break;
case ZT_EVENT_RINGOFFHOOK: case ZT_EVENT_RINGOFFHOOK:
{ {
*event_id = FTDM_OOB_NOOP;
if (fchan->type == FTDM_CHAN_TYPE_FXS || (fchan->type == FTDM_CHAN_TYPE_EM && fchan->state != FTDM_CHANNEL_STATE_UP)) { if (fchan->type == FTDM_CHAN_TYPE_FXS || (fchan->type == FTDM_CHAN_TYPE_EM && fchan->state != FTDM_CHANNEL_STATE_UP)) {
if (fchan->type != FTDM_CHAN_TYPE_EM) { if (fchan->type != FTDM_CHAN_TYPE_EM) {
/* In E&M we're supposed to set this flag when the tx side goes offhook, not the rx */ /* In E&M we're supposed to set this flag only when the local side goes offhook, not the remote */
ftdm_set_flag_locked(fchan, FTDM_CHANNEL_OFFHOOK); ftdm_set_flag_locked(fchan, FTDM_CHANNEL_OFFHOOK);
} }
/* For E&M let's count the ring count (it seems sometimes we receive RINGOFFHOOK once before the other end
* answers, then another RINGOFFHOOK when the other end answers?? anyways, now we count rings before delivering the
* offhook event ... the E&M signaling code in ftmod_analog_em also polls the RBS bits looking for answer, just to
* be safe and not rely on this event, so even if this event does not arrive, when there is answer supervision
* the analog signaling code should detect the cas persistance pattern and answer */
if (fchan->type == FTDM_CHAN_TYPE_EM && ftdm_test_flag(fchan, FTDM_CHANNEL_OUTBOUND)) {
fchan->ring_count++;
/* perhaps some day we'll make this configurable, but since I am not even sure what the hell is going on
* no point in making a configuration option for something that may not be technically correct */
if (fchan->ring_count == 2) {
*event_id = FTDM_OOB_OFFHOOK; *event_id = FTDM_OOB_OFFHOOK;
}
} else {
*event_id = FTDM_OOB_OFFHOOK;
}
} else if (fchan->type == FTDM_CHAN_TYPE_FXO) { } else if (fchan->type == FTDM_CHAN_TYPE_FXO) {
*event_id = FTDM_OOB_RING_START; *event_id = FTDM_OOB_RING_START;
} else {
*event_id = FTDM_OOB_NOOP;
} }
} }
break; break;

View File

@ -92,7 +92,7 @@
!strcasecmp(expr, "true") || \ !strcasecmp(expr, "true") || \
!strcasecmp(expr, "enabled") || \ !strcasecmp(expr, "enabled") || \
!strcasecmp(expr, "active") || \ !strcasecmp(expr, "active") || \
atoi(expr))) ? 1 : 0 atoi(expr))) ? FTDM_TRUE : FTDM_FALSE
#ifdef WIN32_LEAN_AND_MEAN #ifdef WIN32_LEAN_AND_MEAN
#include <winsock2.h> #include <winsock2.h>