提交 68c889ce authored 作者: Moises Silva's avatar Moises Silva

added initial OpenR2 support

git-svn-id: http://svn.openzap.org/svn/openzap/trunk@808 a93c3328-9c30-0410-af19-c9cd2b2d52af
上级 1dda64ab
......@@ -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
......
......@@ -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
......
......@@ -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);
......
......@@ -280,6 +280,17 @@
} \
} 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;
};
......
......@@ -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;
......
/*
* 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
*/
......@@ -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:
......
......@@ -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:
......
#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:
*/
......@@ -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);
......
......@@ -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;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论