]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
added initial OpenR2 support
authorMoises Silva <moy@sangoma.com>
Wed, 19 Aug 2009 21:47:56 +0000 (21:47 +0000)
committerMoises Silva <moy@sangoma.com>
Wed, 19 Aug 2009 21:47:56 +0000 (21:47 +0000)
git-svn-id: http://svn.openzap.org/svn/openzap/trunk@808 a93c3328-9c30-0410-af19-c9cd2b2d52af

libs/freetdm/Makefile.am
libs/freetdm/configure.ac
libs/freetdm/mod_openzap/mod_openzap.c
libs/freetdm/src/include/openzap.h
libs/freetdm/src/include/zap_types.h
libs/freetdm/src/ozmod/ozmod_r2/ozmod_r2.c [new file with mode: 0644]
libs/freetdm/src/ozmod/ozmod_wanpipe/ozmod_wanpipe.c
libs/freetdm/src/ozmod/ozmod_zt/ozmod_zt.c
libs/freetdm/src/testr2.c [new file with mode: 0644]
libs/freetdm/src/zap_config.c
libs/freetdm/src/zap_io.c

index bce0cf2bd535e260de8a725cd6fd707a6c1be2e3..c8d334c86476caa09a6eb24d8ad04a99ece9e366 100644 (file)
@@ -101,7 +101,7 @@ core-install: install-libLTLIBRARIES
 #
 # tools & test programs
 #
-noinst_PROGRAMS = testtones detect_tones detect_dtmf testisdn testpri testboost testanalog testapp testcid
+noinst_PROGRAMS = testtones detect_tones detect_dtmf testisdn testpri testr2 testboost testanalog testapp testcid
 
 testapp_SOURCES = $(SRC)/testapp.c
 testapp_LDADD   = libopenzap.la
@@ -131,6 +131,10 @@ testpri_SOURCES = $(SRC)/testpri.c
 testpri_LDADD   = libopenzap.la
 testpri_CFLAGS  = $(AM_CFLAGS) $(MY_CFLAGS)
 
+testr2_SOURCES = $(SRC)/testr2.c
+testr2_LDADD   = libopenzap.la
+testr2_CFLAGS  = $(AM_CFLAGS) $(MY_CFLAGS)
+
 testboost_SOURCES = $(SRC)/testboost.c
 testboost_LDADD   = libopenzap.la
 testboost_CFLAGS  = $(AM_CFLAGS) $(MY_CFLAGS)
@@ -152,6 +156,10 @@ if LIBPRI
 mod_LTLIBRARIES += ozmod_libpri.la
 endif
 
+if OPENR2
+mod_LTLIBRARIES += ozmod_r2.la
+endif
+
 ozmod_zt_la_SOURCES = $(SRC)/ozmod/ozmod_zt/ozmod_zt.c
 ozmod_zt_la_CFLAGS  = $(AM_CFLAGS) $(MY_CFLAGS)
 ozmod_zt_la_LDFLAGS = -module -avoid-version
@@ -218,6 +226,13 @@ ozmod_libpri_la_LDFLAGS = -module  -avoid-version -lpri
 ozmod_libpri_la_LIBADD  = $(MYLIB)
 endif
 
+if OPENR2
+ozmod_r2_la_SOURCES = $(SRC)/ozmod/ozmod_r2/ozmod_r2.c 
+ozmod_r2_la_CFLAGS  = $(AM_CFLAGS) $(MY_CFLAGS)
+ozmod_r2_la_LDFLAGS = -module  -avoid-version -lopenr2
+ozmod_r2_la_LIBADD  = $(MYLIB)
+endif
+
 
 dox doxygen:
        cd docs && doxygen $(OZ_SRCDIR)/docs/Doxygen.conf
index e89564af7ae73ffa583e79ac0f328f3e4926b632..64f7a46438bcd1382b22b7c18d639fb1be303af7 100644 (file)
@@ -168,6 +168,9 @@ AM_CONDITIONAL([LIBSANGOMA],[test "${have_libsangoma}" = "yes"])
 
 AM_CONDITIONAL([LIBPRI],[test "${enable_libpri}" = "yes"])
 
+AC_CHECK_LIB([openr2], [openr2_context_set_io_type], [have_openr2="yes"])
+AM_CONDITIONAL([OPENR2],[test "${have_openr2}" = "yes"])
+
 COMP_VENDOR_CFLAGS="$COMP_VENDOR_CFLAGS"
 AC_SUBST(COMP_VENDOR_CFLAGS)
 AC_CONFIG_FILES([Makefile
index e2a7db22c052ada83f8c287a8760d1a99305ec1e..905627920f3774f232a6b61f87aea4daa89a6059 100644 (file)
@@ -24,6 +24,7 @@
  * Contributor(s):
  * 
  * Anthony Minessale II <anthmct@yahoo.com>
+ * Moises Silva <moy@sangoma.com>
  *
  *
  * mod_openzap.c -- OPENZAP Endpoint Module
@@ -446,6 +447,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:
+       case ZAP_CHAN_TYPE_CAS:
                {
 
                        zap_set_state_locked(tech_pvt->zchan, ZAP_CHANNEL_STATE_HANGUP);
@@ -723,6 +725,65 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc
 
 }
 
+static switch_status_t channel_receive_message_cas(switch_core_session_t *session, switch_core_session_message_t *msg)
+{
+       switch_channel_t *channel;
+       private_t *tech_pvt;
+
+       channel = switch_core_session_get_channel(session);
+       assert(channel != NULL);
+                       
+       tech_pvt = (private_t *) switch_core_session_get_private(session);
+       assert(tech_pvt != NULL);
+       
+       zap_log(ZAP_LOG_DEBUG, "Got Freeswitch message in R2 channel %d [%d]\n", tech_pvt->zchan->physical_chan_id, 
+            msg->message_id);
+
+       switch (msg->message_id) {
+       case SWITCH_MESSAGE_INDICATE_RINGING:
+               {
+                       if (switch_channel_test_flag(channel, CF_OUTBOUND)) {
+                               zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_PROGRESS);
+                       } else {
+                               zap_set_state_locked_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_PROGRESS);
+                       }
+               }
+               break;
+       case SWITCH_MESSAGE_INDICATE_PROGRESS:
+               {
+                       if (switch_channel_test_flag(channel, CF_OUTBOUND)) {
+                               zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_PROGRESS);
+                               zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_MEDIA);
+                       } else {
+                               zap_set_state_locked_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_PROGRESS);
+                               zap_set_state_locked_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_PROGRESS_MEDIA);
+                       }
+               }
+               break;
+       case SWITCH_MESSAGE_INDICATE_ANSWER:
+               {
+                       if (switch_channel_test_flag(channel, CF_OUTBOUND)) {
+                               zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_ANSWERED);
+                       } else {
+                               /* lets make the ozmod_r2 module life easier by moving thru each
+                 * state waiting for completion, clumsy, but does the job
+                                */
+                               if (tech_pvt->zchan->state < ZAP_CHANNEL_STATE_PROGRESS) {
+                                       zap_set_state_locked_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_PROGRESS);
+                               }
+                               if (tech_pvt->zchan->state < ZAP_CHANNEL_STATE_PROGRESS_MEDIA) {
+                                       zap_set_state_locked_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_PROGRESS_MEDIA);
+                               }
+                               zap_set_state_locked_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_UP);
+                       }
+               }
+               break;
+       default:
+               break;
+       }
+
+       return SWITCH_STATUS_SUCCESS;
+}
 
 static switch_status_t channel_receive_message_b(switch_core_session_t *session, switch_core_session_message_t *msg)
 {
@@ -876,7 +937,10 @@ static switch_status_t channel_receive_message(switch_core_session_t *session, s
                break;
        case ZAP_CHAN_TYPE_B:
                status = channel_receive_message_b(session, msg);
-               break;
+        break;
+       case ZAP_CHAN_TYPE_CAS:
+               status = channel_receive_message_cas(session, msg);
+        break;
        default:
                status = SWITCH_STATUS_FALSE;
                break;
@@ -1473,6 +1537,106 @@ static ZIO_SIGNAL_CB_FUNCTION(on_fxs_signal)
        return status;
 }
 
+static ZIO_SIGNAL_CB_FUNCTION(on_r2_signal)
+{
+       switch_core_session_t *session = NULL;
+       switch_channel_t *channel = NULL;
+       zap_status_t status = ZAP_SUCCESS;
+
+       zap_log(ZAP_LOG_DEBUG, "Got R2 channel sig [%s] in channel %d\n", zap_signal_event2str(sigmsg->event_id), sigmsg->channel->physical_chan_id);
+
+       switch(sigmsg->event_id) {
+               /* on_call_disconnect from the R2 side */
+               case ZAP_SIGEVENT_STOP: 
+               {       
+                       while((session = zap_channel_get_session(sigmsg->channel, 0))) {
+                               channel = switch_core_session_get_channel(session);
+                               switch_channel_hangup(channel, sigmsg->channel->caller_data.hangup_cause);
+                               zap_channel_clear_token(sigmsg->channel, switch_core_session_get_uuid(session));
+                               switch_core_session_rwunlock(session);
+                       }
+               }
+               break;
+
+               /* on_call_offered from the R2 side */
+               case ZAP_SIGEVENT_START: 
+               {
+                       status = zap_channel_from_event(sigmsg, &session);
+               }
+               break;
+
+               /* on DNIS received from the R2 forward side, return status == ZAP_BREAK to stop requesting DNIS */
+               case ZAP_SIGEVENT_COLLECTED_DIGIT: 
+               {
+                       char *regex = SPAN_CONFIG[sigmsg->channel->span->span_id].dial_regex;
+                       char *fail_regex = SPAN_CONFIG[sigmsg->channel->span->span_id].fail_dial_regex;
+
+                       if (switch_strlen_zero(regex)) {
+                               regex = NULL;
+                       }
+
+                       if (switch_strlen_zero(fail_regex)) {
+                               fail_regex = NULL;
+                       }
+
+                       zap_log(ZAP_LOG_DEBUG, "R2 DNIS so far [%s]\n", sigmsg->channel->caller_data.dnis.digits);
+
+                       if ((regex || fail_regex) && !switch_strlen_zero(sigmsg->channel->caller_data.dnis.digits)) {
+                               switch_regex_t *re = NULL;
+                               int ovector[30];
+                               int match = 0;
+
+                               if (fail_regex) {
+                                       match = switch_regex_perform(sigmsg->channel->caller_data.dnis.digits, fail_regex, &re, ovector, sizeof(ovector) / sizeof(ovector[0]));
+                                       status = match ? ZAP_SUCCESS : ZAP_BREAK;
+                                       switch_regex_safe_free(re);
+                               }
+
+                               if (status == ZAP_SUCCESS && regex) {
+                                       match = switch_regex_perform(sigmsg->channel->caller_data.dnis.digits, regex, &re, ovector, sizeof(ovector) / sizeof(ovector[0]));
+                                       status = match ? ZAP_BREAK : ZAP_SUCCESS;
+                               }
+
+                               switch_regex_safe_free(re);
+                       }
+               }
+               break;
+
+               case ZAP_SIGEVENT_PROGRESS:
+               {
+                       if ((session = zap_channel_get_session(sigmsg->channel, 0))) {
+                               channel = switch_core_session_get_channel(session);
+                               switch_channel_mark_ring_ready(channel);
+                               switch_core_session_rwunlock(session);
+                       }
+               }
+               break;
+
+               case ZAP_SIGEVENT_UP:
+               {
+                       if ((session = zap_channel_get_session(sigmsg->channel, 0))) {
+                               zap_tone_type_t tt = ZAP_TONE_DTMF;
+                               channel = switch_core_session_get_channel(session);
+                               switch_channel_mark_answered(channel);
+                               if (zap_channel_command(sigmsg->channel, ZAP_COMMAND_ENABLE_DTMF_DETECT, &tt) != ZAP_SUCCESS) {
+                                       zap_log(ZAP_LOG_ERROR, "Failed to enable DTMF detection in R2 channel %d:%d\n", sigmsg->channel->span_id, sigmsg->channel->chan_id);
+                               }
+                               switch_core_session_rwunlock(session);
+                       }
+               }
+               break;
+
+               default:
+               {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled event %d from R2 for channel %d:%d\n",
+                       sigmsg->event_id, sigmsg->channel->span_id, sigmsg->channel->chan_id);
+               }
+               break;
+       }
+
+       return status;
+}
+
 static ZIO_SIGNAL_CB_FUNCTION(on_clear_channel_signal)
 {
        switch_core_session_t *session = NULL;
@@ -2154,7 +2318,170 @@ static switch_status_t load_config(void)
                }
        }
 
