From 3479ce917e108c9639e06294fdaec9f99941a428 Mon Sep 17 00:00:00 2001 From: Michael Jerris Date: Wed, 17 Sep 2008 02:21:28 +0000 Subject: [PATCH] (OPENZAP-7) inital checkin of E&M support from John Wehle git-svn-id: http://svn.openzap.org/svn/openzap/trunk@561 a93c3328-9c30-0410-af19-c9cd2b2d52af --- libs/freetdm/Makefile.in | 6 +- libs/freetdm/mod_openzap/mod_openzap.c | 97 +++ libs/freetdm/src/include/zap_types.h | 9 +- .../ozmod/ozmod_analog_em/ozmod_analog_em.c | 663 ++++++++++++++++++ .../src/ozmod/ozmod_analog_em/zap_analog_em.h | 61 ++ libs/freetdm/src/ozmod/ozmod_zt/ozmod_zt.c | 8 +- libs/freetdm/src/zap_io.c | 13 +- 7 files changed, 848 insertions(+), 9 deletions(-) create mode 100644 libs/freetdm/src/ozmod/ozmod_analog_em/ozmod_analog_em.c create mode 100644 libs/freetdm/src/ozmod/ozmod_analog_em/zap_analog_em.h diff --git a/libs/freetdm/Makefile.in b/libs/freetdm/Makefile.in index 73481bf8c0..8c69bafe12 100644 --- a/libs/freetdm/Makefile.in +++ b/libs/freetdm/Makefile.in @@ -181,7 +181,7 @@ PIKA_LIB=$(shell ls /usr/lib/libpikahmpapi.so 2>/dev/null) #endif #endif -all: $(MYLIB) analogmod isdnmod boostmod ztmod wpmod +all: $(MYLIB) analogmod analog_emmod isdnmod boostmod ztmod wpmod $(MYLIB): $(OBJS) $(HEADERS) $(SOURCES) $(LINK) $(SOLINK) -o $(MYLIB) $(OBJS) $(ADD_OBJS) $(LIBS) @@ -274,6 +274,10 @@ $(SRC)/ozmod/ozmod_analog/ozmod_analog.$(DYNAMIC_LIB_EXTEN): $(MYLIB) $(SRC)/ozm $(LINK) $(SOLINK) $(SRC)/ozmod/ozmod_analog/ozmod_analog.o $(MYLIB) -rpath $(libdir) analogmod: $(SRC)/ozmod/ozmod_analog/ozmod_analog.$(DYNAMIC_LIB_EXTEN) +$(SRC)/ozmod/ozmod_analog_em/ozmod_analog_em.$(DYNAMIC_LIB_EXTEN): $(MYLIB) $(ANALOG_EM_OBJS) $(SRC)/ozmod/ozmod_analog_em/ozmod_analog_em.o + $(LINK) $(SOLINK) $(SRC)/ozmod/ozmod_analog_em/ozmod_analog_em.o $(MYLIB) -rpath $(libdir) +analog_emmod: $(SRC)/ozmod/ozmod_analog_em/ozmod_analog_em.$(DYNAMIC_LIB_EXTEN) + $(SRC)/ozmod/ozmod_ss7_boost/ozmod_ss7_boost.$(DYNAMIC_LIB_EXTEN): $(MYLIB) $(BOOST_OBJS) $(LINK) $(SOLINK) $(BOOST_OBJS) $(MYLIB) -rpath $(libdir) boostmod: $(SRC)/ozmod/ozmod_ss7_boost/ozmod_ss7_boost.$(DYNAMIC_LIB_EXTEN) diff --git a/libs/freetdm/mod_openzap/mod_openzap.c b/libs/freetdm/mod_openzap/mod_openzap.c index ea57d85688..8d3c39c4f3 100644 --- a/libs/freetdm/mod_openzap/mod_openzap.c +++ b/libs/freetdm/mod_openzap/mod_openzap.c @@ -417,6 +417,7 @@ static switch_status_t channel_on_hangup(switch_core_session_t *session) switch (tech_pvt->zchan->type) { case ZAP_CHAN_TYPE_FXO: + case ZAP_CHAN_TYPE_EM: { zap_set_state_locked(tech_pvt->zchan, ZAP_CHANNEL_STATE_HANGUP); @@ -806,6 +807,7 @@ static switch_status_t channel_receive_message(switch_core_session_t *session, s switch (tech_pvt->zchan->type) { case ZAP_CHAN_TYPE_FXS: + case ZAP_CHAN_TYPE_EM: status = channel_receive_message_fxs(session, msg); break; case ZAP_CHAN_TYPE_FXO: @@ -1445,6 +1447,7 @@ static ZIO_SIGNAL_CB_FUNCTION(on_analog_signal) switch (sigmsg->channel->type) { case ZAP_CHAN_TYPE_FXO: + case ZAP_CHAN_TYPE_EM: { status = on_fxo_signal(sigmsg); } @@ -1616,6 +1619,100 @@ static switch_status_t load_config(void) } } + if ((spans = switch_xml_child(cfg, "analog_em_spans"))) { + for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { + char *id = (char *) switch_xml_attr_soft(myspan, "id"); + char *context = "default"; + char *dialplan = "XML"; + char *tonegroup = NULL; + char *digit_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; + zap_span_t *span = NULL; + analog_option_t analog_options = ANALOG_OPTION_NONE; + + for (param = switch_xml_child(myspan, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + if (!strcasecmp(var, "tonegroup")) { + tonegroup = val; + } else if (!strcasecmp(var, "digit_timeout") || !strcasecmp(var, "digit-timeout")) { + digit_timeout = val; + } else if (!strcasecmp(var, "context")) { + context = val; + } else if (!strcasecmp(var, "dialplan")) { + dialplan = val; + } else if (!strcasecmp(var, "dial-regex")) { + dial_regex = val; + } else if (!strcasecmp(var, "fail-dial-regex")) { + fail_dial_regex = val; + } else if (!strcasecmp(var, "hold-music")) { + hold_music = val; + } else if (!strcasecmp(var, "max_digits") || !strcasecmp(var, "max-digits")) { + max_digits = val; + } else if (!strcasecmp(var, "enable-analog-option")) { + analog_options = enable_analog_option(val, analog_options); + } + } + + if (!id) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n"); + continue; + } + + span_id = atoi(id); + + if (!tonegroup) { + tonegroup = "us"; + } + + if (digit_timeout) { + to = atoi(digit_timeout); + } + + if (max_digits) { + max = atoi(max_digits); + } + + if (zap_span_find(span_id, &span) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error finding OpenZAP span %d\n", span_id); + continue; + } + + if (zap_configure_span("analog_em", span, on_analog_signal, + "tonemap", tonegroup, + "digit_timeout", &to, + "max_dialstr", &max, + TAG_END) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error starting OpenZAP span %d\n", span_id); + continue; + } + + SPAN_CONFIG[span->span_id].span = span; + switch_set_string(SPAN_CONFIG[span->span_id].context, context); + switch_set_string(SPAN_CONFIG[span->span_id].dialplan, dialplan); + SPAN_CONFIG[span->span_id].analog_options = analog_options | globals.analog_options; + + if (dial_regex) { + switch_set_string(SPAN_CONFIG[span->span_id].dial_regex, dial_regex); + } + + if (fail_dial_regex) { + switch_set_string(SPAN_CONFIG[span->span_id].fail_dial_regex, fail_dial_regex); + } + + if (hold_music) { + switch_set_string(SPAN_CONFIG[span->span_id].hold_music, hold_music); + } + switch_copy_string(SPAN_CONFIG[span->span_id].type, "analog_em", sizeof(SPAN_CONFIG[span->span_id].type)); + zap_span_start(span); + } + } + if ((spans = switch_xml_child(cfg, "pri_spans"))) { for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { char *id = (char *) switch_xml_attr_soft(myspan, "id"); diff --git a/libs/freetdm/src/include/zap_types.h b/libs/freetdm/src/include/zap_types.h index f3b75bac98..dc34e3f40b 100644 --- a/libs/freetdm/src/include/zap_types.h +++ b/libs/freetdm/src/include/zap_types.h @@ -123,18 +123,20 @@ typedef enum { ZAP_TRUNK_BRI_PTMP, ZAP_TRUNK_FXO, ZAP_TRUNK_FXS, + ZAP_TRUNK_EM, ZAP_TRUNK_NONE } zap_trunk_type_t; -#define TRUNK_STRINGS "E1", "T1", "J1", "BRI", "BRI_PTMP", "FXO", "FXS", "NONE" +#define TRUNK_STRINGS "E1", "T1", "J1", "BRI", "BRI_PTMP", "FXO", "FXS", "EM", "NONE" ZAP_STR2ENUM_P(zap_str2zap_trunk_type, zap_trunk_type2str, zap_trunk_type_t) typedef enum { ZAP_ANALOG_START_KEWL, ZAP_ANALOG_START_LOOP, ZAP_ANALOG_START_GROUND, + ZAP_ANALOG_START_WINK, ZAP_ANALOG_START_NA } zap_analog_start_type_t; -#define START_TYPE_STRINGS "KEWL", "LOOP", "GROUND", "NA" +#define START_TYPE_STRINGS "KEWL", "LOOP", "GROUND", "WINK", "NA" ZAP_STR2ENUM_P(zap_str2zap_analog_start_type, zap_analog_start_type2str, zap_analog_start_type_t) typedef enum { @@ -284,10 +286,11 @@ typedef enum { ZAP_CHAN_TYPE_DQ931, ZAP_CHAN_TYPE_FXS, ZAP_CHAN_TYPE_FXO, + ZAP_CHAN_TYPE_EM, ZAP_CHAN_TYPE_COUNT } zap_chan_type_t; -#define CHAN_TYPE_STRINGS "B", "DQ921", "DQ931", "FXS", "FXO", "INVALID" +#define CHAN_TYPE_STRINGS "B", "DQ921", "DQ931", "FXS", "FXO", "EM", "INVALID" ZAP_STR2ENUM_P(zap_str2zap_chan_type, zap_chan_type2str, zap_chan_type_t) typedef enum { diff --git a/libs/freetdm/src/ozmod/ozmod_analog_em/ozmod_analog_em.c b/libs/freetdm/src/ozmod/ozmod_analog_em/ozmod_analog_em.c new file mode 100644 index 0000000000..a428d1f6f7 --- /dev/null +++ b/libs/freetdm/src/ozmod/ozmod_analog_em/ozmod_analog_em.c @@ -0,0 +1,663 @@ +/* + * Copyright (c) 2008, Anthony Minessale II + * 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. + * + * Contributor(s): + * + * John Wehle (john@feith.com) + * + */ + +#include "openzap.h" +#include "zap_analog_em.h" + +#ifndef localtime_r +struct tm * localtime_r(const time_t *clock, struct tm *result); +#endif + +static void *zap_analog_em_channel_run(zap_thread_t *me, void *obj); + +static ZIO_CHANNEL_OUTGOING_CALL_FUNCTION(analog_em_outgoing_call) +{ + if (!zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK) && !zap_test_flag(zchan, ZAP_CHANNEL_INTHREAD)) { + zap_channel_clear_needed_tones(zchan); + zap_channel_clear_detected_tones(zchan); + + zap_set_flag(zchan, ZAP_CHANNEL_OUTBOUND); + + zap_channel_command(zchan, ZAP_COMMAND_OFFHOOK, NULL); + zap_channel_command(zchan, ZAP_COMMAND_ENABLE_PROGRESS_DETECT, NULL); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DIALING); + zap_thread_create_detached(zap_analog_em_channel_run, zchan); + return ZAP_SUCCESS; + } + + return ZAP_FAIL; +} + + +static zap_status_t zap_analog_em_start(zap_span_t *span) +{ + zap_analog_em_data_t *analog_data = span->signal_data; + zap_set_flag(analog_data, ZAP_ANALOG_EM_RUNNING); + return zap_thread_create_detached(zap_analog_em_run, span); +} + +static ZIO_SIG_CONFIGURE_FUNCTION(zap_analog_em_configure_span) +//zap_status_t zap_analog_em_configure_span(zap_span_t *span, char *tonemap, uint32_t digit_timeout, uint32_t max_dialstr, zio_signal_cb_t sig_cb) +{ + zap_analog_em_data_t *analog_data; + const char *tonemap = "us"; + uint32_t digit_timeout = 10; + uint32_t max_dialstr = 11; + const char *var, *val; + int *intval; + + assert(sig_cb != NULL); + + if (span->signal_type) { + snprintf(span->last_error, sizeof(span->last_error), "Span is already configured for signalling."); + return ZAP_FAIL; + } + + analog_data = malloc(sizeof(*analog_data)); + assert(analog_data != NULL); + memset(analog_data, 0, sizeof(*analog_data)); + + while((var = va_arg(ap, char *))) { + if (!strcasecmp(var, "tonemap")) { + if (!(val = va_arg(ap, char *))) { + break; + } + tonemap = val; + } else if (!strcasecmp(var, "digit_timeout")) { + if (!(intval = va_arg(ap, int *))) { + break; + } + digit_timeout = *intval; + } else if (!strcasecmp(var, "max_dialstr")) { + if (!(intval = va_arg(ap, int *))) { + break; + } + max_dialstr = *intval; + } + } + + + if (digit_timeout < 2000 || digit_timeout > 10000) { + digit_timeout = 2000; + } + + if (max_dialstr < 2 || max_dialstr > 20) { + max_dialstr = 11; + } + + span->start = zap_analog_em_start; + analog_data->digit_timeout = digit_timeout; + analog_data->max_dialstr = max_dialstr; + analog_data->sig_cb = sig_cb; + span->signal_type = ZAP_SIGTYPE_ANALOG; + span->signal_data = analog_data; + span->outgoing_call = analog_em_outgoing_call; + zap_span_load_tones(span, tonemap); + + return ZAP_SUCCESS; + +} + +static int teletone_handler(teletone_generation_session_t *ts, teletone_tone_map_t *map) +{ + zap_buffer_t *dt_buffer = ts->user_data; + int wrote; + + if (!dt_buffer) { + return -1; + } + wrote = teletone_mux_tones(ts, map); + zap_buffer_write(dt_buffer, ts->buffer, wrote * 2); + return 0; +} + +static void *zap_analog_em_channel_run(zap_thread_t *me, void *obj) +{ + zap_channel_t *zchan = (zap_channel_t *) obj; + zap_buffer_t *dt_buffer = NULL; + teletone_generation_session_t ts; + uint8_t frame[1024]; + zap_size_t len, rlen; + zap_tone_type_t tt = ZAP_TONE_DTMF; + char dtmf[128] = ""; + zap_size_t dtmf_offset = 0; + zap_analog_em_data_t *analog_data = zchan->span->signal_data; + zap_channel_t *closed_chan; + uint32_t state_counter = 0, elapsed = 0, collecting = 0, interval = 0, last_digit = 0, indicate = 0, dial_timeout = 30000; + zap_sigmsg_t sig; + zap_status_t status; + + zap_log(ZAP_LOG_DEBUG, "ANALOG EM CHANNEL thread starting.\n"); + + ts.buffer = NULL; + + if (zap_channel_open_chan(zchan) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "OPEN ERROR [%s]\n", zchan->last_error); + goto done; + } + + if (zap_buffer_create(&dt_buffer, 1024, 3192, 0) != ZAP_SUCCESS) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "memory error!"); + zap_log(ZAP_LOG_ERROR, "MEM ERROR\n"); + goto done; + } + + if (zap_channel_command(zchan, ZAP_COMMAND_ENABLE_DTMF_DETECT, &tt) != ZAP_SUCCESS) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "error initilizing tone detector!"); + zap_log(ZAP_LOG_ERROR, "TONE ERROR\n"); + goto done; + } + + zap_set_flag_locked(zchan, ZAP_CHANNEL_INTHREAD); + teletone_init_session(&ts, 0, teletone_handler, dt_buffer); + ts.rate = 8000; +#if 0 + ts.debug = 1; + ts.debug_stream = stdout; +#endif + zap_channel_command(zchan, ZAP_COMMAND_GET_INTERVAL, &interval); + zap_buffer_set_loops(dt_buffer, -1); + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = zchan->chan_id; + sig.span_id = zchan->span_id; + sig.channel = zchan; + + assert(interval != 0); + + while (zap_running() && zap_test_flag(zchan, ZAP_CHANNEL_INTHREAD)) { + zap_wait_flag_t flags = ZAP_READ; + zap_size_t dlen = 0; + + len = sizeof(frame); + + elapsed += interval; + state_counter += interval; + + if (!zap_test_flag(zchan, ZAP_CHANNEL_STATE_CHANGE)) { + switch(zchan->state) { + case ZAP_CHANNEL_STATE_DIALING: + { + if (! zchan->needed_tones[ZAP_TONEMAP_RING] + && zap_test_flag(zchan, ZAP_CHANNEL_WINK)) { + if (zap_strlen_zero(zchan->caller_data.ani.digits)) { + zap_log(ZAP_LOG_ERROR, "No Digits to send!\n"); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } else { + if (zap_channel_command(zchan, ZAP_COMMAND_SEND_DTMF, zchan->caller_data.ani.digits) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Send Digits Failed [%s]\n", zchan->last_error); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } else { + state_counter = 0; + zchan->needed_tones[ZAP_TONEMAP_RING] = 1; + zchan->needed_tones[ZAP_TONEMAP_BUSY] = 1; + zchan->needed_tones[ZAP_TONEMAP_FAIL1] = 1; + zchan->needed_tones[ZAP_TONEMAP_FAIL2] = 1; + zchan->needed_tones[ZAP_TONEMAP_FAIL3] = 1; + dial_timeout = ((zchan->dtmf_on + zchan->dtmf_off) * strlen(zchan->caller_data.ani.digits)) + 2000; + } + } + break; + } + if (state_counter > dial_timeout) { + if (!zap_test_flag(zchan, ZAP_CHANNEL_WINK)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_UP); + } + } + } + break; + case ZAP_CHANNEL_STATE_DIALTONE: + { + if (state_counter > 10000) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } + } + break; + case ZAP_CHANNEL_STATE_BUSY: + { + if (state_counter > 20000) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_ATTN); + } + } + break; + case ZAP_CHANNEL_STATE_ATTN: + { + if (state_counter > 20000) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + } + break; + case ZAP_CHANNEL_STATE_HANGUP: + { + if (state_counter > 500) { + if (zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK) && + (zchan->last_state == ZAP_CHANNEL_STATE_RING || zchan->last_state == ZAP_CHANNEL_STATE_DIALTONE + || zchan->last_state >= ZAP_CHANNEL_STATE_IDLE)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } else { + zchan->caller_data.hangup_cause = ZAP_CAUSE_NORMAL_CLEARING; + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + } + } + break; + case ZAP_CHANNEL_STATE_UP: + case ZAP_CHANNEL_STATE_IDLE: + { + zap_sleep(interval); + continue; + } + break; + case ZAP_CHANNEL_STATE_DOWN: + { + goto done; + } + break; + default: + break; + } + } else { + zap_clear_flag_locked(zchan, ZAP_CHANNEL_STATE_CHANGE); + zap_clear_flag_locked(zchan->span, ZAP_SPAN_STATE_CHANGE); + zap_channel_complete_state(zchan); + indicate = 0; + state_counter = 0; + + zap_log(ZAP_LOG_DEBUG, "Executing state handler on %d:%d for %s\n", + zchan->span_id, zchan->chan_id, + zap_channel_state2str(zchan->state)); + switch(zchan->state) { + case ZAP_CHANNEL_STATE_UP: + { + zap_channel_use(zchan); + zap_channel_clear_needed_tones(zchan); + zap_channel_flush_dtmf(zchan); + + if (!zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK)) { + zap_channel_command(zchan, ZAP_COMMAND_OFFHOOK, NULL); + } + + sig.event_id = ZAP_SIGEVENT_UP; + + analog_data->sig_cb(&sig); + continue; + } + break; + case ZAP_CHANNEL_STATE_DIALING: + { + zap_channel_use(zchan); + } + break; + case ZAP_CHANNEL_STATE_IDLE: + { + zap_channel_use(zchan); + + if (zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + zap_set_string(zchan->caller_data.dnis.digits, zchan->chan_number); + } else { + zap_set_string(zchan->caller_data.dnis.digits, dtmf); + } + + sig.event_id = ZAP_SIGEVENT_START; + + analog_data->sig_cb(&sig); + continue; + } + break; + case ZAP_CHANNEL_STATE_DOWN: + { + sig.event_id = ZAP_SIGEVENT_STOP; + analog_data->sig_cb(&sig); + goto done; + } + break; + case ZAP_CHANNEL_STATE_DIALTONE: + { + memset(&zchan->caller_data, 0, sizeof(zchan->caller_data)); + *dtmf = '\0'; + dtmf_offset = 0; + zap_buffer_zero(dt_buffer); + teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_DIAL]); + indicate = 1; + + zap_sleep(25); + zap_channel_command(zchan, ZAP_COMMAND_OFFHOOK, NULL); + zap_sleep(200); + zap_channel_command(zchan, ZAP_COMMAND_ONHOOK, NULL); + zap_sleep(50); + } + break; + case ZAP_CHANNEL_STATE_RING: + { + zap_buffer_zero(dt_buffer); + teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_RING]); + indicate = 1; + } + break; + case ZAP_CHANNEL_STATE_BUSY: + { + zchan->caller_data.hangup_cause = ZAP_CAUSE_NORMAL_CIRCUIT_CONGESTION; + if (zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK) && !zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + zap_buffer_zero(dt_buffer); + teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_BUSY]); + indicate = 1; + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + } + break; + case ZAP_CHANNEL_STATE_ATTN: + { + if (zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK) && !zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + zap_buffer_zero(dt_buffer); + teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_ATTN]); + indicate = 1; + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + } + break; + default: + break; + } + } + + + if (zchan->state == ZAP_CHANNEL_STATE_DIALTONE || zchan->state == ZAP_CHANNEL_STATE_COLLECT) { + if ((dlen = zap_channel_dequeue_dtmf(zchan, dtmf + dtmf_offset, sizeof(dtmf) - strlen(dtmf)))) { + + if (zchan->state == ZAP_CHANNEL_STATE_DIALTONE) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_COLLECT); + collecting = 1; + } + dtmf_offset = strlen(dtmf); + last_digit = elapsed; + sig.event_id = ZAP_SIGEVENT_COLLECTED_DIGIT; + sig.raw_data = dtmf; + if (analog_data->sig_cb(&sig) == ZAP_BREAK) { + collecting = 0; + } + } + } + + + if (last_digit && (!collecting || ((elapsed - last_digit > analog_data->digit_timeout) || strlen(dtmf) > analog_data->max_dialstr))) { + zap_log(ZAP_LOG_DEBUG, "Number obtained [%s]\n", dtmf); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_IDLE); + last_digit = 0; + collecting = 0; + } + + if (zap_channel_wait(zchan, &flags, interval * 2) != ZAP_SUCCESS) { + continue; + } + + if (!(flags & ZAP_READ)) { + continue; + } + + if (zap_channel_read(zchan, frame, &len) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "READ ERROR [%s]\n", zchan->last_error); + goto done; + } + + if (zchan->detected_tones[0]) { + zap_sigmsg_t sig; + int i; + memset(&sig, 0, sizeof(sig)); + sig.chan_id = zchan->chan_id; + sig.span_id = zchan->span_id; + sig.channel = zchan; + sig.event_id = ZAP_SIGEVENT_TONE_DETECTED; + + for (i = 1; i < ZAP_TONEMAP_INVALID; i++) { + if (zchan->detected_tones[i]) { + zap_log(ZAP_LOG_DEBUG, "Detected tone %s on %d:%d\n", zap_tonemap2str(i), zchan->span_id, zchan->chan_id); + sig.raw_data = &i; + if (analog_data->sig_cb) { + analog_data->sig_cb(&sig); + } + } + } + + if (zchan->detected_tones[ZAP_TONEMAP_BUSY] || + zchan->detected_tones[ZAP_TONEMAP_FAIL1] || + zchan->detected_tones[ZAP_TONEMAP_FAIL2] || + zchan->detected_tones[ZAP_TONEMAP_FAIL3] || + zchan->detected_tones[ZAP_TONEMAP_ATTN] + ) { + zap_log(ZAP_LOG_ERROR, "Failure indication detected!\n"); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } else if (zchan->detected_tones[ZAP_TONEMAP_RING]) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_UP); + } + + zap_channel_clear_detected_tones(zchan); + } + + if ((zchan->dtmf_buffer && zap_buffer_inuse(zchan->dtmf_buffer))) { + rlen = len; + memset(frame, 0, len); + zap_channel_write(zchan, frame, sizeof(frame), &rlen); + continue; + } + + if (!indicate) { + continue; + } + + if (zchan->effective_codec != ZAP_CODEC_SLIN) { + len *= 2; + } + + rlen = zap_buffer_read_loop(dt_buffer, frame, len); + + if (zchan->effective_codec != ZAP_CODEC_SLIN) { + zio_codec_t codec_func = NULL; + + if (zchan->native_codec == ZAP_CODEC_ULAW) { + codec_func = zio_slin2ulaw; + } else if (zchan->native_codec == ZAP_CODEC_ALAW) { + codec_func = zio_slin2alaw; + } + + if (codec_func) { + status = codec_func(frame, sizeof(frame), &rlen); + } else { + snprintf(zchan->last_error, sizeof(zchan->last_error), "codec error!"); + goto done; + } + } + + zap_channel_write(zchan, frame, sizeof(frame), &rlen); + } + + done: + + zap_channel_command(zchan, ZAP_COMMAND_ONHOOK, NULL); + + closed_chan = zchan; + zap_channel_close(&zchan); + + zap_channel_command(closed_chan, ZAP_COMMAND_SET_NATIVE_CODEC, NULL); + + if (ts.buffer) { + teletone_destroy_session(&ts); + } + + if (dt_buffer) { + zap_buffer_destroy(&dt_buffer); + } + + zap_clear_flag(closed_chan, ZAP_CHANNEL_INTHREAD); + + zap_log(ZAP_LOG_DEBUG, "ANALOG EM CHANNEL thread ended.\n"); + + return NULL; +} + +static __inline__ zap_status_t process_event(zap_span_t *span, zap_event_t *event) +{ + zap_sigmsg_t sig; + int locked = 0; + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = event->channel->chan_id; + sig.span_id = event->channel->span_id; + sig.channel = event->channel; + + + zap_log(ZAP_LOG_DEBUG, "EVENT [%s][%d:%d] STATE [%s]\n", + zap_oob_event2str(event->enum_id), event->channel->span_id, event->channel->chan_id, zap_channel_state2str(event->channel->state)); + + zap_mutex_lock(event->channel->mutex); + locked++; + + switch(event->enum_id) { + case ZAP_OOB_ONHOOK: + { + if (event->channel->state != ZAP_CHANNEL_STATE_DOWN) { + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_DOWN); + } + + } + break; + case ZAP_OOB_OFFHOOK: + { + if (zap_test_flag(event->channel, ZAP_CHANNEL_INTHREAD)) { + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_UP); + } else { + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_DIALTONE); + zap_mutex_unlock(event->channel->mutex); + locked = 0; + zap_thread_create_detached(zap_analog_em_channel_run, event->channel); + } + break; + } + case ZAP_OOB_WINK: + { + if (event->channel->state != ZAP_CHANNEL_STATE_DIALING) { + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_DOWN); + } else { + zap_set_flag_locked(event->channel, ZAP_CHANNEL_WINK); + } + + } + break; + } + if (locked) { + zap_mutex_unlock(event->channel->mutex); + } + return ZAP_SUCCESS; +} + +static void *zap_analog_em_run(zap_thread_t *me, void *obj) +{ + zap_span_t *span = (zap_span_t *) obj; + zap_analog_em_data_t *analog_data = span->signal_data; + + zap_log(ZAP_LOG_DEBUG, "ANALOG EM thread starting.\n"); + + while(zap_running() && zap_test_flag(analog_data, ZAP_ANALOG_EM_RUNNING)) { + int waitms = 10; + zap_status_t status; + + status = zap_span_poll_event(span, waitms); + + switch(status) { + case ZAP_SUCCESS: + { + zap_event_t *event; + while (zap_span_next_event(span, &event) == ZAP_SUCCESS) { + if (event->enum_id == ZAP_OOB_NOOP) { + continue; + } + if (process_event(span, event) != ZAP_SUCCESS) { + goto end; + } + } + } + break; + case ZAP_FAIL: + { + zap_log(ZAP_LOG_ERROR, "Failure Polling event! [%s]\n", span->last_error); + } + break; + default: + break; + } + + } + + end: + + zap_clear_flag(analog_data, ZAP_ANALOG_EM_RUNNING); + + zap_log(ZAP_LOG_DEBUG, "ANALOG EM thread ending.\n"); + + return NULL; +} + + +static ZIO_SIG_LOAD_FUNCTION(zap_analog_em_init) +{ + return ZAP_SUCCESS; +} + +zap_module_t zap_module = { + "analog_em", + NULL, + NULL, + zap_analog_em_init, + zap_analog_em_configure_span, + NULL +}; + + +/* 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 expandtab: + */ diff --git a/libs/freetdm/src/ozmod/ozmod_analog_em/zap_analog_em.h b/libs/freetdm/src/ozmod/ozmod_analog_em/zap_analog_em.h new file mode 100644 index 0000000000..e20875d54c --- /dev/null +++ b/libs/freetdm/src/ozmod/ozmod_analog_em/zap_analog_em.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2008, Anthony Minessale II + * 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. + * + * Contributor(s): + * + * John Wehle (john@feith.com) + * + */ + +#ifndef ZAP_ANALOG_EM_H +#define ZAP_ANALOG_EM_H +#include "openzap.h" + +typedef enum { + ZAP_ANALOG_EM_RUNNING = (1 << 0) +} zap_analog_em_flag_t; + +static void *zap_analog_em_run(zap_thread_t *me, void *obj); +typedef struct zap_analog_data zap_analog_em_data_t; + +#endif + +/* 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 expandtab: + */ diff --git a/libs/freetdm/src/ozmod/ozmod_zt/ozmod_zt.c b/libs/freetdm/src/ozmod/ozmod_zt/ozmod_zt.c index a739300d1b..43a662d9fd 100644 --- a/libs/freetdm/src/ozmod/ozmod_zt/ozmod_zt.c +++ b/libs/freetdm/src/ozmod/ozmod_zt/ozmod_zt.c @@ -173,7 +173,7 @@ static unsigned zt_open_range(zap_span_t *span, unsigned start, unsigned end, za zchan->physical_span_id = ztp.span_no; zchan->physical_chan_id = ztp.chan_no; - if (type == ZAP_CHAN_TYPE_FXS || type == ZAP_CHAN_TYPE_FXO || type == ZAP_CHAN_TYPE_B) { + if (type == ZAP_CHAN_TYPE_FXS || type == ZAP_CHAN_TYPE_FXO || type == ZAP_CHAN_TYPE_EM || type == ZAP_CHAN_TYPE_B) { if (ztp.g711_type == ZT_G711_ALAW) { zchan->native_codec = zchan->effective_codec = ZAP_CODEC_ALAW; } else if (ztp.g711_type == ZT_G711_MULAW) { @@ -343,7 +343,7 @@ static ZIO_OPEN_FUNCTION(zt_open) zap_log(ZAP_LOG_ERROR, "%s\n", zchan->last_error); return ZAP_FAIL; } - } else if (zchan->type == ZAP_CHAN_TYPE_FXS || zchan->type == ZAP_CHAN_TYPE_FXO) { + } else if (zchan->type == ZAP_CHAN_TYPE_FXS || zchan->type == ZAP_CHAN_TYPE_FXO || zchan->type == ZAP_CHAN_TYPE_EM) { int len = zt_globals.eclevel; if (ioctl(zchan->sockfd, ZT_ECHOCANCEL, &len)) { zap_log(ZAP_LOG_WARNING, "Echo cancel not available for %d:%d\n", zchan->span_id, zchan->chan_id); @@ -636,7 +636,7 @@ ZIO_SPAN_NEXT_EVENT_FUNCTION(zt_next_event) break; case ZT_EVENT_WINKFLASH: { - if (span->channels[i]->state == ZAP_CHANNEL_STATE_DOWN) { + if (span->channels[i]->state == ZAP_CHANNEL_STATE_DOWN || span->channels[i]->state == ZAP_CHANNEL_STATE_DIALING) { event_id = ZAP_OOB_WINK; } else { event_id = ZAP_OOB_FLASH; @@ -645,7 +645,7 @@ ZIO_SPAN_NEXT_EVENT_FUNCTION(zt_next_event) break; case ZT_EVENT_RINGOFFHOOK: { - if (span->channels[i]->type == ZAP_CHAN_TYPE_FXS) { + if (span->channels[i]->type == ZAP_CHAN_TYPE_FXS || (span->channels[i]->type == ZAP_CHAN_TYPE_EM && span->channels[i]->state != ZAP_CHANNEL_STATE_UP)) { zap_set_flag_locked(span->channels[i], ZAP_CHANNEL_OFFHOOK); event_id = ZAP_OOB_OFFHOOK; } else if (span->channels[i]->type == ZAP_CHAN_TYPE_FXO) { diff --git a/libs/freetdm/src/zap_io.c b/libs/freetdm/src/zap_io.c index ddbf269b22..acef551eb8 100644 --- a/libs/freetdm/src/zap_io.c +++ b/libs/freetdm/src/zap_io.c @@ -2032,7 +2032,7 @@ static zap_status_t load_config(void) zap_copy_string(number, val, sizeof(number)); } } else if (!strcasecmp(var, "analog-start-type")) { - if (span->trunk_type == ZAP_TRUNK_FXS || span->trunk_type == ZAP_TRUNK_FXO) { + if (span->trunk_type == ZAP_TRUNK_FXS || span->trunk_type == ZAP_TRUNK_FXO || span->trunk_type == ZAP_TRUNK_EM) { if ((tmp = zap_str2zap_analog_start_type(val)) != ZAP_ANALOG_START_NA) { span->start_type = tmp; zap_log(ZAP_LOG_DEBUG, "changing start type to '%s'\n", zap_analog_start_type2str(span->start_type)); @@ -2062,6 +2062,17 @@ static zap_status_t load_config(void) } else { zap_log(ZAP_LOG_WARNING, "Cannot add FXS channels to an FXO trunk!\n"); } + } else if (!strcasecmp(var, "em-channel")) { + if (span->trunk_type == ZAP_TRUNK_NONE) { + span->trunk_type = ZAP_TRUNK_EM; + zap_log(ZAP_LOG_DEBUG, "setting trunk type to '%s' start(%s)\n", zap_trunk_type2str(span->trunk_type), + zap_analog_start_type2str(span->start_type)); + } + if (span->trunk_type == ZAP_TRUNK_EM) { + configured += zio->configure_span(span, val, ZAP_CHAN_TYPE_EM, name, number); + } else { + zap_log(ZAP_LOG_WARNING, "Cannot add EM channels to a non-EM trunk!\n"); + } } else if (!strcasecmp(var, "b-channel")) { configured += zio->configure_span(span, val, ZAP_CHAN_TYPE_B, name, number); } else if (!strcasecmp(var, "d-channel")) {