]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
freetdm: add PRI tapping starting code
authorMoises Silva <moy@sangoma.com>
Fri, 30 Jul 2010 23:46:05 +0000 (19:46 -0400)
committerMoises Silva <moy@sangoma.com>
Fri, 30 Jul 2010 23:46:05 +0000 (19:46 -0400)
libs/freetdm/Makefile.am
libs/freetdm/configure.ac
libs/freetdm/mod_freetdm/mod_freetdm.c
libs/freetdm/src/ftdm_io.c
libs/freetdm/src/ftmod/ftmod_pritap/ftmod_pritap.c [new file with mode: 0644]

index 54c44f81691f7991897f4e990c938e7b0f468dff..ff5e23beb143b65e8fe1de6d273b30c1318e3b1b 100644 (file)
@@ -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 \
index 17b063a3612bacb07980ae60f1c0f4566f051c67..9ad8c4f6784e3d1cb2787251f02d15349dba4874 100644 (file)
@@ -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"])
index c7262242ef00dc0d3980711ea0906c857a6efd61..852914bf36e5afdc9dd5a55879ac69323027329d 100755 (executable)
@@ -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"))) {
index 738c657f218d880acb1df39840bb357cc8e8f3ad..81bef16a623558c8f18b053967321063a0c669ae 100644 (file)
@@ -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 (file)
index 0000000..1441d54
--- /dev/null
@@ -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:
+ */