+       if ((spans = switch_xml_child(cfg, "r2_spans"))) {
+               for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) {
+                       char *id = (char *) switch_xml_attr(myspan, "id");
+                       char *name = (char *) switch_xml_attr(myspan, "name");
+                       zap_status_t zstatus = ZAP_FAIL;
+
+                       /* strings */
+                       const char *variant = "itu";
+                       const char *category = "national_subscriber";
+                       const char *logdir = "/usr/local/freeswitch/log/"; /* FIXME: get PREFIX variable */
+                       const char *logging = "notice,warning,error";
+                       const char *advanced_protocol_file = "";
+
+                       /* booleans */
+                       int call_files = 0;
+                       int get_ani_first = -1;
+                       int immediate_accept = -1;
+                       int double_answer = -1;
+                       int skip_category = -1;
+                       int forced_release = -1;
+                       int charge_calls = -1;
+
+                       /* integers */
+                       int mfback_timeout = -1;
+                       int metering_pulse_timeout = -1;
+                       int allow_collect_calls = -1;
+                       int max_ani = 10;
+                       int max_dnis = 4;
+
+                       /* common non r2 stuff */
+                       const char *context = "default";
+                       const char *dialplan = "XML";
+                       char *dial_regex = NULL;
+                       char *fail_dial_regex = NULL;
+                       uint32_t span_id = 0;
+                       zap_span_t *span = NULL;
+
+
+                       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");
+
+                               /* string parameters */
+                               if (!strcasecmp(var, "variant")) {
+                                       variant = val;
+                               } else if (!strcasecmp(var, "category")) {
+                                       category = val;
+                               } else if (!strcasecmp(var, "logdir")) {
+                                       logdir = val;
+                               } else if (!strcasecmp(var, "logging")) {
+                                       logging = val;
+                               } else if (!strcasecmp(var, "advanced_protocol_file")) {
+                                       advanced_protocol_file = val;
+
+                               /* booleans */
+                               } else if (!strcasecmp(var, "allow_collect_calls")) {
+                                       allow_collect_calls = switch_true(val);
+                               } else if (!strcasecmp(var, "immediate_accept")) {
+                                       immediate_accept = switch_true(val);
+                               } else if (!strcasecmp(var, "double_answer")) {
+                                       double_answer = switch_true(val);
+                               } else if (!strcasecmp(var, "skip_category")) {
+                                       skip_category = switch_true(var);
+                               } else if (!strcasecmp(var, "forced_release")) {
+                                       forced_release = switch_true(val);
+                               } else if (!strcasecmp(var, "charge_calls")) {
+                                       charge_calls = switch_true(val);
+                               } else if (!strcasecmp(var, "get_ani_first")) {
+                                       get_ani_first = switch_true(val);
+                               } else if (!strcasecmp(var, "call_files")) {
+                                       call_files = switch_true(val);
+
+                               /* integers */
+                               } else if (!strcasecmp(var, "mfback_timeout")) {
+                                       mfback_timeout = atoi(val);
+                               } else if (!strcasecmp(var, "metering_pulse_timeout")) {
+                                       metering_pulse_timeout = atoi(val);
+                               } else if (!strcasecmp(var, "max_ani")) {
+                                       max_ani = atoi(val);
+                               } else if (!strcasecmp(var, "max_dnis")) {
+                                       max_dnis = atoi(val);
+
+                               /* common non r2 stuff */
+                               } 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;
+                               }
+                       }
+
+                       if (!id && !name) {
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n");
+                               continue;
+                       }
+
+                       if (name) {
+                               zstatus = zap_span_find_by_name(name, &span);
+                       } else {
+                               if (switch_is_number(id)) {
+                                       span_id = atoi(id);
+                                       zstatus = zap_span_find(span_id, &span);
+                               }
+
+                               if (zstatus != ZAP_SUCCESS) {
+                                       zstatus = zap_span_find_by_name(id, &span);
+                               }
+                       }
+
+                       if (zstatus != ZAP_SUCCESS) {
+                               zap_log(ZAP_LOG_ERROR, "Error finding OpenZAP span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name));
+                               continue;
+                       }
+
+                       if (!span_id) {
+                               span_id = span->span_id;
+                       }
+
+                       if (zap_configure_span("r2", span, on_r2_signal, 
+                               "variant", variant, 
+                               "max_ani", max_ani,
+                               "max_dnis", max_dnis,
+                               "category", category,
+                               "logdir", logdir,
+                               "logging", logging,
+                               "advanced_protocol_file", advanced_protocol_file,
+                               "allow_collect_calls", allow_collect_calls,
+                               "immediate_accept", immediate_accept,
+                               "double_answer", double_answer,
+                               "skip_category", skip_category,
+                               "forced_release", forced_release,
+                               "charge_calls", charge_calls,
+                               "get_ani_first", get_ani_first,
+                               "call_files", call_files,
+                               "mfback_timeout", mfback_timeout,
+                               "metering_pulse_timeout", metering_pulse_timeout, 
+                               TAG_END) != ZAP_SUCCESS) {
+                               zap_log(ZAP_LOG_ERROR, "Error configuring R2 OpenZAP span %d, error: %s\n", 
+                               span_id, span->last_error);
+                               continue;
+                       }
+
+                       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);
+                       }
 
+                       SPAN_CONFIG[span->span_id].span = span;
+                       switch_copy_string(SPAN_CONFIG[span->span_id].context, context, sizeof(SPAN_CONFIG[span->span_id].context));
+                       switch_copy_string(SPAN_CONFIG[span->span_id].dialplan, dialplan, sizeof(SPAN_CONFIG[span->span_id].dialplan));
+                       switch_copy_string(SPAN_CONFIG[span->span_id].type, "r2", sizeof(SPAN_CONFIG[span->span_id].type));
+
+                       if (zap_span_start(span) == ZAP_FAIL) {
+                               zap_log(ZAP_LOG_ERROR, "Error starting R2 OpenZAP span %d, error: %s\n", span_id, span->last_error);
+                               continue;
+                       }
+               }
+       }
 
        switch_xml_free(xml);
 
index 5600219d966f4923ae47f223b82d28917b9f13a9..6b0c703f23a235ad3b9d39ef6f2c46aa63ec0be3 100644 (file)
                }                                                                       \
        } while(0);
 
