Index: channels/chan_zap.c
===================================================================
--- channels/chan_zap.c (revision 114097)
+++ channels/chan_zap.c (working copy)
@@ -44,6 +44,7 @@
tonezone
+
***/
#include "asterisk.h"
@@ -69,6 +70,10 @@
#include
#endif
+#ifdef HAVE_OPENR2
+#include
+#endif
+
#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
@@ -99,6 +104,7 @@
#include "asterisk/astobj.h"
#include "asterisk/event.h"
#include "asterisk/devicestate.h"
+#include "asterisk/paths.h"
#define SMDI_MD_WAIT_TIMEOUT 1500 /* 1.5 seconds */
@@ -197,6 +203,7 @@
#define SIG_BRI (0x2000000 | ZT_SIG_CLEAR)
#define SIG_BRI_PTMP (0X4000000 | ZT_SIG_CLEAR)
#define SIG_SS7 (0x1000000 | ZT_SIG_CLEAR)
+#define SIG_MFCR2 (0x1000000 | ZT_SIG_CAS)
#define SIG_SF ZT_SIG_SF
#define SIG_SFWINK (0x0100000 | ZT_SIG_SF)
#define SIG_SF_FEATD (0x0200000 | ZT_SIG_SF)
@@ -378,6 +385,43 @@
static int cur_defaultdpc = -1;
#endif /* HAVE_SS7 */
+#ifdef HAVE_OPENR2
+
+struct zt_mf_tx_state {
+ openr2_chan_t *r2chan;
+ int forward;
+ int signal;
+ int generate;
+};
+
+struct zt_mf_rx_state {
+ struct ast_dsp *detector;
+ int signal;
+ struct ast_frame fr;
+};
+
+struct zt_mfcr2 {
+ pthread_t master; /*!< Thread of master */
+ openr2_context_t *protocol_context; /*!< OpenR2 context handle */
+ struct zt_pvt *pvts[MAX_CHANNELS]; /*!< Member channel pvt structs */
+ int numchans; /*!< Number of channels in this R2 block */
+ int monitored_count; /*!< Number of channels being monitored */
+ ast_mutex_t monitored_count_lock; /*!< lock access to the counter */
+ ast_cond_t do_monitor; /*!< Condition to wake up the monitor thread when there's work to do */
+};
+
+static struct zt_mfcr2 r2links[NUM_SPANS];
+static openr2_variant_t mfcr2_cur_variant = OR2VAR_UNKNOWN;
+static int mfcr2_cur_max_ani = 10;
+static int mfcr2_cur_max_dnis = 4;
+static int mfcr2_cur_get_ani_first = 0;
+static int mfcr2_cur_context_index = 0;
+static char mfcr2_cur_logdir[OR2_MAX_LOGDIR];
+static openr2_log_level_t mfcr2_cur_loglevel = OR2_LOG_ERROR | OR2_LOG_WARNING;
+static openr2_calling_party_category_t mfcr2_cur_category = OR2_CALLING_PARTY_CATEGORY_NATIONAL_SUBSCRIBER;
+
+#endif /* HAVE_OPENR2 */
+
#ifdef HAVE_PRI
#define PVT_TO_CHANNEL(p) (((p)->prioffset) | ((p)->logicalspan << 8) | (p->pri->mastertrunkgroup ? 0x10000 : 0))
@@ -705,6 +749,15 @@
unsigned int dpc; /*!< CIC's DPC */
unsigned int loopedback:1;
#endif
+#ifdef HAVE_OPENR2
+ int mfcr2call;
+ struct zt_mfcr2 *mfcr2;
+ openr2_chan_t *r2chan;
+ openr2_calling_party_category_t mfcr2_recvd_category;
+ openr2_calling_party_category_t mfcr2_category;
+ struct zt_mf_tx_state mf_tx_state;
+ struct zt_mf_rx_state mf_rx_state;
+#endif
char begindigit;
int muting;
} *iflist = NULL, *ifend = NULL;
@@ -859,6 +912,7 @@
.func_channel_read = zt_func_read,
};
+static struct ast_channel *zt_new(struct zt_pvt *, int, int, int, int, int);
#ifdef HAVE_PRI
#define GET_CHANNEL(p) ((p)->bearer ? (p)->bearer->channel : p->channel)
#else
@@ -1052,6 +1106,400 @@
#endif
}
+#ifdef HAVE_OPENR2
+
+static openr2_calling_party_category_t zt_r2_get_channel_category(struct ast_channel *c)
+{
+ openr2_calling_party_category_t cat;
+ const char *catstr = pbx_builtin_getvar_helper(c, "MFCR2_CATEGORY");
+ struct zt_pvt *p = c->tech_pvt;
+ if (ast_strlen_zero(catstr)) {
+ ast_log(LOG_DEBUG, "no MFC/R2 category specified for chan %s, using default %s\n",
+ c->name, openr2_proto_get_category_string(p->mfcr2_category));
+ return p->mfcr2_category;
+ }
+ if ((cat = openr2_proto_get_category(catstr)) == OR2_CALLING_PARTY_CATEGORY_UNKNOWN) {
+ ast_log(LOG_WARNING, "Invalid category specified '%s' for chan %s, using default %s\n",
+ catstr, c->name, openr2_proto_get_category_string(p->mfcr2_category));
+ return p->mfcr2_category;
+ }
+ ast_log(LOG_DEBUG, "Using category %s\n", catstr);
+ return cat;
+}
+
+static void *zt_r2_mf_tx_init(struct zt_mf_tx_state *handle, int forward_signals)
+{
+ ZT_DIAL_OPERATION zo = {
+ .op = ZT_DIAL_OP_REPLACE
+ };
+ int res;
+ /* choose either fOrward or Reverse signals */
+ ast_copy_string(zo.dialstr, forward_signals ? "O" : "R", sizeof(zo.dialstr));
+ res = ioctl(openr2_chan_get_fd(handle->r2chan), ZT_DIAL, &zo);
+ if (-1 == res) {
+ ast_log(LOG_ERROR, "Failed to init MF tx state: %s, we will not be able to make or receive calls!.\n", strerror(errno));
+ return NULL;
+ }
+ handle->forward = forward_signals;
+ handle->generate = 0;
+ return handle;
+}
+
+static const char zt_r2_mf_names[] = "123456789ABCDEF";
+static int zt_r2_mf_tx_put(struct zt_mf_tx_state *handle, char signal)
+{
+ int res;
+ struct zt_pvt *p;
+ /* OpenR2 0 signal is A signal in Zaptel */
+ signal = (signal == '0') ? 'A' : signal;
+ if (signal && strchr(zt_r2_mf_names, signal)) {
+ handle->signal = handle->forward ? (ZT_TONE_MFR2_FWD_BASE + (signal - zt_r2_mf_names[0]))
+ : (ZT_TONE_MFR2_REV_BASE + (signal - zt_r2_mf_names[0]));
+ if (signal >= 'A') {
+ handle->signal -= 7;
+ }
+ handle->generate = 1;
+ } else if (!signal){
+ /* mute the tone as soon as possible, dont wait until zt_r2_mf_tx gets called.
+ In fact, since OpenR2 was designed for user space tone generation, this could
+ be our last chance to mute it */
+ handle->signal = -1;
+ res = ioctl(openr2_chan_get_fd(handle->r2chan), ZT_SENDTONE, &handle->signal);
+ if (-1 == res) {
+ ast_log(LOG_ERROR, "Failed to mute MF signal, something will likely fail.\n");
+ return -1;
+ }
+ handle->generate = 0;
+ p = openr2_chan_get_client_data(handle->r2chan);
+ p->dialing = 0;
+ } else {
+ ast_log(LOG_ERROR, "Asked to prepare invalid signal '%c', call setup will likely fail.\n", signal);
+ return -1;
+ }
+ return 0;
+}
+
+static int zt_r2_mf_tx(struct zt_mf_tx_state *handle, int16_t buffer[], int samples)
+{
+ /* OpenR2 passes a buffer in case the MF implementation wants to put the
+ signal in a buffer, but zaptel generates the signal directly on the kernel
+ hence we ignore buffer and samples and return 0 to indicate no samples were generated */
+ int res;
+ struct zt_pvt *p = openr2_chan_get_client_data(handle->r2chan);
+ if (handle->signal == -1) {
+ return 0;
+ }
+ p->dialing = 1;
+ res = ioctl(openr2_chan_get_fd(handle->r2chan), ZT_SENDTONE, &handle->signal);
+ if (-1 == res) {
+ ast_log(LOG_ERROR, "Failed to generate tone with ZT_SENDTONE: %s\n", strerror(errno));
+ return -1;
+ }
+ handle->generate = 0;
+ return 0;
+}
+
+static int zt_r2_mf_want_generate(struct zt_mf_tx_state *handle, int signal)
+{
+ return handle->generate;
+}
+
+static void *zt_r2_mf_rx_init(struct zt_mf_rx_state *handle, int forward_signals)
+{
+ if (handle->detector) {
+ ast_dsp_free(handle->detector);
+ } else {
+ handle->fr.frametype = AST_FRAME_VOICE;
+ handle->fr.subclass = AST_FORMAT_SLINEAR;
+ handle->fr.offset = 0;
+ handle->fr.datalen = OR2_CHAN_READ_SIZE * 2;
+ handle->fr.samples = OR2_CHAN_READ_SIZE;
+ handle->fr.data = NULL;
+ handle->fr.src = __FUNCTION__;
+ handle->fr.mallocd = 0;
+ }
+ handle->detector = ast_dsp_new();
+ if (!handle->detector) {
+ ast_log(LOG_ERROR, "Failed to init MF rx state: %s, we will not be able to make or receive calls!.\n", strerror(errno));
+ return NULL;
+ }
+ ast_dsp_set_features(handle->detector, DSP_FEATURE_DIGIT_DETECT | DSP_FEATURE_SILENCE_SUPPRESS);
+ ast_dsp_set_digitmode(handle->detector, forward_signals ? DSP_DIGITMODE_MFR2_FWD : DSP_DIGITMODE_MFR2_REV);
+ return handle;
+}
+
+static void zt_r2_mf_rx_dispose(struct zt_mf_rx_state *handle)
+{
+ if (handle->detector) {
+ ast_dsp_free(handle->detector);
+ handle->detector = NULL;
+ }
+}
+
+static int zt_r2_mf_rx(struct zt_mf_rx_state *handle, int16_t buffer[], int samples)
+{
+ struct ast_frame *f;
+ handle->fr.data = buffer;
+ f = ast_dsp_process(NULL, handle->detector, &handle->fr);
+ if (f && f->frametype == AST_FRAME_DTMF_BEGIN) {
+ handle->signal = f->subclass;
+ } else if (f && f->frametype == AST_FRAME_NULL) {
+ if (handle->signal != 0) {
+ /* a better way must exists to
+ be able to detect tone x, silence, tone x */
+ ast_dsp_digitreset(handle->detector);
+ }
+ handle->signal = 0;
+ }
+ /* OpenR2 A signal is represented as 0 */
+ return (handle->signal == 'A') ? '0' : handle->signal;
+}
+
+static openr2_mflib_interface_t zt_r2_mf_iface = {
+ (openr2_mf_read_init_func)zt_r2_mf_rx_init,
+ (openr2_mf_write_init_func)zt_r2_mf_tx_init,
+
+ (openr2_mf_detect_tone_func)zt_r2_mf_rx,
+ (openr2_mf_generate_tone_func)zt_r2_mf_tx,
+
+ (openr2_mf_select_tone_func)zt_r2_mf_tx_put,
+
+ (openr2_mf_want_generate_func)zt_r2_mf_want_generate,
+ (openr2_mf_read_dispose_func)zt_r2_mf_rx_dispose,
+ NULL
+};
+
+static void zt_r2_on_call_init(openr2_chan_t *r2chan)
+{
+ struct zt_pvt *p = openr2_chan_get_client_data(r2chan);
+ p->mfcr2call = 1;
+ ast_log(LOG_NOTICE, "New MFC/R2 call detected on chan %d.\n", openr2_chan_get_number(r2chan));
+}
+
+static void zt_r2_on_zap_alarm(openr2_chan_t *r2chan, int alarm)
+{
+ ast_log(LOG_WARNING, "Zap alarm on chan %d.\n", openr2_chan_get_number(r2chan));
+}
+
+static void zt_r2_on_os_error(openr2_chan_t *r2chan, int errorcode)
+{
+ ast_log(LOG_ERROR, "OS error on chan %d: %s\n", openr2_chan_get_number(r2chan), strerror(errorcode));
+}
+
+static void zt_r2_on_protocol_error(openr2_chan_t *r2chan, openr2_protocol_error_t reason)
+{
+ struct zt_pvt *p = openr2_chan_get_client_data(r2chan);
+ ast_log(LOG_ERROR, "MFC/R2 protocol error on chan %d: %s\n", openr2_chan_get_number(r2chan), openr2_proto_get_error(reason));
+ if (p->owner) {
+ p->owner->hangupcause = AST_CAUSE_PROTOCOL_ERROR;
+ p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ }
+}
+
+static void zt_r2_on_call_offered(openr2_chan_t *r2chan, const char *ani, const char *dnis, openr2_calling_party_category_t category)
+{
+ struct zt_pvt *p;
+ ast_log(LOG_NOTICE, "MFC/R2 call offered on chan %d. DNIS = %s, ANI = %s, Category = %s\n",
+ openr2_chan_get_number(r2chan), dnis, ani, openr2_proto_get_category_string(category));
+ p = openr2_chan_get_client_data(r2chan);
+ ast_mutex_lock(&p->lock);
+ p->mfcr2_recvd_category = category;
+ if (p->use_callerid && ani) {
+ ast_copy_string(p->cid_num, ani, sizeof(p->cid_num));
+ ast_copy_string(p->cid_name, ani, sizeof(p->cid_name));
+ } else {
+ p->cid_num[0] = 0;
+ p->cid_num[0] = 0;
+ }
+ ast_copy_string(p->rdnis, dnis, sizeof(p->rdnis));
+ ast_copy_string(p->exten, dnis, sizeof(p->exten));
+ ast_mutex_unlock(&p->lock);
+ if (!ast_exists_extension(NULL, p->context, p->exten, 1, p->cid_num)) {
+ ast_log(LOG_NOTICE, "MFC/R2 call on channel %d requested non-existent extension '%s' in context '%s'. Rejecting call.\n",
+ p->channel, p->exten, p->context);
+ openr2_chan_disconnect_call(r2chan, OR2_CAUSE_UNASSIGNED_NUMBER);
+ } else {
+ openr2_chan_accept_call(r2chan, OR2_ACCEPT_WITH_CHARGE);
+ }
+}
+
+static void zt_r2_on_call_end(openr2_chan_t *r2chan)
+{
+ ast_log(LOG_NOTICE, "MFC/R2 call end on chan %d\n", openr2_chan_get_number(r2chan));
+}
+
+static void zt_r2_update_monitor_count(struct zt_mfcr2 *mfcr2, int increment)
+{
+ ast_mutex_lock(&mfcr2->monitored_count_lock);
+ if (increment) {
+ mfcr2->monitored_count++;
+ if (mfcr2->monitored_count == 1) {
+ ast_log(LOG_DEBUG, "At least one device needs monitoring, let's wake up that lazy bastard.\n");
+ ast_cond_signal(&mfcr2->do_monitor);
+ }
+ } else {
+ mfcr2->monitored_count--;
+ if (mfcr2->monitored_count < 0) {
+ ast_log(LOG_ERROR, "wtf? we have a bug here!.\n");
+ }
+ }
+ ast_mutex_unlock(&mfcr2->monitored_count_lock);
+}
+
+static void zt_r2_on_call_accepted(openr2_chan_t *r2chan)
+{
+ struct zt_pvt *p = NULL;
+ struct ast_channel *c = NULL;
+ ast_log(LOG_NOTICE, "MFC/R2 call has been accepted on chan %d\n", openr2_chan_get_number(r2chan));
+ p = openr2_chan_get_client_data(r2chan);
+ if (OR2_DIR_BACKWARD == openr2_chan_get_direction(r2chan)) {
+ c = zt_new(p, AST_STATE_RING, 1, SUB_REAL, ZT_LAW_ALAW, 0);
+ if (c) {
+ zt_r2_update_monitor_count(p->mfcr2, 0);
+ /* chan_zap will take care of reading from now on, tell the library to forget about it */
+ openr2_chan_disable_read(r2chan);
+ } else {
+ ast_log(LOG_WARNING, "Unable to start PBX on chan %d\n", p->channel);
+ openr2_chan_disconnect_call(r2chan, OR2_CAUSE_OUT_OF_ORDER);
+ return;
+ }
+ pbx_builtin_setvar_helper(c, "MFCR2_CATEGORY", openr2_proto_get_category_string(p->mfcr2_recvd_category));
+ } else {
+ ast_log(LOG_NOTICE, "Call accepted on forward channel %d\n", p->channel);
+ p->subs[SUB_REAL].needringing = 1;
+ /* chan_zap will take care of reading from now on, tell the library to forget about it */
+ openr2_chan_disable_read(r2chan);
+ }
+}
+
+static void zt_r2_on_call_answered(openr2_chan_t *r2chan)
+{
+ struct zt_pvt *p = openr2_chan_get_client_data(r2chan);
+ ast_log(LOG_DEBUG, "MFC/R2 call has been answered on chan %d\n", openr2_chan_get_number(r2chan));
+ p->subs[SUB_REAL].needanswer = 1;
+}
+
+static void zt_r2_on_call_read(openr2_chan_t *r2chan, const unsigned char *buf, int buflen)
+{
+ /*ast_log(LOG_DEBUG, "Read data from zap channel %d\n", openr2_chan_get_number(r2chan));*/
+}
+
+static void zt_r2_on_call_disconnected(openr2_chan_t *r2chan, openr2_call_disconnect_reason_t reason)
+{
+ struct zt_pvt *p = openr2_chan_get_client_data(r2chan);
+ ast_log(LOG_NOTICE, "MFC/R2 call disconnected on chan %d\n", openr2_chan_get_number(r2chan));
+ if (p->owner) {
+ if (p->owner->_state == AST_STATE_UP) {
+ p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ } else {
+ /* should we have this branch at all? */
+ switch (reason) {
+ case OR2_CAUSE_BUSY_NUMBER:
+ p->subs[SUB_REAL].needbusy = 1;
+ break;
+ case OR2_CAUSE_NETWORK_CONGESTION:
+ case OR2_CAUSE_OUT_OF_ORDER:
+ case OR2_CAUSE_UNASSIGNED_NUMBER:
+ case OR2_CAUSE_NO_ANSWER:
+ case OR2_CAUSE_UNSPECIFIED:
+ case OR2_CAUSE_NORMAL_CLEARING:
+ p->subs[SUB_REAL].needcongestion = 1;
+ break;
+ default:
+ p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ }
+ }
+ }
+ openr2_chan_disconnect_call(r2chan, OR2_CAUSE_NORMAL_CLEARING);
+}
+
+static void zt_r2_write_log(openr2_log_level_t level, char *logmessage)
+{
+ switch (level) {
+ case OR2_LOG_NOTICE:
+ ast_log(LOG_NOTICE, logmessage);
+ break;
+ case OR2_LOG_WARNING:
+ ast_log(LOG_WARNING, logmessage);
+ break;
+ case OR2_LOG_ERROR:
+ ast_log(LOG_ERROR, logmessage);
+ break;
+ case OR2_LOG_STACK_TRACE:
+ case OR2_LOG_MF_TRACE:
+ case OR2_LOG_CAS_TRACE:
+ case OR2_LOG_DEBUG:
+ ast_log(LOG_DEBUG, logmessage);
+ break;
+ default:
+ ast_log(LOG_WARNING, "We should handle logging level %d here.\n", level);
+ ast_log(LOG_DEBUG, logmessage);
+ break;
+ }
+}
+
+static void zt_r2_on_line_blocked(openr2_chan_t *r2chan)
+{
+ ast_log(LOG_NOTICE, "Far end blocked on chan %d\n", openr2_chan_get_number(r2chan));
+}
+
+static void zt_r2_on_line_idle(openr2_chan_t *r2chan)
+{
+ ast_log(LOG_NOTICE, "Far end unblocked on chan %d\n", openr2_chan_get_number(r2chan));
+}
+
+static void zt_r2_on_context_logging(openr2_context_t *r2context, openr2_log_level_t level, const char *fmt, va_list ap)
+{
+ char logmsg[256];
+ char completemsg[sizeof(logmsg)+50];
+ vsnprintf(logmsg, sizeof(logmsg), fmt, ap);
+ snprintf(completemsg, sizeof(completemsg), "Context - %s", logmsg);
+ zt_r2_write_log(level, completemsg);
+}
+
+static void zt_r2_on_chan_log(openr2_chan_t *r2chan, openr2_log_level_t level, const char *fmt, va_list ap)
+{
+ char logmsg[256];
+ char completemsg[sizeof(logmsg)+50];
+ vsnprintf(logmsg, sizeof(logmsg), fmt, ap);
+ snprintf(completemsg, sizeof(completemsg), "Chan %d - %s", openr2_chan_get_number(r2chan), logmsg);
+ zt_r2_write_log(level, completemsg);
+}
+
+static openr2_event_interface_t zt_r2_event_iface = {
+ zt_r2_on_call_init,
+ zt_r2_on_call_offered,
+ zt_r2_on_call_accepted,
+ zt_r2_on_call_answered,
+ zt_r2_on_call_disconnected,
+ zt_r2_on_call_end,
+ zt_r2_on_call_read,
+ zt_r2_on_zap_alarm,
+ zt_r2_on_os_error,
+ zt_r2_on_protocol_error,
+ zt_r2_on_line_blocked,
+ zt_r2_on_line_idle,
+ zt_r2_on_context_logging
+};
+
+static inline int16_t zt_r2_alaw_to_linear(uint8_t sample)
+{
+ return AST_ALAW(sample);
+}
+
+static inline uint8_t zt_r2_linear_to_alaw(int sample)
+{
+ return AST_LIN2A(sample);
+}
+
+static openr2_transcoder_interface_t zt_r2_transcode_iface = {
+ zt_r2_alaw_to_linear,
+ zt_r2_linear_to_alaw
+};
+
+#endif /* HAVE_OPENR2 */
+
+
static int restore_gains(struct zt_pvt *p);
static void swap_subs(struct zt_pvt *p, int a, int b)
@@ -1425,6 +1873,8 @@
return "ISDN BRI Point to MultiPoint";
case SIG_SS7:
return "SS7";
+ case SIG_MFCR2:
+ return "MFC/R2";
case SIG_SF:
return "SF (Tone) Immediate";
case SIG_SFWINK:
@@ -2360,6 +2810,7 @@
case SIG_BRI:
case SIG_BRI_PTMP:
case SIG_SS7:
+ case SIG_MFCR2:
/* We'll get it in a moment -- but use dialdest to store pre-setup_ack digits */
p->dialdest[0] = '\0';
break;
@@ -2486,6 +2937,34 @@
ss7_rel(p->ss7);
}
#endif /* HAVE_SS7 */
+#ifdef HAVE_OPENR2
+ if (p->mfcr2) {
+ int strip = p->stripmsd;
+ int callres = 0;
+ c = strchr(dest, '/');
+ if (c) {
+ c++;
+ } else {
+ c = dest;
+ }
+ if (!p->hidecallerid) {
+ l = ast->cid.cid_num;
+ } else {
+ l = NULL;
+ }
+ if (strlen(c) < strip) {
+ ast_log(LOG_WARNING, "Destiny number '%s' is shorter than stripmsd(%d)? hum, you should fix that. Assuming stripmsd = 0\n", c, strip);
+ strip = 0;
+ }
+ callres = openr2_chan_make_call(p->r2chan, l, (c + strip), zt_r2_get_channel_category(ast));
+ if (-1 == callres) {
+ ast_mutex_unlock(&p->lock);
+ ast_log(LOG_ERROR, "unable to make new MFC/R2 call!\n");
+ return -1;
+ }
+ ast_setstate(ast, AST_STATE_DIALING);
+ }
+#endif /* HAVE_OPENR2 */
#ifdef HAVE_PRI
if (p->pri) {
struct pri_sr *sr;
@@ -3162,6 +3641,13 @@
}
}
#endif
+#ifdef HAVE_OPENR2
+ if (p->mfcr2) {
+ ast_log(LOG_DEBUG, "disconnecting MFC/R2 call on chan %d\n", p->channel);
+ openr2_chan_disconnect_call(p->r2chan, OR2_CAUSE_NORMAL_CLEARING);
+ zt_r2_update_monitor_count(p->mfcr2, 1);
+ }
+#endif
#ifdef HAVE_PRI
if (p->pri) {
#ifdef SUPPORT_USERUSER
@@ -3215,7 +3701,7 @@
}
}
#endif
- if (p->sig && ((p->sig != SIG_PRI) && (p->sig != SIG_SS7) && (p->sig != SIG_BRI) && (p->sig != SIG_BRI_PTMP)))
+ if (p->sig && ((p->sig != SIG_PRI) && (p->sig != SIG_SS7) && (p->sig != SIG_MFCR2) && (p->sig != SIG_BRI) && (p->sig != SIG_BRI_PTMP)))
res = zt_set_hook(p->subs[SUB_REAL].zfd, ZT_ONHOOK);
if (res < 0) {
ast_log(LOG_WARNING, "Unable to hangup line %s\n", ast->name);
@@ -3401,6 +3887,12 @@
}
break;
#endif
+#ifdef HAVE_OPENR2
+ case SIG_MFCR2:
+ ast_log(LOG_DEBUG, "Accepting MFC/R2 call on chan %d\n", p->channel);
+ openr2_chan_answer_call(p->r2chan);
+ break;
+#endif
case 0:
ast_mutex_unlock(&p->lock);
return 0;
@@ -4081,8 +4573,6 @@
static void *ss_thread(void *data);
-static struct ast_channel *zt_new(struct zt_pvt *, int, int, int, int, int);
-
static int attempt_transfer(struct zt_pvt *p)
{
/* In order to transfer, we need at least one of the channels to
@@ -4332,7 +4822,16 @@
break;
#endif
case ZT_EVENT_BITSCHANGED:
+#ifdef HAVE_OPENR2
+ if (p->sig != SIG_MFCR2) {
+ ast_log(LOG_WARNING, "Recieved bits changed on %s signalling?\n", sig2str(p->sig));
+ } else {
+ ast_log(LOG_DEBUG, "bits changed in chan %d\n", p->channel);
+ openr2_proto_handle_abcd_change(p->r2chan);
+ }
+#else
ast_log(LOG_WARNING, "Recieved bits changed on %s signalling?\n", sig2str(p->sig));
+#endif
case ZT_EVENT_PULSE_START:
/* Stop tone if there's a pulse start and the PBX isn't started */
if (!ast->pbx)
@@ -5261,6 +5760,12 @@
else if (p->ringt > 0)
p->ringt--;
+#ifdef HAVE_OPENR2
+ if (p->mfcr2) {
+ openr2_chan_process_event(p->r2chan);
+ }
+#endif
+
if (p->subs[index].needringing) {
/* Send ringing frame if requested */
p->subs[index].needringing = 0;
@@ -5305,6 +5810,23 @@
ast_mutex_unlock(&p->lock);
return &p->subs[index].f;
}
+#ifdef HAVE_OPENR2
+ if (p->mfcr2 && openr2_chan_get_read_enabled(p->r2chan)) {
+ /* openr2 took care of reading and handling any event
+ (needanswer, needbusy etc), if we continue we will read()
+ twice, lets just return a null frame. This should only
+ happen when openr2 is dialing out */
+ p->subs[index].f.frametype = AST_FRAME_NULL;
+ p->subs[index].f.subclass = 0;
+ p->subs[index].f.samples = 0;
+ p->subs[index].f.mallocd = 0;
+ p->subs[index].f.offset = 0;
+ p->subs[index].f.data = NULL;
+ p->subs[index].f.datalen= 0;
+ ast_mutex_unlock(&p->lock);
+ return &p->subs[index].f;
+ }
+#endif
if (p->subs[index].needflash) {
/* Send answer frame if requested */
@@ -7706,7 +8228,7 @@
count = 0;
i = iflist;
while (i) {
- if ((i->subs[SUB_REAL].zfd > -1) && i->sig && (!i->radio)) {
+ if ((i->subs[SUB_REAL].zfd > -1) && i->sig && (!i->radio) && !(i->sig & SIG_MFCR2)) {
if (!i->owner && !i->subs[SUB_REAL].owner && !i->mwimonitoractive) {
/* This needs to be watched, as it lacks an owner */
pfds[count].fd = i->subs[SUB_REAL].zfd;
@@ -8045,6 +8567,19 @@
}
#endif /* HAVE_SS7 */
+#ifdef HAVE_OPENR2
+
+static struct zt_mfcr2 *mfcr2_get_context(int id)
+{
+ if ((id < 0) || (id > sizeof(r2links))) {
+ ast_log(LOG_ERROR, "Wah! you surely did not mean to do this\n.");
+ return NULL;
+ }
+ return &r2links[id];
+}
+
+#endif
+
/* converts a Zaptel sigtype to signalling as can be configured from
* zapata.conf.
* While both have basically the same values, this will later be the
@@ -8190,6 +8725,62 @@
linksets[span].calling_nai = conf.ss7.calling_nai;
}
#endif
+#ifdef HAVE_OPENR2
+ if (conf.chan.sig == SIG_MFCR2 && !reloading) {
+ char logdir[OR2_MAX_LOGDIR];
+ struct zt_mfcr2 *zap_r2;
+ int threshold = 0;
+ int snres = 0;
+ zap_r2 = mfcr2_get_context(mfcr2_cur_context_index);
+ if (!zap_r2) {
+ ast_log(LOG_WARNING, "Cannot get another R2 zap context!\n");
+ } else if (!zap_r2->protocol_context){
+ zap_r2->protocol_context = openr2_context_new(&zt_r2_mf_iface, &zt_r2_event_iface,
+ &zt_r2_transcode_iface, mfcr2_cur_variant, mfcr2_cur_max_ani, mfcr2_cur_max_dnis);
+ if (!zap_r2->protocol_context) {
+ ast_log(LOG_ERROR, "Cannot create OpenR2 protocol context.\n");
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+ openr2_context_set_log_level(zap_r2->protocol_context, mfcr2_cur_loglevel);
+ openr2_context_set_ani_first(zap_r2->protocol_context, mfcr2_cur_get_ani_first);
+ openr2_context_set_mf_threshold(zap_r2->protocol_context, threshold);
+ if (ast_strlen_zero(mfcr2_cur_logdir)) {
+ openr2_context_set_log_directory(zap_r2->protocol_context, "/tmp");
+ } else {
+ snres = snprintf(logdir, sizeof(logdir), "%s/%s/%s", ast_config_AST_LOG_DIR, "mfcr2", mfcr2_cur_logdir);
+ if (snres >= sizeof(logdir)) {
+ ast_log(LOG_ERROR, "MFC/R2 logging directory truncated, using /tmp\n");
+ openr2_context_set_log_directory(zap_r2->protocol_context, logdir);
+ } else {
+ openr2_context_set_log_directory(zap_r2->protocol_context, logdir);
+ }
+ }
+ ast_cond_init(&zap_r2->do_monitor, NULL);
+ ast_mutex_init(&zap_r2->monitored_count_lock);
+ zap_r2->monitored_count = 0;
+ }
+ if (zap_r2) {
+ /* TODO: should we check numchans overflow, or is it already done by zap? */
+ zap_r2->pvts[zap_r2->numchans++] = tmp;
+ tmp->r2chan = openr2_chan_new_from_fd(zap_r2->protocol_context, tmp->subs[SUB_REAL].zfd,
+ &tmp->mf_tx_state, &tmp->mf_rx_state);
+ if (!tmp->r2chan) {
+ ast_log(LOG_ERROR, "Cannot create OpenR2 protocol context.\n");
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
+ openr2_chan_set_client_data(tmp->r2chan, tmp);
+ openr2_chan_set_logging_func(tmp->r2chan, zt_r2_on_chan_log);
+ openr2_chan_set_log_level(tmp->r2chan, mfcr2_cur_loglevel);
+ tmp->mf_tx_state.r2chan = tmp->r2chan;
+ tmp->mfcr2_category = mfcr2_cur_category;
+ tmp->mfcr2 = zap_r2;
+ tmp->mfcr2call = 0;
+ zap_r2->monitored_count++;
+ }
+ }
+#endif
#ifdef HAVE_PRI
if ((conf.chan.sig == SIG_PRI) || (conf.chan.sig == SIG_BRI) || (conf.chan.sig == SIG_BRI_PTMP) || (conf.chan.sig == SIG_GR303FXOKS) || (conf.chan.sig == SIG_GR303FXSKS)) {
int offset;
@@ -8514,7 +9105,11 @@
ast_dsp_set_digitmode(tmp->dsp, DSP_DIGITMODE_DTMF | tmp->dtmfrelax);
update_conf(tmp);
if (!here) {
- if ((conf.chan.sig != SIG_BRI) && (conf.chan.sig != SIG_BRI_PTMP) && (conf.chan.sig != SIG_PRI) && (conf.chan.sig != SIG_SS7))
+ if ((conf.chan.sig != SIG_BRI)
+ && (conf.chan.sig != SIG_BRI_PTMP)
+ && (conf.chan.sig != SIG_PRI)
+ && (conf.chan.sig != SIG_SS7)
+ && (conf.chan.sig != SIG_MFCR2))
/* Hang it up to be sure it's good */
zt_set_hook(tmp->subs[SUB_REAL].zfd, ZT_ONHOOK);
}
@@ -8646,6 +9241,14 @@
return 1;
}
#endif
+#ifdef HAVE_OPENR2
+ if (p->mfcr2) {
+ if (p->mfcr2call)
+ return 0;
+ else
+ return 1;
+ }
+#endif
if (!(p->radio || (p->oprmode < 0)))
{
if (!p->sig || (p->sig == SIG_FXSLS))
@@ -8935,6 +9538,11 @@
}
p->outgoing = 1;
tmp = zt_new(p, AST_STATE_RESERVED, 0, p->owner ? SUB_CALLWAIT : SUB_REAL, 0, 0);
+#ifdef HAVE_OPENR2
+ if (p->mfcr2) {
+ zt_r2_update_monitor_count(p->mfcr2, 0);
+ }
+#endif
#ifdef HAVE_PRI
if (p->bearer) {
/* Log owner to bearer channel, too */
@@ -9754,6 +10362,73 @@
#endif /* HAVE_SS7 */
+#ifdef HAVE_OPENR2
+static void *mfcr2_monitor(void *data)
+{
+ struct zt_mfcr2 *mfcr2 = data;
+ /* we should be using pthread_key_create
+ and allocate pollers dynamically.
+ I think do_monitor() could be leaking, since it
+ could be cancelled at any time and is not
+ using thread keys, why?, */
+ struct pollfd pollers[sizeof(mfcr2->pvts)];
+ int nextms = 0;
+ int res = 0;
+ int i = 0;
+ int oldstate = 0;
+ /* now that we're ready to get calls, unblock our side and
+ get current line state */
+ for (i = 0; i < mfcr2->numchans; i++) {
+ openr2_proto_set_idle(mfcr2->pvts[i]->r2chan);
+ openr2_proto_handle_abcd_change(mfcr2->pvts[i]->r2chan);
+ }
+ while(1) {
+ /* we trust here that the mfcr2 channel list will not ever change once
+ the module is loaded */
+ nextms = openr2_context_get_time_to_next_event(mfcr2->protocol_context);
+ ast_mutex_lock(&mfcr2->monitored_count_lock);
+ if (mfcr2->monitored_count == 0) {
+ ast_log(LOG_DEBUG, "No one requires my monitoring services :-(\n");
+ ast_cond_wait(&mfcr2->do_monitor, &mfcr2->monitored_count_lock);
+ ast_log(LOG_DEBUG, "Alright, back to work!\n");
+ }
+
+ for (i = 0; i < mfcr2->numchans; i++) {
+ pollers[i].revents = 0;
+ pollers[i].events = 0;
+ if (mfcr2->pvts[i]->owner) {
+ continue;
+ }
+ if (mfcr2->pvts[i]->mfcr2call) {
+ mfcr2->pvts[i]->mfcr2call = 0;
+ }
+ openr2_chan_enable_read(mfcr2->pvts[i]->r2chan);
+ pollers[i].events = POLLIN | POLLPRI;
+ pollers[i].fd = mfcr2->pvts[i]->subs[SUB_REAL].zfd;
+ }
+ ast_mutex_unlock(&mfcr2->monitored_count_lock);
+ /* probably poll() is a valid cancel point, lets just be on the safe side
+ by calling pthread_testcancel */
+ pthread_testcancel();
+ res = poll(pollers, mfcr2->numchans, nextms);
+ pthread_testcancel();
+ if ((res < 0) && (errno != EINTR)) {
+ ast_log(LOG_ERROR, "going out, poll failed: %s\n", strerror(errno));
+ break;
+ }
+ /* do we want to allow to cancel while processing events? */
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
+ for (i = 0; i < mfcr2->numchans; i++) {
+ if (pollers[i].revents & POLLPRI || pollers[i].revents & POLLIN) {
+ openr2_chan_process_event(mfcr2->pvts[i]->r2chan);
+ }
+ }
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
+ }
+ return 0;
+}
+#endif
+
#ifdef HAVE_PRI
static struct zt_pvt *pri_find_crv(struct zt_pri *pri, int crv)
{
@@ -11626,6 +12301,290 @@
#endif /* HAVE_PRI */
+#ifdef HAVE_OPENR2
+
+static char *handle_mfcr2_version(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ const char *version;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "mfcr2 show version";
+ e->usage =
+ "Usage: mfcr2 show version\n"
+ " Shows the version of the OpenR2 library being used.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ version = openr2_get_version();
+ ast_cli(a->fd, "OpenR2 version: %s\n", version);
+ return CLI_SUCCESS;
+}
+
+static char *handle_mfcr2_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT "%7s %-10.10s %-15.15s %-10.10s %-20.20s %-10.10s %-10.10s\n"
+#define FORMAT2 "%7s %-10.10s %-15.15s %-10.10s %-20.20s %-10.10s %-10.10s\n"
+ int filtertype = 0;
+ int targetnum;
+ char channo[5];
+ char anino[5];
+ char dnisno[5];
+ struct zt_pvt *tmp;
+ openr2_context_t *r2context;
+ openr2_variant_t r2variant;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "mfcr2 show channels [group|context]";
+ e->usage =
+ "Usage: mfcr2 show channels [group | context ]\n"
+ " Shows the zap channels configured with MFC/R2 signaling.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (!((a->argc == 3) || (a->argc == 5))) {
+ return CLI_SHOWUSAGE;
+ }
+ if (a->argc == 5) {
+ if (!strcasecmp(a->argv[3], "group")) {
+ targetnum = atoi(a->argv[4]);
+ if ((targetnum < 0) || (targetnum > 63))
+ return CLI_SHOWUSAGE;
+ targetnum = 1 << targetnum;
+ filtertype = 1;
+ } else if (!strcasecmp(a->argv[3], "context")) {
+ filtertype = 2;
+ } else {
+ return CLI_SHOWUSAGE;
+ }
+ }
+ ast_cli(a->fd, FORMAT, "Chan", "Variant", "Max ANI", "Max DNIS", "ANI First", "Tx State", "Rx State");
+ ast_mutex_lock(&iflock);
+ tmp = iflist;
+ while (tmp) {
+ if (!(tmp->sig & SIG_MFCR2)) {
+ tmp = tmp->next;
+ continue;
+ }
+ if (filtertype) {
+ switch(filtertype) {
+ case 1: /* mfcr2 show channels group */
+ if (tmp->group != targetnum) {
+ tmp = tmp->next;
+ continue;
+ }
+ break;
+ case 2: /* mfcr2 show channels context */
+ if (strcasecmp(tmp->context, a->argv[4])) {
+ tmp = tmp->next;
+ continue;
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ r2context = openr2_chan_get_context(tmp->r2chan);
+ r2variant = openr2_context_get_variant(r2context);
+ snprintf(channo, sizeof(channo), "%d", tmp->channel);
+ snprintf(anino, sizeof(anino), "%d", openr2_context_get_max_ani(r2context));
+ snprintf(dnisno, sizeof(dnisno), "%d", openr2_context_get_max_dnis(r2context));
+ ast_cli(a->fd, FORMAT, channo, openr2_proto_get_variant_string(r2variant),
+ anino, dnisno, openr2_context_get_ani_first(r2context) ? "Yes" : "No",
+ openr2_proto_get_tx_state_string(tmp->r2chan), openr2_proto_get_rx_state_string(tmp->r2chan));
+ tmp = tmp->next;
+ }
+ ast_mutex_unlock(&iflock);
+ return CLI_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static char *handle_mfcr2_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct zt_pvt *p = NULL;
+ int channo = 0;
+ char *toklevel = NULL;
+ char *saveptr = NULL;
+ char *logval = NULL;
+ openr2_log_level_t loglevel = OR2_LOG_NOTHING;
+ openr2_log_level_t tmplevel = OR2_LOG_NOTHING;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "mfcr2 set debug";
+ e->usage =
+ "Usage: mfcr2 set debug \n"
+ " Set a new logging level for the specified channel.\n"
+ " If no channel is specified the logging level will be applied to all channels.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc < 4) {
+ return CLI_SHOWUSAGE;
+ }
+ channo = (a->argc == 5) ? atoi(a->argv[4]) : -1;
+ logval = ast_strdupa(a->argv[3]);
+ toklevel = strtok_r(logval, ",", &saveptr);
+ if (-1 == (tmplevel = openr2_log_get_level(toklevel))) {
+ ast_cli(a->fd, "Invalid MFC/R2 logging level '%s'.\n", a->argv[3]);
+ return CLI_FAILURE;
+ } else if (OR2_LOG_NOTHING == tmplevel) {
+ loglevel = tmplevel;
+ } else {
+ loglevel |= tmplevel;
+ while ((toklevel = strtok_r(NULL, ",", &saveptr))) {
+ if (-1 == (tmplevel = openr2_log_get_level(toklevel))) {
+ ast_cli(a->fd, "Ignoring invalid logging level: '%s'.\n", toklevel);
+ continue;
+ }
+ loglevel |= tmplevel;
+ }
+ }
+ ast_mutex_lock(&iflock);
+ p = iflist;
+ while (p) {
+ if (!(p->sig & SIG_MFCR2)) {
+ p = p->next;
+ continue;
+ }
+ if ((channo != -1) && (p->channel != channo )) {
+ p = p->next;
+ continue;
+ }
+ openr2_chan_set_log_level(p->r2chan, loglevel);
+ if (channo != -1) {
+ ast_cli(a->fd, "MFC/R2 debugging set to '%s' for channel %d.\n", a->argv[3], p->channel);
+ break;
+ } else {
+ p = p->next;
+ }
+ }
+ if ((channo != -1) && !p) {
+ ast_cli(a->fd, "MFC/R2 channel %d not found.\n", channo);
+ }
+ if (channo == -1) {
+ ast_cli(a->fd, "MFC/R2 debugging set to '%s' for all channels.\n", a->argv[3]);
+ }
+ ast_mutex_unlock(&iflock);
+ return CLI_SUCCESS;
+}
+
+static char *handle_mfcr2_call_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct zt_pvt *p = NULL;
+ int channo = 0;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "mfcr2 call files [on|off]";
+ e->usage =
+ "Usage: mfcr2 call files [on|off] \n"
+ " Enable call files creation on the specified channel.\n"
+ " If no channel is specified call files creation policy will be applied to all channels.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc < 4) {
+ return CLI_SHOWUSAGE;
+ }
+ channo = (a->argc == 5) ? atoi(a->argv[4]) : -1;
+ ast_mutex_lock(&iflock);
+ p = iflist;
+ while (p) {
+ if (!(p->sig & SIG_MFCR2)) {
+ p = p->next;
+ continue;
+ }
+ if ((channo != -1) && (p->channel != channo )) {
+ p = p->next;
+ continue;
+ }
+ if (ast_true(a->argv[3])) {
+ openr2_chan_enable_call_files(p->r2chan);
+ } else {
+ openr2_chan_disable_call_files(p->r2chan);
+ }
+ if (channo != -1) {
+ if (ast_true(a->argv[3])) {
+ ast_cli(a->fd, "MFC/R2 call files enabled for channel %d.\n", p->channel);
+ } else {
+ ast_cli(a->fd, "MFC/R2 call files disabled for channel %d.\n", p->channel);
+ }
+ break;
+ } else {
+ p = p->next;
+ }
+ }
+ if ((channo != -1) && !p) {
+ ast_cli(a->fd, "MFC/R2 channel %d not found.\n", channo);
+ }
+ if (channo == -1) {
+ if (ast_true(a->argv[3])) {
+ ast_cli(a->fd, "MFC/R2 Call files enabled for all channels.\n");
+ } else {
+ ast_cli(a->fd, "MFC/R2 Call files disabled for all channels.\n");
+ }
+ }
+ ast_mutex_unlock(&iflock);
+ return CLI_SUCCESS;
+}
+
+static char *handle_mfcr2_set_idle(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct zt_pvt *p = NULL;
+ int channo = 0;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "mfcr2 set idle";
+ e->usage =
+ "Usage: mfcr2 set idle \n"
+ " DON'T USE THIS UNLESS YOU KNOW WHAT YOU ARE DOING.\n"
+ " Force the given channel into IDLE state.\n"
+ " If no channel is specified, all channels will be set to IDLE.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ channo = (a->argc == 4) ? atoi(a->argv[3]) : -1;
+ ast_mutex_lock(&iflock);
+ p = iflist;
+ while (p) {
+ if (!(p->sig & SIG_MFCR2)) {
+ p = p->next;
+ continue;
+ }
+ if ((channo != -1) && (p->channel != channo )) {
+ p = p->next;
+ continue;
+ }
+ openr2_proto_set_idle(p->r2chan);
+ openr2_proto_handle_abcd_change(p->r2chan);
+ if (channo != -1) {
+ break;
+ } else {
+ p = p->next;
+ }
+ }
+ if ((channo != -1) && !p) {
+ ast_cli(a->fd, "MFC/R2 channel %d not found.\n", channo);
+ }
+ ast_mutex_unlock(&iflock);
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry zap_mfcr2_cli[] = {
+ AST_CLI_DEFINE(handle_mfcr2_version, "Show OpenR2 library version"),
+ AST_CLI_DEFINE(handle_mfcr2_show_channels, "Show MFC/R2 channels"),
+ AST_CLI_DEFINE(handle_mfcr2_set_debug, "Set MFC/R2 channel logging level"),
+ AST_CLI_DEFINE(handle_mfcr2_call_files, "Enable/Disable MFC/R2 call files"),
+ AST_CLI_DEFINE(handle_mfcr2_set_idle, "Reset MFC/R2 channel forcing it to IDLE"),
+};
+
+#endif /* HAVE_OPENR2 */
+
+
static char *zap_destroy_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
int channel;
@@ -11957,6 +12916,29 @@
if (tmp->slaves[x])
ast_cli(a->fd, "Slave Channel: %d\n", tmp->slaves[x]->channel);
}
+#ifdef HAVE_OPENR2
+ if (tmp->mfcr2) {
+ char calldir[OR2_MAX_LOGDIR];
+ openr2_context_t *r2context = openr2_chan_get_context(tmp->r2chan);
+ openr2_variant_t r2variant = openr2_context_get_variant(r2context);
+ ast_cli(a->fd, "MFC/R2 MF State: %s\n", openr2_proto_get_mf_state_string(tmp->r2chan));
+ ast_cli(a->fd, "MFC/R2 MF Group: %s\n", openr2_proto_get_mf_group_string(tmp->r2chan));
+ ast_cli(a->fd, "MFC/R2 State: %s\n", openr2_proto_get_r2_state_string(tmp->r2chan));
+ ast_cli(a->fd, "MFC/R2 Call State: %s\n", openr2_proto_get_call_state_string(tmp->r2chan));
+ ast_cli(a->fd, "MFC/R2 Call Files Enabled: %s\n", openr2_chan_get_call_files_enabled(tmp->r2chan) ? "Yes" : "No");
+ ast_cli(a->fd, "MFC/R2 Variant: %s\n", openr2_proto_get_variant_string(r2variant));
+ ast_cli(a->fd, "MFC/R2 Max ANI: %d\n", openr2_context_get_max_ani(r2context));
+ ast_cli(a->fd, "MFC/R2 Max DNIS: %d\n", openr2_context_get_max_dnis(r2context));
+ ast_cli(a->fd, "MFC/R2 Get ANI First: %s\n", openr2_context_get_ani_first(r2context) ? "Yes" : "No");
+ ast_cli(a->fd, "MFC/R2 Rx State: %s\n", openr2_proto_get_rx_state_string(tmp->r2chan));
+ ast_cli(a->fd, "MFC/R2 Tx State: %s\n", openr2_proto_get_tx_state_string(tmp->r2chan));
+ ast_cli(a->fd, "MFC/R2 MF Tx Signal: %d\n", openr2_proto_get_mf_tx(tmp->r2chan));
+ ast_cli(a->fd, "MFC/R2 MF Rx Signal: %d\n", openr2_proto_get_mf_rx(tmp->r2chan));
+ ast_cli(a->fd, "MFC/R2 Call Files Directory: %s\n", openr2_context_get_log_directory(r2context, calldir, sizeof(calldir)));
+
+
+ }
+#endif
#ifdef HAVE_SS7
if (tmp->ss7) {
ast_cli(a->fd, "CIC: %d\n", tmp->cic);
@@ -12615,7 +13597,7 @@
{
int x;
struct zt_pvt *p, *pl;
-#if defined(HAVE_PRI) || defined(HAVE_SS7)
+#if defined(HAVE_PRI) || defined(HAVE_SS7) || defined(HAVE_OPENR2)
int i;
#endif
@@ -12627,6 +13609,15 @@
ast_cli_unregister_multiple(zap_pri_cli, sizeof(zap_pri_cli) / sizeof(struct ast_cli_entry));
ast_unregister_application(zap_send_keypad_facility_app);
#endif
+#if defined(HAVE_OPENR2)
+ for (i = 0; i < NUM_SPANS; i++) {
+ if (r2links[i].master != AST_PTHREADT_NULL) {
+ pthread_cancel(r2links[i].master);
+ pthread_join(r2links[i].master, NULL);
+ }
+ }
+ ast_cli_unregister_multiple(zap_mfcr2_cli, sizeof(zap_mfcr2_cli) / sizeof(zap_mfcr2_cli[0]));
+#endif
ast_cli_unregister_multiple(zap_cli, sizeof(zap_cli) / sizeof(struct ast_cli_entry));
ast_manager_unregister( "ZapDialOffhook" );
@@ -13036,7 +14027,7 @@
static int unload_module(void)
{
-#if defined(HAVE_PRI) || defined(HAVE_SS7)
+#if defined(HAVE_PRI) || defined(HAVE_SS7)
int y;
#endif
#ifdef HAVE_PRI
@@ -13133,6 +14124,11 @@
return -1;
}
}
+#ifdef HAVE_OPENR2
+ if (!reload && r2links[mfcr2_cur_context_index].protocol_context) {
+ mfcr2_cur_context_index++;
+ }
+#endif
}
return 0;
@@ -13583,6 +14579,10 @@
} else if (!strcasecmp(v->value, "ss7")) {
confp->chan.sig = SIG_SS7;
#endif
+#ifdef HAVE_OPENR2
+ } else if (!strcasecmp(v->value, "mfcr2")) {
+ confp->chan.sig = SIG_MFCR2;
+#endif
} else if (!strcasecmp(v->value, "auto")) {
confp->is_sig_auto = 1;
} else {
@@ -13837,6 +14837,47 @@
if (res < 0)
return -1;
#endif /* HAVE_SS7 */
+#ifdef HAVE_OPENR2
+ } else if (!strcasecmp(v->name, "mfcr2_logdir")) {
+ ast_copy_string(mfcr2_cur_logdir, v->value, sizeof(mfcr2_cur_logdir));
+ } else if (!strcasecmp(v->name, "mfcr2_variant")) {
+ mfcr2_cur_variant = openr2_proto_get_variant(v->value);
+ if (OR2VAR_UNKNOWN == mfcr2_cur_variant) {
+ ast_log(LOG_WARNING, "Unknown MFC/R2 variant '%s' at line %d.\n", v->value, v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "mfcr2_get_ani_first")) {
+ mfcr2_cur_get_ani_first = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "mfcr2_max_ani")) {
+ mfcr2_cur_max_ani = atoi(v->value);
+ } else if (!strcasecmp(v->name, "mfcr2_max_dnis")) {
+ mfcr2_cur_max_dnis = atoi(v->value);
+ } else if (!strcasecmp(v->name, "mfcr2_category")) {
+ mfcr2_cur_category = openr2_proto_get_category(v->value);
+ if (OR2_CALLING_PARTY_CATEGORY_UNKNOWN == mfcr2_cur_category) {
+ mfcr2_cur_category = OR2_CALLING_PARTY_CATEGORY_NATIONAL_SUBSCRIBER;
+ ast_log(LOG_WARNING, "Invalid MFC/R2 caller category '%s' at line %d. Using national subscriber as default.\n",
+ v->value, v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "mfcr2_logging")) {
+ openr2_log_level_t tmplevel;
+ char *toklevel, *saveptr;
+ char *logval = ast_strdupa(v->value);
+ toklevel = strtok_r(logval, ",", &saveptr);
+ if (-1 == (tmplevel = openr2_log_get_level(toklevel))) {
+ ast_log(LOG_WARNING, "Invalid MFC/R2 logging level '%s' at line %d.\n", v->value, v->lineno);
+ } else if (OR2_LOG_NOTHING == tmplevel) {
+ mfcr2_cur_loglevel = tmplevel;
+ } else {
+ mfcr2_cur_loglevel |= tmplevel;
+ while ((toklevel = strtok_r(NULL, ",", &saveptr))) {
+ if (-1 == (tmplevel = openr2_log_get_level(toklevel))) {
+ ast_log(LOG_WARNING, "Ignoring invalid logging level: '%s' at line %d.\n", toklevel, v->lineno);
+ continue;
+ }
+ mfcr2_cur_loglevel |= tmplevel;
+ }
+ }
+#endif /* HAVE_OPENR2 */
} else if (!strcasecmp(v->name, "cadence")) {
/* setup to scan our argument */
int element_count, c[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
@@ -14191,6 +15232,21 @@
}
}
#endif
+#ifdef HAVE_OPENR2
+ if (!reload) {
+ int x;
+ for (x = 0; x < NUM_SPANS; x++) {
+ if (r2links[x].protocol_context) {
+ if (ast_pthread_create(&r2links[x].master, NULL, mfcr2_monitor, &r2links[x])) {
+ ast_log(LOG_ERROR, "Unable to start R2 context on span %d\n", x + 1);
+ return -1;
+ } else {
+ ast_verb(2, "Starting R2 context on span %d\n", x + 1);
+ }
+ }
+ }
+ }
+#endif
/* And start the monitor for the first time */
restart_monitor();
return 0;
@@ -14199,8 +15255,10 @@
static int load_module(void)
{
int res;
-#if defined(HAVE_PRI) || defined(HAVE_SS7)
+#if defined(HAVE_PRI) || defined(HAVE_SS7)
int y, i;
+#elif defined(HAVE_OPENR2)
+ int i;
#endif
#ifdef HAVE_PRI
@@ -14228,6 +15286,12 @@
ss7_set_error(zt_ss7_error);
ss7_set_message(zt_ss7_message);
#endif /* HAVE_SS7 */
+#ifdef HAVE_OPENR2
+ memset(r2links, 0, sizeof(r2links));
+ for (i = 0; i < NUM_SPANS; i++) {
+ r2links[i].master = AST_PTHREADT_NULL;
+ }
+#endif
res = setup_zap(0);
/* Make sure we can register our Zap channel type */
if (res)
@@ -14245,6 +15309,9 @@
#ifdef HAVE_SS7
ast_cli_register_multiple(zap_ss7_cli, sizeof(zap_ss7_cli) / sizeof(zap_ss7_cli[0]));
#endif
+#ifdef HAVE_OPENR2
+ ast_cli_register_multiple(zap_mfcr2_cli, sizeof(zap_mfcr2_cli)/sizeof(zap_mfcr2_cli[0]));
+#endif
ast_cli_register_multiple(zap_cli, sizeof(zap_cli) / sizeof(struct ast_cli_entry));
Index: configure.ac
===================================================================
--- configure.ac (revision 114097)
+++ configure.ac (working copy)
@@ -234,6 +234,7 @@
AST_EXT_LIB_SETUP([PORTAUDIO], [PortAudio], [portaudio])
AST_EXT_LIB_SETUP([PRI], [ISDN PRI], [pri])
AST_EXT_LIB_SETUP([SS7], [ISDN SS7], [ss7])
+AST_EXT_LIB_SETUP([OPENR2], [MFR2], [openr2])
AST_EXT_LIB_SETUP([PWLIB], [PWlib], [pwlib])
AST_EXT_LIB_SETUP([OPENH323], [OpenH323], [h323])
AST_EXT_LIB_SETUP([RADIUS], [Radius Client], [radius])
@@ -1269,6 +1270,8 @@
AST_EXT_LIB_CHECK([SS7], [ss7], [isup_cqr], [libss7.h])
+AST_EXT_LIB_CHECK([OPENR2], [openr2], [openr2_chan_new], [openr2.h])
+
if test "${USE_PWLIB}" != "no"; then
if test -n "${PWLIB_DIR}"; then
PWLIBDIR="${PWLIB_DIR}"
Index: makeopts.in
===================================================================
--- makeopts.in (revision 114097)
+++ makeopts.in (working copy)
@@ -151,6 +151,9 @@
SS7_INCLUDE=@SS7_INCLUDE@
SS7_LIB=@SS7_LIB@
+OPENR2_INCLUDE=@OPENR2_INCLUDE@
+OPENR2_LIB=@OPENR2_LIB@
+
PWLIB_INCLUDE=@PWLIB_INCLUDE@
PWLIB_LIB=@PWLIB_LIB@