From 2b4aa480490738cd5458e9c0563a6ea9b2f17829 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Tue, 18 Dec 2012 22:50:49 -0500 Subject: [PATCH] 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 --- libs/freetdm/mod_freetdm/mod_freetdm.c | 15 ++++- .../ftmod/ftmod_analog_em/ftdm_analog_em.h | 2 + .../ftmod/ftmod_analog_em/ftmod_analog_em.c | 59 ++++++++++++++++--- libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c | 23 ++++++-- libs/freetdm/src/include/private/ftdm_core.h | 2 +- 5 files changed, 88 insertions(+), 13 deletions(-) diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c index 7ed8be290a..514fbcf527 100755 --- a/libs/freetdm/mod_freetdm/mod_freetdm.c +++ b/libs/freetdm/mod_freetdm/mod_freetdm.c @@ -3775,11 +3775,14 @@ static switch_status_t load_config(void) const char *dialplan = "XML"; const char *tonegroup = NULL; char *digit_timeout = NULL; + char *dial_timeout = NULL; char *max_digits = NULL; char *dial_regex = NULL; char *hold_music = 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; analog_option_t analog_options = ANALOG_OPTION_NONE; @@ -3791,6 +3794,8 @@ static switch_status_t load_config(void) tonegroup = val; } else if (!strcasecmp(var, "digit_timeout") || !strcasecmp(var, "digit-timeout")) { digit_timeout = val; + } else if (!strcasecmp(var, "dial-timeout")) { + dial_timeout = val; } else if (!strcasecmp(var, "context")) { context = val; } else if (!strcasecmp(var, "dialplan")) { @@ -3803,6 +3808,8 @@ static switch_status_t load_config(void) hold_music = val; } else if (!strcasecmp(var, "max_digits") || !strcasecmp(var, "max-digits")) { max_digits = val; + } else if (!strcasecmp(var, "answer-supervision")) { + answer_supervision = val; } else if (!strcasecmp(var, "enable-analog-option")) { analog_options = enable_analog_option(val, analog_options); } @@ -3821,6 +3828,10 @@ static switch_status_t load_config(void) to = atoi(digit_timeout); } + if (dial_timeout) { + dial_timeout_int = atoi(dial_timeout); + } + if (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, "tonemap", tonegroup, + "answer_supervision", answer_supervision, "digit_timeout", &to, + "dial_timeout", &dial_timeout_int, "max_dialstr", &max, FTDM_TAG_END) != FTDM_SUCCESS) { LOAD_ERROR("Error starting FreeTDM span %d\n", span_id); diff --git a/libs/freetdm/src/ftmod/ftmod_analog_em/ftdm_analog_em.h b/libs/freetdm/src/ftmod/ftmod_analog_em/ftdm_analog_em.h index 22a1c25132..30c4877d26 100644 --- a/libs/freetdm/src/ftmod/ftmod_analog_em/ftdm_analog_em.h +++ b/libs/freetdm/src/ftmod/ftmod_analog_em/ftdm_analog_em.h @@ -51,6 +51,8 @@ struct ftdm_analog_data { uint32_t flags; uint32_t max_dialstr; 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); diff --git a/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c b/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c index 6888cbf8c5..c073c5364e 100644 --- a/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c +++ b/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c @@ -120,8 +120,10 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_em_configure_span) { ftdm_analog_em_data_t *analog_data; const char *tonemap = "us"; - uint32_t digit_timeout = 10; + uint32_t digit_timeout = 2000; uint32_t max_dialstr = 11; + uint32_t dial_timeout = 0; + ftdm_bool_t answer_supervision = FTDM_FALSE; const char *var, *val; int *intval; @@ -137,11 +139,22 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_em_configure_span) memset(analog_data, 0, sizeof(*analog_data)); while((var = va_arg(ap, char *))) { + ftdm_log(FTDM_LOG_DEBUG, "Parsing analog em parameter '%s'\n", var); if (!strcasecmp(var, "tonemap")) { if (!(val = va_arg(ap, char *))) { break; } 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")) { if (!(intval = va_arg(ap, int *))) { break; @@ -153,7 +166,7 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_em_configure_span) } max_dialstr = *intval; } 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; } } @@ -171,6 +184,8 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_em_configure_span) span->start = ftdm_analog_em_start; analog_data->digit_timeout = digit_timeout; 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_type = FTDM_SIGTYPE_ANALOG; 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; uint32_t state_counter = 0, elapsed = 0, collecting = 0, interval = 0, last_digit = 0, indicate = 0, dial_timeout = 30000; 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"); @@ -259,6 +277,7 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj) sig.channel = ftdmchan; 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)) { ftdm_wait_flag_t flags = FTDM_READ; @@ -266,7 +285,7 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj) elapsed += interval; state_counter += interval; - + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) { switch(ftdmchan->state) { case FTDM_CHANNEL_STATE_DIALING: @@ -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_FAIL3] = 1; 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; @@ -295,10 +319,25 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj) if (state_counter > dial_timeout) { if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_WINK)) { 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); } - } + } + 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; 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_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_BUSY); } else if (ftdmchan->detected_tones[FTDM_TONEMAP_RING]) { - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP); + if (!analog_data->answer_supervision) { + 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); @@ -617,7 +660,9 @@ static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *e case FTDM_OOB_OFFHOOK: { if (ftdm_test_flag(event->channel, FTDM_CHANNEL_INTHREAD)) { - ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_UP); + if (event->channel->state < FTDM_CHANNEL_STATE_UP) { + ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_UP); + } } else { ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_DIALTONE); ftdm_mutex_unlock(event->channel->mutex); diff --git a/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c b/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c index dc95c9ac08..c937c1bcd2 100644 --- a/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c +++ b/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c @@ -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) { + ftdm_log_chan(fchan, FTDM_LOG_DEBUG, "Processing zap hardware event %d\n", zt_event_id); switch(zt_event_id) { case ZT_EVENT_RINGEROFF: { @@ -1132,16 +1133,30 @@ static __inline__ ftdm_status_t zt_channel_process_event(ftdm_channel_t *fchan, break; 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_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); } - *event_id = FTDM_OOB_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; + } + } else { + *event_id = FTDM_OOB_OFFHOOK; + } } else if (fchan->type == FTDM_CHAN_TYPE_FXO) { *event_id = FTDM_OOB_RING_START; - } else { - *event_id = FTDM_OOB_NOOP; } } break; diff --git a/libs/freetdm/src/include/private/ftdm_core.h b/libs/freetdm/src/include/private/ftdm_core.h index e378001183..728390dd54 100644 --- a/libs/freetdm/src/include/private/ftdm_core.h +++ b/libs/freetdm/src/include/private/ftdm_core.h @@ -92,7 +92,7 @@ !strcasecmp(expr, "true") || \ !strcasecmp(expr, "enabled") || \ !strcasecmp(expr, "active") || \ - atoi(expr))) ? 1 : 0 + atoi(expr))) ? FTDM_TRUE : FTDM_FALSE #ifdef WIN32_LEAN_AND_MEAN #include