+#define zap_locked_wait_for_flag_cleared(obj, flag, time)                                              \
+       do {                                                                            \
+               int __safety = time;                                                    \
+               while(__safety-- && zap_test_flag(obj, flag)) { \
+                       zap_sleep(10);                                                  \
+               }                                                                       \
+               if(!__safety) {                                                         \
+                       zap_log(ZAP_LOG_CRIT, "flag %d was never cleared\n", flag);             \
+               }                                                                       \
+       } while(0);
+
 
 typedef enum {
        ZAP_STATE_CHANGE_FAIL,
@@ -517,7 +528,7 @@ struct zap_channel {
        struct zap_span *span;
        struct zap_io_interface *zio;
        zap_hash_t *variable_hash;
-       unsigned char cas_bits;
+       unsigned char rx_cas_bits;
 };
 
 
index 13e8c2a7a32ccdd4414fc21bd77e59b84b1eb147..713e67e3e8b54fef0eff49f8fc0138661a612d92 100644 (file)
@@ -175,7 +175,8 @@ typedef enum {
        ZAP_SIGTYPE_RBS,
        ZAP_SIGTYPE_ANALOG,
        ZAP_SIGTYPE_SS7BOOST,
-       ZAP_SIGTYPE_M3UA
+       ZAP_SIGTYPE_M3UA,
+       ZAP_SIGTYPE_R2
 } zap_signal_type_t;
 
 typedef enum {
@@ -279,6 +280,9 @@ typedef enum {
        ZAP_COMMAND_GET_RX_GAIN,
        ZAP_COMMAND_SET_TX_GAIN,
        ZAP_COMMAND_GET_TX_GAIN,
+       ZAP_COMMAND_FLUSH_TX_BUFFERS,
+       ZAP_COMMAND_FLUSH_RX_BUFFERS,
+       ZAP_COMMAND_FLUSH_BUFFERS,
        ZAP_COMMAND_COUNT
 } zap_command_t;
 
diff --git a/libs/freetdm/src/ozmod/ozmod_r2/ozmod_r2.c b/libs/freetdm/src/ozmod/ozmod_r2/ozmod_r2.c
new file mode 100644 (file)
index 0000000..0ffb61d
--- /dev/null
@@ -0,0 +1,1368 @@
+/*
+ * Copyright (c) 2009, 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 <stdio.h>
+#include <pthread.h>
+#include <openr2.h>
+#include "openzap.h"
+
+/* debug thread count for r2 legs */
+static zap_mutex_t* g_thread_count_mutex;
+static int32_t g_thread_count = 0;
+
+/* when the users kills a span we clear this flag to kill the signaling thread */
+/* FIXME: what about the calls that are already up-and-running? */
+typedef enum {
+       ZAP_R2_RUNNING = (1 << 0),
+} zap_r2_flag_t;
+
+/* private call information stored in zchan->call_data void* ptr */
+#define R2CALL(zchan) ((zap_r2_call_t*)((zchan)->call_data))
+typedef struct zap_r2_call_t {
+    openr2_chan_t *r2chan;
+       int accepted:1;
+       int answer_pending:1;
+       int state_ack_pending:1;
+       int disconnect_rcvd:1;
+       int zap_started:1;
+       zap_channel_state_t chanstate;
+       zap_size_t dnis_index;
+       zap_size_t ani_index;
+       char name[10];
+} zap_r2_call_t;
+
+/* this is just used as place holder in the stack when configuring the span to avoid using bunch of locals */
+typedef struct oz_r2_conf_s {
+       /* openr2 types */
+       openr2_variant_t variant;
+       openr2_calling_party_category_t category;
+       openr2_log_level_t loglevel;
+
+       /* strings */
+       char *logdir;
+       char *advanced_protocol_file;
+
+       /* ints */
+       int32_t max_ani;
+       int32_t max_dnis;
+       int32_t mfback_timeout; 
+       int32_t metering_pulse_timeout;
+
+       /* booleans */
+       int immediate_accept;
+       int skip_category;
+       int get_ani_first;
+       int call_files;
+       int double_answer;
+       int charge_calls;
+       int forced_release;
+       int allow_collect_calls;
+} oz_r2_conf_t;
+
+/* r2 configuration stored in span->signal_data */
+typedef struct zap_r2_data_s {
+       /* signaling callback */
+       zio_signal_cb_t sig_cb;
+       /* span flags */
+       zap_r2_flag_t flags;
+       /* openr2 handle for the R2 variant context */
+       openr2_context_t *r2context;
+       /* category to use when making calls */
+       openr2_calling_party_category_t category;
+       /* whether to use OR2_CALL_WITH_CHARGE or OR2_CALL_NO_CHARGE when accepting a call */
+       int charge_calls:1;
+       /* allow or reject collect calls */
+       int allow_collect_calls:1;
+       /* whether to use forced release when hanging up */
+       int forced_release:1;
+       /* whether accept the call when offered, or wait until the user decides to accept */
+       int accept_on_offer:1;
+} zap_r2_data_t;
+
+/* one element per span will be stored in g_mod_data_hash global var to keep track of them
+   and destroy them on module unload */
+typedef struct zap_r2_span_pvt_s {
+       openr2_context_t *r2context; /* r2 context allocated for this span */
+       zap_hash_t *r2calls; /* hash table of allocated call data per channel for this span */
+} zap_r2_span_pvt_t;
+
+/* span monitor thread */
+static void *zap_r2_run(zap_thread_t *me, void *obj);
+
+/* channel monitor thread */
+static void *zap_r2_channel_run(zap_thread_t *me, void *obj);
+
+/* hash of all the private span allocations
+   we need to keep track of them to destroy them when unloading the module 
+   since openzap does not notify signaling modules when destroying a span
+   span -> zap_r2_mod_allocs_t */
+static zap_hash_t *g_mod_data_hash;
+
+/* IO interface for the command API */
+static zap_io_interface_t g_zap_r2_interface;
+
+static void oz_r2_clean_call(zap_r2_call_t *call)
+{
+    openr2_chan_t *r2chan = call->r2chan;
+    memset(call, 0, sizeof(*call));
+    call->r2chan = r2chan;
+}
+
+static void oz_r2_accept_call(zap_channel_t *zchan)
+{
+       openr2_chan_t *r2chan = R2CALL(zchan)->r2chan;
+       // FIXME: not always accept as no charge, let the user decide that
+       // also we should check the return code from openr2_chan_accept_call and handle error condition
+       // hanging up the call with protocol error as the reason, this openr2 API will fail only when there something
+       // wrong at the I/O layer or the library itself
+       openr2_chan_accept_call(r2chan, OR2_CALL_NO_CHARGE);
+       R2CALL(zchan)->accepted = 1;
+}
+
+static void oz_r2_answer_call(zap_channel_t *zchan)
+{
+       openr2_chan_t *r2chan = R2CALL(zchan)->r2chan;
+       // FIXME
+       // 1. check openr2_chan_answer_call return code
+       // 2. The openr2_chan_answer_call_with_mode should be used depending on user settings
+       // openr2_chan_answer_call_with_mode(r2chan, OR2_ANSWER_SIMPLE);
+       openr2_chan_answer_call(r2chan);
+       R2CALL(zchan)->answer_pending = 0;
+}
+
+static ZIO_CHANNEL_OUTGOING_CALL_FUNCTION(r2_outgoing_call)
+{
+       zap_status_t status;
+       zap_mutex_lock(zchan->mutex);
+
+       /* the channel may be down but the thread not quite done */
+       zap_locked_wait_for_flag_cleared(zchan, ZAP_CHANNEL_INTHREAD, 200);
+
+       if (zap_test_flag(zchan, ZAP_CHANNEL_INTHREAD)) {
+               zap_log(ZAP_LOG_ERROR, "%d:%d Yay! R2 outgoing call in channel that is already in thread.\n", 
+                               zchan->span_id, zchan->chan_id);
+               zap_mutex_unlock(zchan->mutex);
+               return ZAP_FAIL;
+       }
+
+       oz_r2_clean_call(zchan->call_data);
+       R2CALL(zchan)->chanstate = ZAP_CHANNEL_STATE_DOWN;
+       zap_channel_set_state(zchan, ZAP_CHANNEL_STATE_DIALING, 0);
+       zap_set_flag(zchan, ZAP_CHANNEL_OUTBOUND);
+       R2CALL(zchan)->zap_started = 1;
+       zap_mutex_unlock(zchan->mutex);
+
+       status = zap_thread_create_detached(zap_r2_channel_run, zchan);
+       if (status == ZAP_FAIL) {
+               zap_log(ZAP_LOG_ERROR, "%d:%d Cannot handle request to start call in channel, failed to create thread!\n", 
+                               zchan->span_id, zchan->chan_id);
+               zap_channel_done(zchan);
+               return ZAP_FAIL;
+       }
+
+       return ZAP_SUCCESS;
+}
+
+static zap_status_t zap_r2_start(zap_span_t *span)
+{
+       zap_r2_data_t *r2_data = span->signal_data;
+       zap_set_flag(r2_data, ZAP_R2_RUNNING);
+       return zap_thread_create_detached(zap_r2_run, span);
+}
+
+/* always called from the monitor thread */
+static void zap_r2_on_call_init(openr2_chan_t *r2chan)
+{
+       zap_channel_t *zchan = openr2_chan_get_client_data(r2chan);
+       zap_status_t status;
+       zap_log(ZAP_LOG_NOTICE, "Received request to start call on chan %d\n", openr2_chan_get_number(r2chan));
+
+       zap_mutex_lock(zchan->mutex);
+
+       if (zchan->state != ZAP_CHANNEL_STATE_DOWN) {
+               zap_log(ZAP_LOG_ERROR, "Cannot handle request to start call in channel %d, invalid state (%d)\n", 
+                               openr2_chan_get_number(r2chan), zchan->state);
+               zap_mutex_unlock(zchan->mutex);
+               return;
+       }
+
+       /* the channel may be down but the thread not quite done */
+       zap_locked_wait_for_flag_cleared(zchan, ZAP_CHANNEL_INTHREAD, 200);
+
+       if (zap_test_flag(zchan, ZAP_CHANNEL_INTHREAD)) {
+               zap_log(ZAP_LOG_ERROR, "Cannot handle request to start call in channel %d, already in thread!\n", 
+                               openr2_chan_get_number(r2chan));
+               zap_mutex_unlock(zchan->mutex);
+               return;
+       }
+       oz_r2_clean_call(zchan->call_data);
+       R2CALL(zchan)->chanstate = ZAP_CHANNEL_STATE_DOWN;
+       zap_channel_set_state(zchan, ZAP_CHANNEL_STATE_COLLECT, 0);
+       zap_mutex_unlock(zchan->mutex);
+
+       status = zap_thread_create_detached(zap_r2_channel_run, zchan);
+       if (status == ZAP_FAIL) {
+               zap_log(ZAP_LOG_ERROR, "Cannot handle request to start call in channel %d, failed to create thread!\n", 
+                               openr2_chan_get_number(r2chan));
+       }
+}
+
+/* only called for incoming calls when the ANI, DNIS etc is complete and the user has to decide either to accept or reject the call */
+static void zap_r2_on_call_offered(openr2_chan_t *r2chan, const char *ani, const char *dnis, openr2_calling_party_category_t category)
+{
+       zap_sigmsg_t sigev;
+       zap_channel_t *zchan = openr2_chan_get_client_data(r2chan);
+       zap_r2_data_t *r2data = zchan->span->signal_data;
+
+       zap_log(ZAP_LOG_NOTICE, "Call offered on chan %d, ANI = %s, DNIS = %s, Category = %s\n", openr2_chan_get_number(r2chan), 
+                       ani, dnis, openr2_proto_get_category_string(category));
+
+       /* notify the user about the new call */
+       memset(&sigev, 0, sizeof(sigev));
+       sigev.chan_id = zchan->chan_id;
+       sigev.span_id = zchan->span_id;
+       sigev.channel = zchan;
+       sigev.event_id = ZAP_SIGEVENT_START;
+
+       if (r2data->sig_cb(&sigev) != ZAP_SUCCESS) {
+               zap_log(ZAP_LOG_NOTICE, "Failed to handle call offered on chan %d\n", openr2_chan_get_number(r2chan));
+               openr2_chan_disconnect_call(r2chan, OR2_CAUSE_OUT_OF_ORDER);
+               zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_CANCEL);
+               return; 
+       }
+       zap_channel_use(zchan);
+       R2CALL(zchan)->zap_started = 1; 
+}
+
+static void zap_r2_on_call_accepted(openr2_chan_t *r2chan, openr2_call_mode_t mode)
+{
+       zap_channel_t *zchan = openr2_chan_get_client_data(r2chan);
+       zap_log(ZAP_LOG_NOTICE, "Call accepted on chan %d\n", openr2_chan_get_number(r2chan));
+       /* at this point the MF signaling has ended and there is no point on keep reading */
+       openr2_chan_disable_read(r2chan);
+       if (OR2_DIR_BACKWARD == openr2_chan_get_direction(r2chan)) {
+               R2CALL(zchan)->state_ack_pending = 1;
+               if (R2CALL(zchan)->answer_pending) {
+                       zap_log(ZAP_LOG_DEBUG, "Answer was pending on chan %d, answering now.\n", openr2_chan_get_number(r2chan));
+                       oz_r2_answer_call(zchan);
+                       return;
+               }
+       } else {
+               zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_PROGRESS);
+       }
+}
+
+static void zap_r2_on_call_answered(openr2_chan_t *r2chan)
+{
+       zap_channel_t *zchan = openr2_chan_get_client_data(r2chan);
+       zap_log(ZAP_LOG_NOTICE, "Call answered on chan %d\n", openr2_chan_get_number(r2chan));
+       /* notify the upper layer of progress in the outbound call */
+       if (OR2_DIR_FORWARD == openr2_chan_get_direction(r2chan)) {
+               zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_UP);
+       }
+}
+
+/* may be called in the signaling or media thread depending on whether the hangup is product of MF or CAS signaling */
+static void zap_r2_on_call_disconnect(openr2_chan_t *r2chan, openr2_call_disconnect_cause_t cause)
+{
+       zap_sigmsg_t sigev;
+       zap_r2_data_t *r2data;
+       zap_channel_t *zchan = openr2_chan_get_client_data(r2chan);
+       zap_log(ZAP_LOG_NOTICE, "Call disconnected on chan %d\n", openr2_chan_get_number(r2chan));
+
+       zap_log(ZAP_LOG_DEBUG, "Got openr2 disconnection, clearing call on channel %d\n", zchan->physical_chan_id);
+
+       R2CALL(zchan)->disconnect_rcvd = 1;
+
+       /* acknowledge the hangup, cause will be ignored. From here to -> HANGUP once the openzap side hangs up as well */
+       openr2_chan_disconnect_call(r2chan, OR2_CAUSE_NORMAL_CLEARING);
+
+       /* if the call has not been started yet we must go to HANGUP right here */ 
+       if (!R2CALL(zchan)->zap_started) {
+               zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP);
+               return;
+       }
+
+       /* FIXME: use the cause received from openr2 and map it to zap cause  */
+       zchan->caller_data.hangup_cause  = ZAP_CAUSE_NORMAL_CLEARING; 
+
+       /* notify the user of the call terminating */
+       memset(&sigev, 0, sizeof(sigev));
+       sigev.chan_id = zchan->chan_id;
+       sigev.span_id = zchan->span_id;
+       sigev.channel = zchan;
+       sigev.event_id = ZAP_SIGEVENT_STOP;
+       r2data = zchan->span->signal_data;
+
+       r2data->sig_cb(&sigev);
+}
+
+static void zap_r2_on_call_end(openr2_chan_t *r2chan)
+{
+       zap_channel_t *zchan = openr2_chan_get_client_data(r2chan);
+       zap_log(ZAP_LOG_NOTICE, "Call finished on chan %d\n", openr2_chan_get_number(r2chan));
+       /* this means the openzap side disconnected the call, therefore we must move to DOWN here */
+       if (!R2CALL(zchan)->disconnect_rcvd) {
+               zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN);
+               return;
+       }
+}
+
+static void zap_r2_on_call_read(openr2_chan_t *r2chan, const unsigned char *buf, int buflen)
+{
+       zap_log(ZAP_LOG_NOTICE, "Call read data on chan %d\n", openr2_chan_get_number(r2chan));
+}
+
+static void zap_r2_on_hardware_alarm(openr2_chan_t *r2chan, int alarm)
+{
+       zap_log(ZAP_LOG_NOTICE, "Alarm on chan %d (%d)\n", openr2_chan_get_number(r2chan), alarm);
+}
+
+static void zap_r2_on_os_error(openr2_chan_t *r2chan, int errorcode)
+{
+       zap_log(ZAP_LOG_ERROR, "OS error on chan %d: %s\n", openr2_chan_get_number(r2chan), strerror(errorcode));
+}
+
+static void zap_r2_on_protocol_error(openr2_chan_t *r2chan, openr2_protocol_error_t reason)
+{
+       zap_sigmsg_t sigev;
+       zap_r2_data_t *r2data;
+       zap_channel_t *zchan = openr2_chan_get_client_data(r2chan);
+
+       zap_log(ZAP_LOG_ERROR, "Protocol error on chan %d\n", openr2_chan_get_number(r2chan));
+
+       R2CALL(zchan)->disconnect_rcvd = 1;
+
+       if (!R2CALL(zchan)->zap_started) {
+               zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP);
+               return;
+       }
+
+       zchan->caller_data.hangup_cause  = ZAP_CAUSE_PROTOCOL_ERROR; 
+
+       /* notify the user of the call terminating */
+       memset(&sigev, 0, sizeof(sigev));
+       sigev.chan_id = zchan->chan_id;
+       sigev.span_id = zchan->span_id;
+       sigev.channel = zchan;
+       sigev.event_id = ZAP_SIGEVENT_STOP;
+       r2data = zchan->span->signal_data;
+
+       r2data->sig_cb(&sigev);
+}
+
+static void zap_r2_on_line_blocked(openr2_chan_t *r2chan)
+{
+       zap_log(ZAP_LOG_NOTICE, "Far end blocked on chan %d\n", openr2_chan_get_number(r2chan));
+}
+
+static void zap_r2_on_line_idle(openr2_chan_t *r2chan)
+{
+       zap_log(ZAP_LOG_NOTICE, "Far end unblocked on chan %d\n", openr2_chan_get_number(r2chan));
+}
+
+static void zap_r2_write_log(openr2_log_level_t level, const char *message)
+{
+       switch (level) {
+               case OR2_LOG_NOTICE:
+                       zap_log(ZAP_LOG_NOTICE, "%s", message);
+                       break;
+               case OR2_LOG_WARNING:
+                       zap_log(ZAP_LOG_WARNING, "%s", message);
+                       break;
+               case OR2_LOG_ERROR:
+                       zap_log(ZAP_LOG_ERROR, "%s", message);
+                       break;
+               case OR2_LOG_STACK_TRACE:
+               case OR2_LOG_MF_TRACE:
+               case OR2_LOG_CAS_TRACE:
+               case OR2_LOG_DEBUG:
+               case OR2_LOG_EX_DEBUG:
+                       zap_log(ZAP_LOG_DEBUG, "%s", message);
+                       break;
+               default:
+                       zap_log(ZAP_LOG_WARNING, "We should handle logging level %d here.\n", level);
+                       zap_log(ZAP_LOG_DEBUG, "%s", message);
+                       break;
+       }
+}
+
+static void zap_r2_on_context_log(openr2_context_t *r2context, openr2_log_level_t level, const char *fmt, va_list ap)
+{
+#define CONTEXT_TAG "Context -"
+       char logmsg[256];
+       char completemsg[sizeof(logmsg) + sizeof(CONTEXT_TAG) - 1];
+       vsnprintf(logmsg, sizeof(logmsg), fmt, ap);
+       snprintf(completemsg, sizeof(completemsg), CONTEXT_TAG "%s", logmsg);
+       zap_r2_write_log(level, completemsg);
+#undef CONTEXT_TAG
+}
+
+static void zap_r2_on_chan_log(openr2_chan_t *r2chan, openr2_log_level_t level, const char *fmt, va_list ap)
+{
+#define CHAN_TAG "Chan "
+       char logmsg[256];
+       char completemsg[sizeof(logmsg) + sizeof(CHAN_TAG) - 1];
+       vsnprintf(logmsg, sizeof(logmsg), fmt, ap);
+       snprintf(completemsg, sizeof(completemsg), CHAN_TAG "%d: %s", openr2_chan_get_number(r2chan), logmsg);
+       zap_r2_write_log(level, completemsg);
+#undef CHAN_TAG
+}
+
+static int zap_r2_on_dnis_digit_received(openr2_chan_t *r2chan, char digit)
+{
+       zap_sigmsg_t sigev;
+       zap_r2_data_t *r2data;
+       zap_channel_t *zchan = openr2_chan_get_client_data(r2chan);
+       zap_size_t collected_len = R2CALL(zchan)->dnis_index;
+
+       zap_log(ZAP_LOG_DEBUG, "DNIS digit %d received chan %d\n", digit, openr2_chan_get_number(r2chan));
+
+       /* save the digit we just received */ 
+       zchan->caller_data.dnis.digits[collected_len] = digit;
+       collected_len++;
+       zchan->caller_data.dnis.digits[collected_len] = '\0';
+       R2CALL(zchan)->dnis_index = collected_len;
+
+       /* notify the user about the new digit and check if we should stop requesting more DNIS */
+       memset(&sigev, 0, sizeof(sigev));
+       sigev.chan_id = zchan->chan_id;
+       sigev.span_id = zchan->span_id;
+       sigev.channel = zchan;
+       sigev.event_id = ZAP_SIGEVENT_COLLECTED_DIGIT;
+       r2data = zchan->span->signal_data;
+       if (r2data->sig_cb(&sigev) == ZAP_BREAK) {
+               zap_log(ZAP_LOG_NOTICE, "Requested to stop getting DNIS. Current DNIS = %s on chan %d\n", zchan->caller_data.dnis.digits, openr2_chan_get_number(r2chan));
+               return OR2_STOP_DNIS_REQUEST; 
+       }
+
+       /* the only other reason to stop requesting DNIS is that there is no more room to save it */
+       if (collected_len == (sizeof(zchan->caller_data.dnis.digits) - 1)) {
+               zap_log(ZAP_LOG_NOTICE, "No more room for DNIS. Current DNIS = %s on chan %d\n", zchan->caller_data.dnis.digits, openr2_chan_get_number(r2chan));
+               return OR2_STOP_DNIS_REQUEST;
+       }
+
+       return OR2_CONTINUE_DNIS_REQUEST; 
+}
+
+static void zap_r2_on_ani_digit_received(openr2_chan_t *r2chan, char digit)
+{
+       zap_channel_t *zchan = openr2_chan_get_client_data(r2chan);
+       zap_size_t collected_len = R2CALL(zchan)->ani_index;
+
+       /* check if we should drop ANI */
+       if (collected_len == (sizeof(zchan->caller_data.ani.digits) - 1)) {
+               zap_log(ZAP_LOG_NOTICE, "No more room for ANI %c on chan %d, digit dropped.\n", digit, openr2_chan_get_number(r2chan));
+               return;
+       }
+       zap_log(ZAP_LOG_DEBUG, "ANI digit %c received chan %d\n", digit, openr2_chan_get_number(r2chan));
+
+       /* save the digit we just received */ 
+       zchan->caller_data.ani.digits[collected_len++] = digit;
+       zchan->caller_data.ani.digits[collected_len] = '\0';
+}
+
+static openr2_event_interface_t zap_r2_event_iface = {
+       .on_call_init = zap_r2_on_call_init,
+       .on_call_offered = zap_r2_on_call_offered,
+       .on_call_accepted = zap_r2_on_call_accepted,
+       .on_call_answered = zap_r2_on_call_answered,
+       .on_call_disconnect = zap_r2_on_call_disconnect,
+       .on_call_end = zap_r2_on_call_end,
+       .on_call_read = zap_r2_on_call_read,
+       .on_hardware_alarm = zap_r2_on_hardware_alarm,
+       .on_os_error = zap_r2_on_os_error,
+       .on_protocol_error = zap_r2_on_protocol_error,
+       .on_line_blocked = zap_r2_on_line_blocked,
+       .on_line_idle = zap_r2_on_line_idle,
+       /* cast seems to be needed to get rid of the annoying warning regarding format attribute  */
+       .on_context_log = (openr2_handle_context_logging_func)zap_r2_on_context_log,
+       .on_dnis_digit_received = zap_r2_on_dnis_digit_received,
+       .on_ani_digit_received = zap_r2_on_ani_digit_received,
+       /* so far we do nothing with billing pulses */
+       .on_billing_pulse_received = NULL
+};
+
+static int zap_r2_io_set_cas(openr2_chan_t *r2chan, int cas)
+{
+       zap_channel_t *zap_chan = openr2_chan_get_fd(r2chan);
+       zap_status_t status = zap_channel_command(zap_chan, ZAP_COMMAND_SET_CAS_BITS, &cas);
+       if (ZAP_FAIL == status) {
+               return -1;
+       }
+       return 0;
+}
+
+static int zap_r2_io_get_cas(openr2_chan_t *r2chan, int *cas)
+{
+       zap_channel_t *zap_chan = openr2_chan_get_fd(r2chan);
+       zap_status_t status = zap_channel_command(zap_chan, ZAP_COMMAND_GET_CAS_BITS, cas);
+       if (ZAP_FAIL == status) {
+               return -1;
+       }
+       return 0;
+}
+
+static int zap_r2_io_flush_write_buffers(openr2_chan_t *r2chan)
+{
+       zap_channel_t *zap_chan = openr2_chan_get_fd(r2chan);
+       zap_status_t status = zap_channel_command(zap_chan, ZAP_COMMAND_FLUSH_TX_BUFFERS, NULL);
+       if (ZAP_FAIL == status) {
+               return -1;
+       }
+       return 0;
+}
+
+static int zap_r2_io_write(openr2_chan_t *r2chan, const void *buf, int size)
+{
+       zap_channel_t *zap_chan = openr2_chan_get_fd(r2chan);
+       zap_size_t outsize = size;
+       zap_status_t status = zap_channel_write(zap_chan, (void *)buf, size, &outsize);
+       if (ZAP_FAIL == status) {
+               return -1;
+       }
+       return outsize;
+}
+
+static int zap_r2_io_read(openr2_chan_t *r2chan, const void *buf, int size)
+{
+       zap_channel_t *zap_chan = openr2_chan_get_fd(r2chan);
+       zap_size_t outsize = size;
+       zap_status_t status = zap_channel_read(zap_chan, (void *)buf, &outsize);
+       if (ZAP_FAIL == status) {
+               return -1;
+       }
+       return outsize;
+}
+
+static int zap_r2_io_wait(openr2_chan_t *r2chan, int *flags, int block)
+{
+       zap_status_t status;
+       zap_wait_flag_t zapflags = 0;
+
+       zap_channel_t *zap_chan = openr2_chan_get_fd(r2chan);
+       int32_t timeout = block ? -1 : 0;
+
+       if (*flags & OR2_IO_READ) {
+               zapflags |= ZAP_READ;
+       }
+       if (*flags & OR2_IO_WRITE) {
+               zapflags |= ZAP_WRITE;
+       }
+       if (*flags & OR2_IO_OOB_EVENT) {
+               zapflags |= ZAP_EVENTS;
+       }
+
+       status = zap_channel_wait(zap_chan, &zapflags, timeout);
+
+       if (ZAP_SUCCESS != status) {
+               return -1;
+       }
+
+       *flags = 0;
+       if (zapflags & ZAP_READ) {
+               *flags |= OR2_IO_READ;
+       }
+       if (zapflags & ZAP_WRITE) {
+               *flags |= OR2_IO_WRITE;
+       }
+       if (zapflags & ZAP_EVENTS) {
+               *flags |= OR2_IO_OOB_EVENT;
+       }
+
+       return 0;
+}
+
+/* The following openr2 hooks never get called, read on for reasoning ... */
+/* since openzap takes care of opening the file descriptor and using openr2_chan_new_from_fd, openr2 should never call this hook */
+static openr2_io_fd_t zap_r2_io_open(openr2_context_t *r2context, int channo)
+{
+       zap_log(ZAP_LOG_ERROR, "I should not be called (I/O open)!!\n");
+       return NULL;
+}
+
+/* since openzap takes care of closing the file descriptor and uses openr2_chan_new_from_fd, openr2 should never call this hook */
+static int zap_r2_io_close(openr2_chan_t *r2chan)
+{
+       zap_log(ZAP_LOG_ERROR, "I should not be called (I/O close)!!\n");
+       return 0;
+}
+
+/* since openzap takes care of opening the file descriptor and using openr2_chan_new_from_fd, openr2 should never call this hook */
+static int zap_r2_io_setup(openr2_chan_t *r2chan)
+{
+       zap_log(ZAP_LOG_ERROR, "I should not be called (I/O Setup)!!\n");
+       return 0;
+}
+
+/* since the signaling thread calls openr2_chan_process_cas_signaling directly, openr2 should never call this hook */
+static int zap_r2_io_get_oob_event(openr2_chan_t *r2chan, openr2_oob_event_t *event)
+{
+       *event = 0;
+       zap_log(ZAP_LOG_ERROR, "I should not be called (I/O get oob event)!!\n");
+       return 0;
+}
+
+static openr2_io_interface_t zap_r2_io_iface = {
+       .open = zap_r2_io_open, /* never called */
+       .close = zap_r2_io_close, /* never called */
+       .set_cas = zap_r2_io_set_cas,
+       .get_cas = zap_r2_io_get_cas,
+       .flush_write_buffers = zap_r2_io_flush_write_buffers,
+       .write = zap_r2_io_write,
+       .read = zap_r2_io_read,
+       .setup = zap_r2_io_setup, /* never called */
+       .wait = zap_r2_io_wait,
+       .get_oob_event = zap_r2_io_get_oob_event /* never called */
+};
+
+static ZIO_SIG_CONFIGURE_FUNCTION(zap_r2_configure_span)
+       //zap_status_t (zap_span_t *span, zio_signal_cb_t sig_cb, va_list ap)
+{
+       int i = 0;
+       int conf_failure = 0;
+       char *var = NULL;
+       char *val = NULL;
+       zap_r2_data_t *r2data = NULL;
+       zap_r2_span_pvt_t *spanpvt = NULL;
+       zap_r2_call_t *r2call = NULL;
+       openr2_chan_t *r2chan = NULL;
+
+       assert(sig_cb != NULL);
+
+       oz_r2_conf_t r2conf = 
+       {
+               .variant = OR2_VAR_ITU,
+               .category = OR2_CALLING_PARTY_CATEGORY_NATIONAL_SUBSCRIBER,
+               .loglevel = OR2_LOG_ERROR | OR2_LOG_WARNING,
+               .max_ani = 10,
+               .max_dnis = 4,
+               .mfback_timeout = -1,
+               .metering_pulse_timeout = -1,
+               .allow_collect_calls = -1,
+               .immediate_accept = -1,
+               .skip_category = -1,
+               .forced_release = -1,
+               .charge_calls = -1,
+               .get_ani_first = -1,
+               .call_files = -1,
+               .logdir = NULL,
+               .advanced_protocol_file = NULL 
+       };
+
+
+       if (span->signal_type) {
+               snprintf(span->last_error, sizeof(span->last_error), "Span is already configured for signalling.");
+               return ZAP_FAIL;
+       }
+
+       while ((var = va_arg(ap, char *))) {
+               zap_log(ZAP_LOG_DEBUG, "Reading R2 parameter %s for span %d\n", var, span->span_id);
+               if (!strcasecmp(var, "variant")) {
+                       if (!(val = va_arg(ap, char *))) {
+                               break;
+                       }
+                       if (zap_strlen_zero_buf(val)) {
+                               zap_log(ZAP_LOG_NOTICE, "Ignoring empty R2 variant parameter\n");
+                               continue;
+                       }
+                       r2conf.variant = openr2_proto_get_variant(val);
+                       if (r2conf.variant == OR2_VAR_UNKNOWN) {
+                               zap_log(ZAP_LOG_ERROR, "Unknown R2 variant %s\n", val);
+                               conf_failure = 1;
+                               break;
+                       }
+                       zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d for variant %s\n", span->span_id, val);
+               } else if (!strcasecmp(var, "category")) {
+                       if (!(val = va_arg(ap, char *))) {
+                               break;
+                       }
+                       if (zap_strlen_zero_buf(val)) {
+                               zap_log(ZAP_LOG_NOTICE, "Ignoring empty R2 category parameter\n");
+                               continue;
+                       }
+                       r2conf.category = openr2_proto_get_category(val);
+                       if (r2conf.category == OR2_CALLING_PARTY_CATEGORY_UNKNOWN) {
+                               zap_log(ZAP_LOG_ERROR, "Unknown R2 caller category %s\n", val);
+                               conf_failure = 1;
+                               break;
+                       }
+                       zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with default category %s\n", span->span_id, val);
+               } else if (!strcasecmp(var, "logdir")) {
+                       if (!(val = va_arg(ap, char *))) {
+                               break;
+                       }
+                       if (zap_strlen_zero_buf(val)) {
+                               zap_log(ZAP_LOG_NOTICE, "Ignoring empty R2 logdir parameter\n");
+                               continue;
+                       }
+                       r2conf.logdir = val;
+                       zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with logdir %s\n", span->span_id, val);
+               } else if (!strcasecmp(var, "logging")) {
+                       if (!(val = va_arg(ap, char *))) {
+                               break;
+                       }
+                       if (zap_strlen_zero_buf(val)) {
+                               zap_log(ZAP_LOG_NOTICE, "Ignoring empty R2 logging parameter\n");
+                               continue;
+                       }
+                       openr2_log_level_t tmplevel;
+                       char *clevel;
+                       char *logval = malloc(strlen(val)+1); /* alloca man page scared me, so better to use good ol' malloc  */
+                       if (!logval) {
+                               zap_log(ZAP_LOG_WARNING, "Ignoring R2 logging parameter: '%s', failed to alloc memory\n", val);
+                               continue;
+                       }
+                       strcpy(logval, val);
+                       while (logval) {
+                               clevel = strsep(&logval, ",");
+                               if (-1 == (tmplevel = openr2_log_get_level(clevel))) {
+                                       zap_log(ZAP_LOG_WARNING, "Ignoring invalid R2 logging level: '%s'\n", clevel);
+                                       continue;
+                               }
+                               r2conf.loglevel |= tmplevel;
+                               zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with loglevel %s\n", span->span_id, clevel);
+                       }
+                       free(logval);
+               } else if (!strcasecmp(var, "advanced_protocol_file")) {
+                       if (!(val = va_arg(ap, char *))) {
+                               break;
+                       }
+                       if (zap_strlen_zero_buf(val)) {
+                               zap_log(ZAP_LOG_NOTICE, "Ignoring empty R2 advanced_protocol_file parameter\n");
+                               continue;
+                       }
+                       r2conf.advanced_protocol_file = val;
+                       zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with advanced protocol file %s\n", span->span_id, val);
+               } else if (!strcasecmp(var, "allow_collect_calls")) {
+                       r2conf.allow_collect_calls = va_arg(ap, int);
+                       zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with allow collect calls max ani = %d\n", span->span_id, r2conf.allow_collect_calls);
+               } else if (!strcasecmp(var, "double_answer")) {
+                       r2conf.double_answer = va_arg(ap, int);
+                       zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with double answer = %d\n", span->span_id, r2conf.double_answer);
+               } else if (!strcasecmp(var, "immediate_accept")) {
+                       r2conf.immediate_accept = va_arg(ap, int);
+                       zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with immediate accept = %d\n", span->span_id, r2conf.immediate_accept);
+               } else if (!strcasecmp(var, "skip_category")) {
+                       r2conf.skip_category = va_arg(ap, int);
+                       zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with skip category = %d\n", span->span_id, r2conf.skip_category);
+               } else if (!strcasecmp(var, "forced_release")) {
+                       r2conf.forced_release = va_arg(ap, int);
+                       zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with forced release = %d\n", span->span_id, r2conf.forced_release);
+               } else if (!strcasecmp(var, "charge_calls")) {
+                       r2conf.charge_calls = va_arg(ap, int);
+                       zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with charge calls = %d\n", span->span_id, r2conf.charge_calls);
+               } else if (!strcasecmp(var, "get_ani_first")) {
+                       r2conf.get_ani_first = va_arg(ap, int);
+                       zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with get ani first = %d\n", span->span_id, r2conf.get_ani_first);
+               } else if (!strcasecmp(var, "call_files")) {
+                       r2conf.call_files = va_arg(ap, int);
+                       zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with call files = %d\n", span->span_id, r2conf.call_files);
+               } else if (!strcasecmp(var, "mfback_timeout")) {
+                       r2conf.mfback_timeout = va_arg(ap, int);
+                       zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with MF backward timeout = %dms\n", span->span_id, r2conf.mfback_timeout);
+               } else if (!strcasecmp(var, "metering_pulse_timeout")) {
+                       r2conf.metering_pulse_timeout = va_arg(ap, int);
+                       zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with metering pulse timeout = %dms\n", span->span_id, r2conf.metering_pulse_timeout);
+               } else if (!strcasecmp(var, "max_ani")) {
+                       r2conf.max_ani = va_arg(ap, int);
+                       zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with max ani = %d\n", span->span_id, r2conf.max_ani);
+               } else if (!strcasecmp(var, "max_dnis")) {
+                       r2conf.max_dnis = va_arg(ap, int);
+                       zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with max dnis = %d\n", span->span_id, r2conf.max_dnis);
+               } else {
+                       snprintf(span->last_error, sizeof(span->last_error), "Unknown R2 parameter [%s]", var);
+                       return ZAP_FAIL;
+               }
+       }
+
+       if (conf_failure) {
+               snprintf(span->last_error, sizeof(span->last_error), "R2 configuration error");
+               return ZAP_FAIL;
+       }
+
+       r2data = malloc(sizeof(*r2data));
+       if (!r2data) {
+               snprintf(span->last_error, sizeof(span->last_error), "Failed to allocate R2 data.");
+               return ZAP_FAIL;
+       }
+       memset(r2data, 0, sizeof(*r2data));
+
+       spanpvt = malloc(sizeof(*spanpvt));
+       if (!spanpvt) {
+               snprintf(span->last_error, sizeof(span->last_error), "Failed to allocate private span data container.");
+               goto fail;
+       }
+       memset(spanpvt, 0, sizeof(*spanpvt));
+
+       r2data->r2context = openr2_context_new(r2conf.variant, &zap_r2_event_iface, r2conf.max_ani, r2conf.max_dnis);
+       if (!r2data->r2context) {
+               snprintf(span->last_error, sizeof(span->last_error), "Cannot create openr2 context for span.");
+               goto fail;
+       }
+       openr2_context_set_io_type(r2data->r2context, OR2_IO_CUSTOM, &zap_r2_io_iface);
+       openr2_context_set_log_level(r2data->r2context, r2conf.loglevel);
+       openr2_context_set_ani_first(r2data->r2context, r2conf.get_ani_first);
+       openr2_context_set_skip_category_request(r2data->r2context, r2conf.skip_category);
+       openr2_context_set_mf_back_timeout(r2data->r2context, r2conf.mfback_timeout);
+       openr2_context_set_metering_pulse_timeout(r2data->r2context, r2conf.metering_pulse_timeout);
+       openr2_context_set_double_answer(r2data->r2context, r2conf.double_answer);
+       openr2_context_set_immediate_accept(r2data->r2context, r2conf.immediate_accept);
+       if (r2conf.logdir) {
+               openr2_context_set_log_directory(r2data->r2context, r2conf.logdir);
+       }
+       if (r2conf.advanced_protocol_file) {
+               openr2_context_configure_from_advanced_file(r2data->r2context, r2conf.advanced_protocol_file);
+       }
+
+       spanpvt->r2calls = create_hashtable(ZAP_MAX_CHANNELS_SPAN, zap_hash_hashfromstring, zap_hash_equalkeys);
+       if (!spanpvt->r2calls) {
+               snprintf(span->last_error, sizeof(span->last_error), "Cannot create channel calls hash for span.");
+               goto fail;
+       }
+
+       for (i = 1; (i <= span->chan_count) && (i <= ZAP_MAX_CHANNELS_SPAN); i++) {
+               r2chan = openr2_chan_new_from_fd(r2data->r2context, span->channels[i], span->channels[i]->physical_chan_id);
+               if (!r2chan) {
+                       snprintf(span->last_error, sizeof(span->last_error), "Cannot create all openr2 channels for span.");
+                       goto fail;
+               }
+               if (r2conf.call_files) {
+                       openr2_chan_enable_call_files(r2chan);
+                       openr2_chan_set_log_level(r2chan, r2conf.loglevel);
+               }
+
+               r2call = malloc(sizeof(*r2call));
+               if (!r2call) {
+                       snprintf(span->last_error, sizeof(span->last_error), "Cannot create all R2 call data structures for the span.");
+                       zap_safe_free(r2chan);
+                       goto fail;
+               }
+               memset(r2call, 0, sizeof(*r2call));
+               openr2_chan_set_logging_func(r2chan, zap_r2_on_chan_log);
+               openr2_chan_set_client_data(r2chan, span->channels[i]);
+        r2call->r2chan = r2chan;
+               span->channels[i]->call_data = r2call;
+               /* value and key are the same so just free one of them */
+               snprintf(r2call->name, sizeof(r2call->name), "chancall%d", i);
+               hashtable_insert(spanpvt->r2calls, (void *)r2call->name, r2call, HASHTABLE_FLAG_FREE_VALUE);
+
+       }
+       spanpvt->r2context = r2data->r2context;
+
+       /* just the value must be freed by the hash */
+       hashtable_insert(g_mod_data_hash, (void *)span->name, spanpvt, HASHTABLE_FLAG_FREE_VALUE);
+
+       span->start = zap_r2_start;
+       r2data->flags = 0;
+       r2data->sig_cb = sig_cb;
+       span->signal_type = ZAP_SIGTYPE_R2;
+       span->signal_data = r2data;
+       span->outgoing_call = r2_outgoing_call;
+
+       return ZAP_SUCCESS;
+
+fail:
+
+       if (r2data && r2data->r2context) {
+               openr2_context_delete(r2data->r2context);
+       }
+       if (spanpvt && spanpvt->r2calls) {
+               hashtable_destroy(spanpvt->r2calls);
+       }
+       zap_safe_free(r2data);
+       zap_safe_free(spanpvt);
+       return ZAP_FAIL;
+
+}
+
+static void *zap_r2_channel_run(zap_thread_t *me, void *obj)
+{
+       zap_channel_t *closed_chan;
+       uint32_t interval = 0;
+       zap_sigmsg_t sigev;
+       zap_channel_t *zchan = (zap_channel_t *)obj;
+       openr2_chan_t *r2chan = R2CALL(zchan)->r2chan;
+       zap_r2_data_t *r2data = zchan->span->signal_data;
+
+       zap_set_flag_locked(zchan, ZAP_CHANNEL_INTHREAD);
+
+       zap_mutex_lock(g_thread_count_mutex);
+       g_thread_count++;
+       zap_mutex_unlock(g_thread_count_mutex);
+
+       zap_log(ZAP_LOG_DEBUG, "R2 CHANNEL thread starting on %d in state %s.\n", 
+                       zchan->physical_chan_id,
+                       zap_channel_state2str(zchan->state));
+
+       if (zap_channel_open_chan(zchan) != ZAP_SUCCESS) {
+               zap_log(ZAP_LOG_ERROR, "OPEN ERROR [%s]\n", zchan->last_error);
+               goto endthread;
+       }
+
+       zap_channel_command(zchan, ZAP_COMMAND_GET_INTERVAL, &interval);
+
+       assert(interval != 0);
+       zap_log(ZAP_LOG_DEBUG, "Got %d interval for chan %d\n", interval, zchan->physical_chan_id);
+
+       if (!zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) {
+               /* FIXME: is this needed? */
+               memset(zchan->caller_data.dnis.digits, 0, sizeof(zchan->caller_data.collected));
+               memset(zchan->caller_data.ani.digits, 0, sizeof(zchan->caller_data.collected));
+       }
+
+       memset(&sigev, 0, sizeof(sigev));
+       sigev.chan_id = zchan->chan_id;
+       sigev.span_id = zchan->span_id;
+       sigev.channel = zchan;
+
+       while (zap_running()) {
+               int32_t read_enabled = openr2_chan_get_read_enabled(r2chan);
+               zap_wait_flag_t flags = read_enabled ? ( ZAP_READ | ZAP_WRITE ) : 0;
+
+               if (zap_test_flag(zchan, ZAP_CHANNEL_STATE_CHANGE) && (R2CALL(zchan)->chanstate != zchan->state)) {
+
+                       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));
+                       R2CALL(zchan)->chanstate = zchan->state;
+
+                       if (!zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND) && !R2CALL(zchan)->accepted &&
+                                       (zchan->state == ZAP_CHANNEL_STATE_PROGRESS ||
+                                        zchan->state == ZAP_CHANNEL_STATE_PROGRESS_MEDIA ||
+                                        zchan->state == ZAP_CHANNEL_STATE_UP) ) {
+                               /* if an accept ack will be required we should not acknowledge the state change just yet, 
+                                  it will be done below after processing the MF signals, otherwise we have a race condition between openzap calling
+                                  openr2_chan_answer_call and openr2 accepting the call first, if openzap calls openr2_chan_answer_call before the accept cycle
+                                  completes, openr2 will fail to answer the call */
+                               zap_log(ZAP_LOG_DEBUG, "State ack in chan %d:%d for state %s will have to wait a bit\n", zchan->span_id, zchan->chan_id, zap_channel_state2str(zchan->state));
+                       } else if (zchan->state != ZAP_CHANNEL_STATE_DOWN){
+                               /* the down state will be completed in zap_channel_done below */
+                               zap_clear_flag_locked(zchan, ZAP_CHANNEL_STATE_CHANGE);
+                               zap_channel_complete_state(zchan);
+                       }
+
+                       switch (zchan->state) {
+
+                               /* starting an incoming call */
+                               case ZAP_CHANNEL_STATE_COLLECT: 
+                                       {
+                                               zap_log(ZAP_LOG_DEBUG, "COLLECT: Starting processing of incoming call in channel %d with interval %d\n", zchan->physical_chan_id, interval);
+                                       }
+                                       break;
+
+                                       /* starting an outgoing call */
+                               case ZAP_CHANNEL_STATE_DIALING:
+                                       {
+                                               // FIXME: use user defined calling party
+                                               zap_channel_use(zchan);
+                                               zap_log(ZAP_LOG_DEBUG, "DIALING: Starting processing of outgoing call in channel %d with interval %d\n", zchan->physical_chan_id, interval);
+                                               if (openr2_chan_make_call(r2chan, zchan->caller_data.cid_num.digits, zchan->caller_data.ani.digits, OR2_CALLING_PARTY_CATEGORY_NATIONAL_SUBSCRIBER)) {
+                                                       zap_log(ZAP_LOG_ERROR, "%d:%d Failed to make call in R2 channel, openr2_chan_make_call failed\n", zchan->span_id, zchan->chan_id);
+                                                       zchan->caller_data.hangup_cause = ZAP_CAUSE_DESTINATION_OUT_OF_ORDER;
+                                                       zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP);
+                                               }
+                                       }
+                                       break;
+
+                                       /* the call is ringing */
+                               case ZAP_CHANNEL_STATE_PROGRESS:
+                               case ZAP_CHANNEL_STATE_PROGRESS_MEDIA:
+                                       {
+                                               if (!zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) {
+                                                       if (!R2CALL(zchan)->accepted) {
+                                                               zap_log(ZAP_LOG_DEBUG, "PROGRESS: Accepting call on channel %d\n", zchan->physical_chan_id);
+                                                               oz_r2_accept_call(zchan);
+                                                       } 
+                                               } else {
+                                                       zap_log(ZAP_LOG_DEBUG, "PROGRESS: Notifying progress in channel %d\n", zchan->physical_chan_id);
+                                                       sigev.event_id = ZAP_SIGEVENT_PROGRESS;
+                                                       if (r2data->sig_cb(&sigev) != ZAP_SUCCESS) {
+                                                               zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP);
+                                                       }
+                                               }
+                                       }
+                                       break;
+
+                                       /* the call was answered */
+                               case ZAP_CHANNEL_STATE_UP:
+                                       {
+                                               zap_log(ZAP_LOG_DEBUG, "UP: Call was answered on channel %d\n", zchan->physical_chan_id);
+                                               if (!zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) {
+                                                       if (!R2CALL(zchan)->accepted) {
+                                                               zap_log(ZAP_LOG_DEBUG, "UP: Call has not been accepted, need to accept first\n");
+                                                               // the answering will be done in the on_call_accepted handler
+                                                               oz_r2_accept_call(zchan);
+                                                               R2CALL(zchan)->answer_pending = 1;
+                                                       } else {
+                                                               oz_r2_answer_call(zchan);
+                                                       }
+                                               } else {
+                                                       zap_log(ZAP_LOG_DEBUG, "UP: Notifying of call answered in channel %d\n", zchan->physical_chan_id);
+                                                       sigev.event_id = ZAP_SIGEVENT_UP;
+                                                       if (r2data->sig_cb(&sigev) != ZAP_SUCCESS) {
+                                                               zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP);
+                                                       }
+                                               }
+                                       }
+                                       break;
+
+                                       /* just got hangup */
+                               case ZAP_CHANNEL_STATE_HANGUP: 
+                                       {
+                                               /* FIXME: the cause should be retrieved from zchan->caller_data.hangup_cause and translated from Q931 to R2 cause */
+                                               zap_log(ZAP_LOG_DEBUG, "HANGUP: Clearing call on channel %d\n", zchan->physical_chan_id);
+                                               if (!R2CALL(zchan)->disconnect_rcvd) {
+                                                       /* this will disconnect the call, but need to wait for the call end before moving to DOWN */
+                                                       openr2_chan_disconnect_call(r2chan, OR2_CAUSE_NORMAL_CLEARING);
+                                               } else {
+                                                       /* at this point on_call_end possibly was already called, 
+                                                        * but we needed to wait for the openzap confirmation before moving to DOWN */
+                                                       zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN);
+                                               }
+                                       }
+                                       break;
+
+                                       /* just got hangup from the openzap side due to abnormal failure */
+                               case ZAP_CHANNEL_STATE_CANCEL:
+                                       {
+                                               zap_log(ZAP_LOG_DEBUG, "CANCEL: Unable to receive call on channel %d\n", zchan->physical_chan_id);
+                                               openr2_chan_disconnect_call(r2chan, OR2_CAUSE_OUT_OF_ORDER);
+                                       }
+                                       break;
+
+                                       /* finished call for good */
+                               case ZAP_CHANNEL_STATE_DOWN: 
+                                       {
+                                               zap_log(ZAP_LOG_DEBUG, "DOWN: Placing channel %d back to the pool of available channels\n", zchan->physical_chan_id);
+                                               zap_channel_done(zchan);
+                                               goto endthread;
+                                       }
+                                       break;
+
+                               default:
+                                       {
+                                               zap_log(ZAP_LOG_ERROR, "%s: Unhandled channel state change in channel %d\n", zap_channel_state2str(zchan->state), zchan->physical_chan_id);
+                                       }
+                                       break;
+
+                       }
+               }
+
+               if (flags) {
+                       if (zap_channel_wait(zchan, &flags, interval * 2) != ZAP_SUCCESS) {
+                               zap_log(ZAP_LOG_DEBUG, "zap_channel_wait did not return ZAP_SUCCESS\n");
+                               continue;
+                       }
+
+                       /* handle timeout events first if any */
+                       openr2_chan_run_schedule(r2chan);
+
+                       /* openr2 will now try to detect MF tones, make sense out of them, reply if necessary with another tone and trigger 
+                        * telephony events via the call event interface we provided when creating the R2 context.
+                        * openr2 will also call our I/O callbacks to retrieve audio from the channel and call our wait poll I/O registered callback
+                        * and will not return from this function until the I/O poll callback returns no pending events
+                        * */
+                       openr2_chan_process_mf_signaling(r2chan);
+                       if (R2CALL(zchan)->state_ack_pending) {
+                               zap_clear_flag_locked(zchan, ZAP_CHANNEL_STATE_CHANGE);
+                               zap_channel_complete_state(zchan);
+                               R2CALL(zchan)->state_ack_pending = 0;
+                       }
+               } else {
+                       /* once the MF signaling has end we just loop here waiting for state changes */
+                       zap_sleep(interval);
+               }
+
+       }
+
+endthread:
+
+       closed_chan = zchan;
+       zap_channel_close(&closed_chan);
+       zap_clear_flag(zchan, ZAP_CHANNEL_INTHREAD);
+       zap_log(ZAP_LOG_DEBUG, "R2 channel %d thread ended.\n", zchan->physical_chan_id);
+
+       zap_mutex_lock(g_thread_count_mutex);
+       g_thread_count--;
+       zap_mutex_unlock(g_thread_count_mutex);
+
+       return NULL;
+}
+
+static void *zap_r2_run(zap_thread_t *me, void *obj)
+{
+       openr2_chan_t *r2chan;
+       zap_status_t status;
+       zap_span_t *span = (zap_span_t *) obj;
+       zap_r2_data_t *r2data = span->signal_data;
+       int waitms = 1000;
+       int i;
+
+       zap_log(ZAP_LOG_DEBUG, "OpenR2 monitor thread started.\n");
+       for (i = 1; i <= span->chan_count; i++) {
+               r2chan = R2CALL(span->channels[i])->r2chan;
+               openr2_chan_set_idle(r2chan);
+               openr2_chan_process_cas_signaling(r2chan);
+       }
+
+       while (zap_running() && zap_test_flag(r2data, ZAP_R2_RUNNING)) {
+               status = zap_span_poll_event(span, waitms);
+               if (ZAP_FAIL == status) {
+                       zap_log(ZAP_LOG_ERROR, "Failure Polling event! [%s]\n", span->last_error);
+                       continue;
+               }
+               if (ZAP_SUCCESS == status) {
+                       zap_event_t *event;
+            zap_log(ZAP_LOG_ERROR, "success getting poll event\n");
+                       while (zap_span_next_event(span, &event) == ZAP_SUCCESS) {
+                               if (event->enum_id == ZAP_OOB_CAS_BITS_CHANGE) {
+                    r2chan = R2CALL(event->channel)->r2chan;
+                                       zap_log(ZAP_LOG_DEBUG, "Handling CAS on channel %d.\n", openr2_chan_get_number(r2chan));
+                                       // we only expect CAS and other OOB events on this thread/loop, once a call is started
+                                       // the MF events (in-band signaling) are handled in the call thread
+                                       openr2_chan_process_cas_signaling(r2chan);
+                               } else {
+                                       zap_log(ZAP_LOG_DEBUG, "Ignoring event %d on channel %d.\n", event->enum_id, openr2_chan_get_number(r2chan));
+                                       // XXX TODO: handle alarms here XXX
+                               }
+                       }
+               } else if (status != ZAP_TIMEOUT) {
+                       zap_log(ZAP_LOG_ERROR, "zap_span_poll_event returned %d.\n", status);
+               } else {
+                       //zap_log(ZAP_LOG_DEBUG, "timed out waiting for event on span %d\n", span->span_id);
+               }
+       }
+
+    /*
+    FIXME: we should set BLOCKED but at this point I/O routines of openzap caused segfault
+       for (i = 1; i <= span->chan_count; i++) {
+               r2chan = R2CALL(span->channels[i])->r2chan;
+               openr2_chan_set_blocked(r2chan);
+       }
+    */
+
+       zap_clear_flag(r2data, ZAP_R2_RUNNING);
+       zap_log(ZAP_LOG_DEBUG, "R2 thread ending.\n");
+
+       return NULL;
+
+}
+
+static ZIO_API_FUNCTION(zap_r2_api)
+{
+       char *mycmd = NULL, *argv[10] = { 0 };
+       int argc = 0;
+
+       if (data) {
+               mycmd = strdup(data);
+               argc = zap_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
+       }
+
+       if (argc == 2) {
+               if (!strcasecmp(argv[0], "kill")) {
+                       int span_id = atoi(argv[1]);
+                       zap_span_t *span = NULL;
+
+                       if (zap_span_find_by_name(argv[1], &span) == ZAP_SUCCESS || zap_span_find(span_id, &span) == ZAP_SUCCESS) {
+                               zap_r2_data_t *r2data = span->signal_data;
+
+                               if (span->start != zap_r2_start) {
+                                       stream->write_function(stream, "-ERR invalid span.\n");
+                                       goto done;
+                               }
+
+                               zap_clear_flag(r2data, ZAP_R2_RUNNING);
+                               stream->write_function(stream, "+OK killed.\n");
+                               goto done;
+                       } else {
+                               stream->write_function(stream, "-ERR invalid span.\n");
+                               goto done;
+                       }
+               }
+
+               if (!strcasecmp(argv[0], "status")) {
+                       int span_id = atoi(argv[1]);
+                       zap_r2_data_t *r2data = NULL;
+                       zap_span_t *span = NULL;
+                       openr2_chan_t *r2chan = NULL;
+                       openr2_context_t *r2context = NULL;
+                       int i = 0;
+
+                       if (zap_span_find_by_name(argv[1], &span) == ZAP_SUCCESS || zap_span_find(span_id, &span) == ZAP_SUCCESS) {
+                               if (span->start != zap_r2_start) {
+                                       stream->write_function(stream, "-ERR not an R2 span.\n");
+                                       goto done;
+                               }
+                               if (!(r2data =  span->signal_data)) {
+                                       stream->write_function(stream, "-ERR invalid span. No R2 singal data in span.\n");
+                                       goto done;
+                               }
+                               r2context = r2data->r2context;
+                               openr2_variant_t r2variant = openr2_context_get_variant(r2context);
+                               stream->write_function(stream, 
+                                               "Variant: %s\n"
+                                               "Max ANI: %d\n"
+                                               "Max DNIS: %d\n"
+                                               "ANI First: %s\n"
+                                               "Immediate Accept: %s\n",
+                                               openr2_proto_get_variant_string(r2variant),
+                                               openr2_context_get_max_ani(r2context),
+                                               openr2_context_get_max_dnis(r2context),
+                                               openr2_context_get_ani_first(r2context) ? "Yes" : "No",
+                                               openr2_context_get_immediate_accept(r2context) ? "Yes" : "No");
+                               stream->write_function(stream, "\n");
+                               stream->write_function(stream, "%4s %-12.12s %-12.12s\n", "Channel", "Tx CAS", "Rx CAS");
+                               for (i = 1; i <= span->chan_count; i++) {
+                                       if (i == 16) continue;
+                                       r2chan = R2CALL(span->channels[i])->r2chan;
+                                       stream->write_function(stream, "%4d    %-12.12s %-12.12s\n", 
+                                                       span->channels[i]->physical_chan_id,
+                                                       openr2_chan_get_tx_cas_string(r2chan),
+                                                       openr2_chan_get_rx_cas_string(r2chan));
+                               }
+                               stream->write_function(stream, "\n");
+                               stream->write_function(stream, "+OK.\n");
+                               goto done;
+                       } else {
+                               stream->write_function(stream, "-ERR invalid span.\n");
+                               goto done;
+                       }
+               }
+
+       }
+
+       if (argc == 1) {
+               if (!strcasecmp(argv[0], "threads")) {
+                       zap_mutex_lock(g_thread_count_mutex);
+                       stream->write_function(stream, "%d R2 channel threads up\n", g_thread_count);
+                       zap_mutex_unlock(g_thread_count_mutex);
+                       stream->write_function(stream, "+OK.\n");
+                       goto done;
+               }
+
+               if (!strcasecmp(argv[0], "version")) {
+                       stream->write_function(stream, "OpenR2 version: %s, revision: %s\n", openr2_get_version(), openr2_get_revision());
+                       stream->write_function(stream, "+OK.\n");
+                       goto done;
+               }
+
+               if (!strcasecmp(argv[0], "variants")) {
+                       int32_t numvariants = 0;
+                       const openr2_variant_entry_t *variants = openr2_proto_get_variant_list(&numvariants);
+                       if (!variants) {
+                               stream->write_function(stream, "-ERR failed to retrieve openr2 variant list.\n");
+                               goto done;
+                       }
+#define VARIANT_FORMAT "%4s %40s\n"
+                       stream->write_function(stream, VARIANT_FORMAT, "Variant Code", "Country");
+                       numvariants--;
+                       for (; numvariants; numvariants--) {
+                               stream->write_function(stream, VARIANT_FORMAT, variants[numvariants].name, variants[numvariants].country);
+                       }
+                       stream->write_function(stream, "+OK.\n");
+#undef VARIANT_FORMAT
+                       goto done;
+               }
+       }
+
+       stream->write_function(stream, "-ERR invalid command.\n");
+
+done:
+
+       zap_safe_free(mycmd);
+
+       return ZAP_SUCCESS;
+
+}
+
+static ZIO_IO_LOAD_FUNCTION(zap_r2_io_init)
+{
+       assert(zio != NULL);
+       memset(&g_zap_r2_interface, 0, sizeof(g_zap_r2_interface));
+
+       g_zap_r2_interface.name = "r2";
+       g_zap_r2_interface.api = zap_r2_api;
+
+       *zio = &g_zap_r2_interface;
+
+       return ZAP_SUCCESS;
+}
+
+static ZIO_SIG_LOAD_FUNCTION(zap_r2_init)
+{
+       g_mod_data_hash = create_hashtable(10, zap_hash_hashfromstring, zap_hash_equalkeys);
+       if (!g_mod_data_hash) {
+               return ZAP_FAIL;
+       }
+       zap_mutex_create(&g_thread_count_mutex);
+       return ZAP_SUCCESS;
+}
+
+static ZIO_SIG_UNLOAD_FUNCTION(zap_r2_destroy)
+{
+       zap_hash_iterator_t *i = NULL;
+       zap_r2_span_pvt_t *spanpvt = NULL;
+       const void *key = NULL;
+       void *val = NULL;
+       for (i = hashtable_first(g_mod_data_hash); i; i = hashtable_next(i)) {
+               hashtable_this(i, &key, NULL, &val);
+               if (key && val) {
+                       spanpvt = val;
+                       openr2_context_delete(spanpvt->r2context);
+                       hashtable_destroy(spanpvt->r2calls);
+               }
+       }
+       hashtable_destroy(g_mod_data_hash);
+       zap_mutex_destroy(&g_thread_count_mutex);
+       return ZAP_SUCCESS;
+}
+
+zap_module_t zap_module = { 
+       "r2",
+       zap_r2_io_init,
+       NULL,
+       zap_r2_init,
+       zap_r2_configure_span,
+       zap_r2_destroy
+};
+       
+
+/* 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
+ */
index c45e5f295bd6026e5e1c5bb6c342e8abd0a76501..241d180a373a118a71d13f31f887746feb5d5ff5 100644 (file)
@@ -202,7 +202,7 @@ static unsigned wp_open_range(zap_span_t *span, unsigned spanno, unsigned start,
        unsigned configured = 0, x;
 
        if (type == ZAP_CHAN_TYPE_CAS) {
-               zap_log(ZAP_LOG_DEBUG, "Configuring CAS channels with abcd == 0x%X\n", cas_bits);
+               zap_log(ZAP_LOG_DEBUG, "Configuring Wanpipe CAS channels with abcd == 0x%X\n", cas_bits);
        }       
        for(x = start; x < end; x++) {
                zap_channel_t *chan;
@@ -210,22 +210,25 @@ static unsigned wp_open_range(zap_span_t *span, unsigned spanno, unsigned start,
                const char *dtmf = "none";
 
                sockfd = tdmv_api_open_span_chan(spanno, x);
+               if (sockfd == WP_INVALID_SOCKET) {
+                       zap_log(ZAP_LOG_ERROR, "Failed to open wanpipe device span %d channel %d\n", spanno, x);
+                       continue;
+               }
                
-               if (sockfd != WP_INVALID_SOCKET && zap_span_add_channel(span, sockfd, type, &chan) == ZAP_SUCCESS) {
+               if (zap_span_add_channel(span, sockfd, type, &chan) == ZAP_SUCCESS) {
                        wanpipe_tdm_api_t tdm_api;
+                       memset(&tdm_api, 0, sizeof(tdm_api));
 #ifdef LIBSANGOMA_VERSION
-            sangoma_status_t sangstatus;
+                       sangoma_status_t sangstatus;
                        sangoma_wait_obj_t *sangoma_wait_obj;
 
-            sangstatus = sangoma_wait_obj_create(&sangoma_wait_obj, sockfd, SANGOMA_DEVICE_WAIT_OBJ);
-            if (sangstatus != SANG_STATUS_SUCCESS) {
-                           zap_log(ZAP_LOG_ERROR, "failure create waitable object for s%dc%d\n", spanno, x);
-                continue;
-            }
+                       sangstatus = sangoma_wait_obj_create(&sangoma_wait_obj, sockfd, SANGOMA_DEVICE_WAIT_OBJ);
+                       if (sangstatus != SANG_STATUS_SUCCESS) {
+                               zap_log(ZAP_LOG_ERROR, "failure create waitable object for s%dc%d\n", spanno, x);
+                               continue;
+                       }
                        chan->mod_data = sangoma_wait_obj;
 #endif
-
-                       memset(&tdm_api,0,sizeof(tdm_api));
                        
                        chan->physical_span_id = spanno;
                        chan->physical_chan_id = x;
@@ -237,7 +240,7 @@ static unsigned wp_open_range(zap_span_t *span, unsigned spanno, unsigned start,
                                dtmf = "software";
 
                                /* FIXME: Handle Error Condition Check for return code */
-                               err= sangoma_tdm_get_hw_coding(chan->sockfd, &tdm_api);
+                               err = sangoma_tdm_get_hw_coding(chan->sockfd, &tdm_api);
 
                                if (tdm_api.wp_tdm_cmd.hw_tdm_coding) {
                                        chan->native_codec = chan->effective_codec = ZAP_CODEC_ALAW;
@@ -270,15 +273,26 @@ static unsigned wp_open_range(zap_span_t *span, unsigned spanno, unsigned start,
                        if (type == ZAP_CHAN_TYPE_CAS || 
                                ((span->trunk_type == ZAP_TRUNK_T1 || span->trunk_type == ZAP_TRUNK_E1) && type != ZAP_CHAN_TYPE_B)) {
 #ifdef LIBSANGOMA_VERSION
-                               sangoma_tdm_write_rbs(chan->sockfd,&tdm_api,chan->physical_chan_id,wanpipe_swap_bits(cas_bits));
+                               sangoma_tdm_write_rbs(chan->sockfd,&tdm_api,chan->physical_chan_id, wanpipe_swap_bits(cas_bits));
+
+                               /* this should probably be done for old libsangoma but I am not sure if the API is available and I'm lazy to check,
+                                  The poll rate is hard coded to 100 per second (done in the driver, is the max rate of polling allowed by wanpipe)
+                                */
+                               if (sangoma_tdm_enable_rbs_events(chan->sockfd, &tdm_api, 100)) {
+                                       zap_log(ZAP_LOG_ERROR, "Failed to enable RBS/CAS events in device %d:%d fd:%d\n", chan->span_id, chan->chan_id, sockfd);
+                                       continue;
+                               }
+                               /* probably done by the driver but lets write defensive code this time */
+                               sangoma_flush_bufs(chan->sockfd, &tdm_api);
 #else
-                               sangoma_tdm_write_rbs(chan->sockfd,&tdm_api,wanpipe_swap_bits(cas_bits));
+                               sangoma_tdm_write_rbs(chan->sockfd,&tdm_api, wanpipe_swap_bits(cas_bits));
 #endif
                        }
                        
                        if (!zap_strlen_zero(name)) {
                                zap_copy_string(chan->chan_name, name, sizeof(chan->chan_name));
                        }
+
                        if (!zap_strlen_zero(number)) {
                                zap_copy_string(chan->chan_number, number, sizeof(chan->chan_number));
                        }
@@ -288,7 +302,7 @@ static unsigned wp_open_range(zap_span_t *span, unsigned spanno, unsigned start,
                                spanno, x, chan->span_id, chan->chan_id, sockfd, dtmf);
 
                } else {
-                       zap_log(ZAP_LOG_ERROR, "failure configuring device s%dc%d\n", spanno, x);
+                       zap_log(ZAP_LOG_ERROR, "zap_span_add_channel failed for wanpipe span %d channel %d\n", spanno, x);
                }
        }
        
@@ -371,15 +385,13 @@ static ZIO_CONFIGURE_SPAN_FUNCTION(wanpipe_configure_span)
                }
 
                if (!(sp && ch)) {
-                       zap_log(ZAP_LOG_ERROR, "Invalid input\n");
+                       zap_log(ZAP_LOG_ERROR, "No valid wanpipe span and channel was specified\n");
                        continue;
                }
 
-
                channo = atoi(ch);
                spanno = atoi(sp);
 
-
                if (channo < 0) {
                        zap_log(ZAP_LOG_ERROR, "Invalid channel number %d\n", channo);
                        continue;
@@ -528,16 +540,24 @@ static ZIO_COMMAND_FUNCTION(wanpipe_command)
        case ZAP_COMMAND_SET_CAS_BITS:
                {
 #ifdef LIBSANGOMA_VERSION
-                       err=sangoma_tdm_write_rbs(zchan->sockfd,&tdm_api,zchan->physical_chan_id,wanpipe_swap_bits(ZAP_COMMAND_OBJ_INT));
+                       err = sangoma_tdm_write_rbs(zchan->sockfd,&tdm_api, zchan->physical_chan_id, wanpipe_swap_bits(ZAP_COMMAND_OBJ_INT));
 #else
-                       err=sangoma_tdm_write_rbs(zchan->sockfd,&tdm_api,wanpipe_swap_bits(ZAP_COMMAND_OBJ_INT));
+                       err = sangoma_tdm_write_rbs(zchan->sockfd, &tdm_api, wanpipe_swap_bits(ZAP_COMMAND_OBJ_INT));
 #endif
                }
                break;
        case ZAP_COMMAND_GET_CAS_BITS:
                {
-                       /* wanpipe does not has a command to get the CAS bits so we emulate it */
-                       ZAP_COMMAND_OBJ_INT = zchan->cas_bits;
+#ifdef LIBSANGOMA_VERSION
+            unsigned char rbsbits;
+            err = sangoma_tdm_read_rbs(zchan->sockfd, &tdm_api, zchan->physical_chan_id, &rbsbits);
+            if (!err) {
+                ZAP_COMMAND_OBJ_INT = wanpipe_swap_bits(rbsbits);
+            }
+#else
+            // does sangoma_tdm_read_rbs is available here?
+                       ZAP_COMMAND_OBJ_INT = zchan->rx_cas_bits;
+#endif
                }
                break;
        default:
@@ -817,7 +837,6 @@ ZIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_next_event)
 {
        uint32_t i,err;
        zap_oob_event_t event_id;
-       
        for(i = 1; i <= span->chan_count; i++) {
                if (span->channels[i]->last_event_time && !zap_test_flag(span->channels[i], ZAP_CHANNEL_EVENT)) {
                        uint32_t diff = (uint32_t)(zap_current_time_in_ms() - span->channels[i]->last_event_time);
@@ -856,7 +875,7 @@ ZIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_next_event)
                        memset(&tdm_api, 0, sizeof(tdm_api));
                        zap_clear_flag(span->channels[i], ZAP_CHANNEL_EVENT);
 
-                       err=sangoma_tdm_read_event(zchan->sockfd,&tdm_api);
+                       err = sangoma_tdm_read_event(zchan->sockfd, &tdm_api);
                        if (err != ZAP_SUCCESS) {
                                snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno));
                                return ZAP_FAIL;
@@ -926,9 +945,7 @@ ZIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_next_event)
                        case WP_TDMAPI_EVENT_RBS:
                                {
                                        event_id = ZAP_OOB_CAS_BITS_CHANGE;
-                                       /* save the CAS bits, user should retrieve it with ZAP_COMMAND_GET_CAS_BITS 
-                                          is there a best play to store this? instead of adding cas_bits member to zap_chan? */
-                                       span->channels[i]->cas_bits = wanpipe_swap_bits(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_rbs_bits);
+                                       span->channels[i]->rx_cas_bits = wanpipe_swap_bits(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_rbs_bits);
                                }
                                break;
             case WP_TDMAPI_EVENT_DTMF:
index a8ce338a63dad96415a271afbb807e4d92fb26db..9e4369334bcb5644039549caab5caf3f32f68261 100644 (file)
@@ -442,7 +442,7 @@ static ZIO_CONFIGURE_SPAN_FUNCTION(zt_configure_span)
 {
 
        int items, i;
-        char *mydata, *item_list[10];
+       char *mydata, *item_list[10];
        char *ch, *mx;
        unsigned char cas_bits = 0;
        int channo;
@@ -777,10 +777,9 @@ static ZIO_COMMAND_FUNCTION(zt_command)
                                zchan->packet_len = len;
                                zchan->effective_interval = zchan->native_interval = zchan->packet_len / 8;
 
-                if (zchan->effective_codec == ZAP_CODEC_SLIN) {
-                    zchan->packet_len *= 2;
-                }
-
+                               if (zchan->effective_codec == ZAP_CODEC_SLIN) {
+                                       zchan->packet_len *= 2;
+                               }
                        }
                }
                break;
@@ -792,21 +791,42 @@ static ZIO_COMMAND_FUNCTION(zt_command)
                break;
        case ZAP_COMMAND_GET_CAS_BITS:
                {
-                       /* probably we should call ZT_GETRXBITS instead? */
-                       ZAP_COMMAND_OBJ_INT = zchan->cas_bits;
+                       err = ioctl(zchan->sockfd, codes.GETRXBITS, &zchan->rx_cas_bits);
+                       if (!err) {
+                               ZAP_COMMAND_OBJ_INT = zchan->rx_cas_bits;
+                       }
+               }
+               break;
+       case ZAP_COMMAND_FLUSH_TX_BUFFERS:
+               {
+                       int flushmode = ZT_FLUSH_WRITE;
+                       err = ioctl(zchan->sockfd, codes.FLUSH, &flushmode);
+               }
+               break;
+       case ZAP_COMMAND_FLUSH_RX_BUFFERS:
+               {
+                       int flushmode = ZT_FLUSH_READ;
+                       err = ioctl(zchan->sockfd, codes.FLUSH, &flushmode);
+               }
+               break;
+       case ZAP_COMMAND_FLUSH_BUFFERS:
+               {
+                       int flushmode = ZT_FLUSH_BOTH;
+                       err = ioctl(zchan->sockfd, codes.FLUSH, &flushmode);
                }
                break;
        default:
+               err = ZAP_NOTIMPL;
                break;
        };
 
-       if (err) {
+       if (err && err != ZAP_NOTIMPL) {
                snprintf(zchan->last_error, sizeof(zchan->last_error), "%s", strerror(errno));
                return ZAP_FAIL;
        }
 
 
-       return ZAP_SUCCESS;
+       return err == 0 ? ZAP_SUCCESS : err;
 }
 
 /**
@@ -1019,7 +1039,7 @@ ZIO_SPAN_NEXT_EVENT_FUNCTION(zt_next_event)
                                        if (err) {
                                                return ZAP_FAIL;
                                        }
-                                       span->channels[i]->cas_bits = bits;
+                                       span->channels[i]->rx_cas_bits = bits;
                                }
                                break;
                        default:
diff --git a/libs/freetdm/src/testr2.c b/libs/freetdm/src/testr2.c
new file mode 100644 (file)
index 0000000..f5f093c
--- /dev/null
@@ -0,0 +1,86 @@
+#include "openzap.h"
+#include <signal.h>
+
+static int R = 0;
+static zap_mutex_t *mutex = NULL;
+
+static ZIO_SIGNAL_CB_FUNCTION(on_r2_signal)
+{
+    zap_log(ZAP_LOG_DEBUG, "Got R2 channel sig [%s] in channel\n", zap_signal_event2str(sigmsg->event_id), sigmsg->channel->physical_chan_id);
+    return ZAP_SUCCESS;
+}
+
+static void handle_SIGINT(int sig)
+{
+       zap_mutex_lock(mutex);
+       R = 0;
+       zap_mutex_unlock(mutex);
+       return;
+}
+
+int main(int argc, char *argv[])
+{
+       zap_span_t *span;
+       zap_mutex_create(&mutex);
+
+       zap_global_set_default_logger(ZAP_LOG_LEVEL_DEBUG);
+
+       if (argc < 2) {
+               printf("umm no\n");
+               exit(-1);
+       }
+
+       if (zap_global_init() != ZAP_SUCCESS) {
+               fprintf(stderr, "Error loading OpenZAP\n");
+               exit(-1);
+       }
+
+       printf("OpenZAP loaded\n");
+
+       if (zap_span_find(atoi(argv[1]), &span) != ZAP_SUCCESS) {
+               fprintf(stderr, "Error finding OpenZAP span\n");
+               goto done;
+       }
+       
+
+
+       if (zap_configure_span("r2", span, on_r2_signal,
+                                                  "variant", "mx",
+                                                  "max_ani", 10,
+                                                  "max_dnis", 4,
+                                                  "logging", "all",
+                                                  TAG_END) == ZAP_SUCCESS) {
+                                                  
+
+               zap_span_start(span);
+       } else {
+               fprintf(stderr, "Error starting R2 span\n");
+               goto done;
+       }
+
+       signal(SIGINT, handle_SIGINT);
+       zap_mutex_lock(mutex);
+       R = 1;
+       zap_mutex_unlock(mutex);
+       while(R) {
+               zap_sleep(1 * 1000);
+       }
+
+ done:
+
+       zap_global_destroy();
+
+       return 1;
+
+}
+
+/* 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:
+ */
index 32567041eb482d459f49e74856c7f5069ca3bb71..05d5db3c61a86530e9f38662c8d86929ddd03762 100644 (file)
@@ -216,14 +216,14 @@ OZ_DECLARE (int) zap_config_get_cas_bits(char *strvalue, unsigned char *outbits)
        int x = 0;
        char *double_colon = strchr(strvalue, ':');
        if (!double_colon) {
-               zap_log(ZAP_LOG_ERROR, "No CAS bits specified: %s, :xxxx definition expected, where x is 1 or 0\n", double_colon);
+               zap_log(ZAP_LOG_ERROR, "No CAS bits specified: %s, :xxxx definition expected, where x is 1 or 0\n", strvalue);
                return -1;
        }
        double_colon++;
        *outbits = 0;
        cas_bits[4] = 0;
        if (sscanf(double_colon, "%c%c%c%c", &cas_bits[0], &cas_bits[1], &cas_bits[2], &cas_bits[3]) != 4) {
-               zap_log(ZAP_LOG_ERROR, "Invalid CAS bits specified: %s, :xxxx definition expected, where x is 1 or 0\n", double_colon);
+               zap_log(ZAP_LOG_ERROR, "Invalid CAS bits specified: '%s', :xxxx definition expected, where x is 1 or 0\n", double_colon);
                return -1;
        }
        zap_log(ZAP_LOG_DEBUG, "CAS bits specification found: %s\n", cas_bits);
index 4b62dd31534d2dc5374a9d6909860ec642f757a1..a20ea0ccf9bbae2ebae8879c5c28ef2409118b1d 100644 (file)
@@ -1522,14 +1522,17 @@ OZ_DECLARE(zap_status_t) zap_channel_command(zap_channel_t *zchan, zap_command_t
 
        if (!zchan->zio->command) {
                snprintf(zchan->last_error, sizeof(zchan->last_error), "method not implemented");
-               zap_log(ZAP_LOG_ERROR, "no commnand functon!\n");       
+               zap_log(ZAP_LOG_ERROR, "no command function defined by the I/O openzap module!\n");     
                GOTO_STATUS(done, ZAP_FAIL);
        }
 
     status = zchan->zio->command(zchan, command, obj);
 
-
- done:
+       if (status == ZAP_NOTIMPL) {
+               snprintf(zchan->last_error, sizeof(zchan->last_error), "I/O command %d not implemented in backend", command);
+               zap_log(ZAP_LOG_ERROR, "I/O backend does not support command %d!\n", command);  
+       }
+done:
        zap_mutex_unlock(zchan->mutex);
        return status;