From b485f25f5ec8de1a481802bfd7008db4b56195fe Mon Sep 17 00:00:00 2001 From: Moises Silva <moy@sangoma.com> Date: Fri, 30 Jul 2010 19:46:05 -0400 Subject: [PATCH] freetdm: add PRI tapping starting code --- libs/freetdm/Makefile.am | 11 + libs/freetdm/configure.ac | 7 + libs/freetdm/mod_freetdm/mod_freetdm.c | 53 ++ libs/freetdm/src/ftdm_io.c | 21 +- .../src/ftmod/ftmod_pritap/ftmod_pritap.c | 626 ++++++++++++++++++ 5 files changed, 711 insertions(+), 7 deletions(-) create mode 100644 libs/freetdm/src/ftmod/ftmod_pritap/ftmod_pritap.c diff --git a/libs/freetdm/Makefile.am b/libs/freetdm/Makefile.am index 54c44f8169..ff5e23beb1 100644 --- a/libs/freetdm/Makefile.am +++ b/libs/freetdm/Makefile.am @@ -172,6 +172,10 @@ if LIBPRI mod_LTLIBRARIES += ftmod_libpri.la endif +if PRITAP +mod_LTLIBRARIES += ftmod_pritap.la +endif + if SNGSS7 mod_LTLIBRARIES += ftmod_sangoma_ss7.la endif @@ -252,6 +256,13 @@ ftmod_libpri_la_LDFLAGS = -module -avoid-version -lpri ftmod_libpri_la_LIBADD = $(MYLIB) endif +if PRITAP +ftmod_pritap_la_SOURCES = $(SRC)/ftmod/ftmod_pritap/ftmod_pritap.c +ftmod_pritap_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) +ftmod_pritap_la_LDFLAGS = -module -avoid-version -lpri +ftmod_pritap_la_LIBADD = $(MYLIB) +endif + if SNGSS7 ftmod_sangoma_ss7_la_SOURCES = $(SRC)/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_support.c \ $(SRC)/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c \ diff --git a/libs/freetdm/configure.ac b/libs/freetdm/configure.ac index 17b063a361..9ad8c4f678 100644 --- a/libs/freetdm/configure.ac +++ b/libs/freetdm/configure.ac @@ -165,6 +165,11 @@ AC_ARG_WITH([libpri], [AS_HELP_STRING([--with-libpri], [Install ftmod_libpri])], [enable_libpri="yes"], [enable_libpri="no"]) AC_SUBST(enable_libpri) +# pritap? +AC_ARG_WITH([pritap], + [AS_HELP_STRING([--with-pritap], [Install ftmod_pritap])], [enable_pritap="yes"], [enable_pritap="no"]) +AC_SUBST(enable_pritap) + # debug dtmf? AC_ARG_WITH([debugdtmf], [AS_HELP_STRING([--with-debugdtmf], [Debug DTMF])], [enable_debugdtmf="yes"], [enable_debugdtmf="no"]) @@ -175,6 +180,8 @@ AM_CONDITIONAL([LIBSANGOMA],[test "${have_libsangoma}" = "yes"]) AM_CONDITIONAL([LIBPRI],[test "${enable_libpri}" = "yes"]) +AM_CONDITIONAL([PRITAP],[test "${enable_pritap}" = "yes"]) + AM_CONDITIONAL([DEBUGDTMF],[test "${enable_debugdtmf}" = "yes"]) AC_CHECK_LIB([sng_ss7], [sng_isup_init], [have_sng_ss7="yes"]) diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c index c7262242ef..852914bf36 100755 --- a/libs/freetdm/mod_freetdm/mod_freetdm.c +++ b/libs/freetdm/mod_freetdm/mod_freetdm.c @@ -2938,6 +2938,59 @@ static switch_status_t load_config(void) } } + if ((spans = switch_xml_child(cfg, "pritap_spans"))) { + for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { + + char *name = (char *) switch_xml_attr(myspan, "name"); + + ftdm_status_t zstatus = FTDM_FAIL; + unsigned paramindex = 0; + ftdm_conf_parameter_t spanparameters[10]; + const char *context = "default"; + const char *dialplan = "XML"; + ftdm_span_t *span = NULL; + int span_id = 0; + + if (!name) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required attribute 'name'\n"); + continue; + } + + 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, "context")) { + context = val; + } else if (!strcasecmp(var, "dialplan")) { + dialplan = val; + } else { + spanparameters[paramindex].var = var; + spanparameters[paramindex].val = val; + paramindex++; + } + } + + zstatus = ftdm_span_find_by_name(name, &span); + if (zstatus != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span %s\n", name); + continue; + } + + span_id = ftdm_span_get_id(span); + if (ftdm_configure_span_signaling(span, "pritap", on_clear_channel_signal, spanparameters) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error configuring FreeTDM span %s\n", name); + continue; + } + + SPAN_CONFIG[span_id].span = span; + switch_copy_string(SPAN_CONFIG[span_id].context, context, sizeof(SPAN_CONFIG[span_id].context)); + switch_copy_string(SPAN_CONFIG[span_id].dialplan, dialplan, sizeof(SPAN_CONFIG[span_id].dialplan)); + switch_copy_string(SPAN_CONFIG[span_id].type, "isdn", sizeof(SPAN_CONFIG[span_id].type)); + + ftdm_span_start(span); + } + } if ((spans = switch_xml_child(cfg, "libpri_spans"))) { diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index 738c657f21..81bef16a62 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -1334,21 +1334,28 @@ static __inline__ int chan_is_avail(ftdm_channel_t *check) ftdm_test_flag(check, FTDM_CHANNEL_INUSE) || ftdm_test_flag(check, FTDM_CHANNEL_SUSPENDED) || ftdm_test_flag(check, FTDM_CHANNEL_IN_ALARM) || - check->state != FTDM_CHANNEL_STATE_DOWN || - !FTDM_IS_VOICE_CHANNEL(check)) { + check->state != FTDM_CHANNEL_STATE_DOWN) { return 0; } return 1; } -static __inline__ int request_channel(ftdm_channel_t *check, ftdm_channel_t **ftdmchan, +static __inline__ int chan_voice_is_avail(ftdm_channel_t *check) +{ + if (!FTDM_IS_VOICE_CHANNEL(check)) { + return 0; + } + return chan_is_avail(check); +} + +static __inline__ int request_voice_channel(ftdm_channel_t *check, ftdm_channel_t **ftdmchan, ftdm_caller_data_t *caller_data, ftdm_direction_t direction) { ftdm_status_t status; - if (chan_is_avail(check)) { + if (chan_voice_is_avail(check)) { /* unlocked testing passed, try again with the channel locked */ ftdm_mutex_lock(check->mutex); - if (chan_is_avail(check)) { + if (chan_voice_is_avail(check)) { if (check->span && check->span->channel_request) { /* I am only unlocking here cuz this function is called * sometimes with the group or span lock held and were @@ -1468,7 +1475,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_open_by_group(uint32_t group_id, ftdm_dir break; } - if (request_channel(check, ftdmchan, caller_data, direction)) { + if (request_voice_channel(check, ftdmchan, caller_data, direction)) { status = FTDM_SUCCESS; break; } @@ -1579,7 +1586,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_open_by_span(uint32_t span_id, ftdm_direc break; } - if (request_channel(check, ftdmchan, caller_data, direction)) { + if (request_voice_channel(check, ftdmchan, caller_data, direction)) { status = FTDM_SUCCESS; break; } diff --git a/libs/freetdm/src/ftmod/ftmod_pritap/ftmod_pritap.c b/libs/freetdm/src/ftmod/ftmod_pritap/ftmod_pritap.c new file mode 100644 index 0000000000..1441d54f8d --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_pritap/ftmod_pritap.c @@ -0,0 +1,626 @@ +/* + * 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" + +typedef enum { + PRITAP_RUNNING = (1 << 0), +} pritap_flags_t; + +typedef struct pritap { + int32_t flags; + struct pri *pri; + int debug; + ftdm_channel_t *dchan; + ftdm_span_t *span; + ftdm_span_t *peerspan; + struct pritap *pritap; +} 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, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_CHANNEL_STATE_DOWN, FTDM_END}, + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_END}, + {FTDM_CHANNEL_STATE_DOWN, FTDM_END}, + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, + FTDM_CHANNEL_STATE_CANCEL, FTDM_CHANNEL_STATE_UP, FTDM_END}, + }, + { + 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) +{ + pritap_t *pritap = ftdmchan->span->signal_data; + ftdm_status_t status; + ftdm_sigmsg_t sig; + q931_call *call = (q931_call *) ftdmchan->call_data; + + + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "processing state %s\n", ftdmchan->span_id, ftdmchan->chan_id, ftdm_channel_state2str(ftdmchan->state)); + + 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: + { + ftdmchan->call_data = NULL; + ftdm_channel_done(ftdmchan); + } + break; + + case FTDM_CHANNEL_STATE_PROGRESS: + { + pri_progress(pritap->pri, call, ftdmchan->chan_id, 1); + } + break; + + case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: + { + pri_proceeding(pritap->pri, call, ftdmchan->chan_id, 1); + } + break; + + case FTDM_CHANNEL_STATE_RING: + { + pri_acknowledge(pritap->pri, call, ftdmchan->chan_id, 0); + 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_UP: + { + pri_answer(pritap->pri, call, 0, 1); + } + break; + + case FTDM_CHANNEL_STATE_HANGUP: + { + if (call) { + pri_hangup(pritap->pri, call, ftdmchan->caller_data.hangup_cause); + pri_destroycall(pritap->pri, call); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } else { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RESTART); + } + } + break; + + case FTDM_CHANNEL_STATE_HANGUP_COMPLETE: + break; + + case FTDM_CHANNEL_STATE_TERMINATING: + { + sig.event_id = FTDM_SIGEVENT_STOP; + status = ftdm_span_send_signal(ftdmchan->span, &sig); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + + } + + 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; +} + +static void handle_pri_passive_event(pritap_t *pritap, pri_event *e) +{ + ftdm_log(FTDM_LOG_NOTICE, "passive event %s on span %s\n", pri_event2str(e->gen.e), pritap->span->name); + + switch (e->e) { + + case PRI_EVENT_RING: + break; + + case PRI_EVENT_PROGRESS: + break; + + case PRI_EVENT_PROCEEDING: + break; + + case PRI_EVENT_ANSWER: + break; + + case PRI_EVENT_HANGUP: + break; + + case PRI_EVENT_HANGUP_ACK: + 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); + } + + return FTDM_SUCCESS; +} + +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; + } + + 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; + 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: + */