[asterisk-commits] moy: branch moy/mfcr2 r114872 - in /team/moy/mfcr2: ./ channels/ include/aste...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Apr 29 22:25:31 CDT 2008


Author: moy
Date: Tue Apr 29 22:25:30 2008
New Revision: 114872

URL: http://svn.digium.com/view/asterisk?view=rev&rev=114872
Log:
added first code to chan_zap for MFC/R2 support

Modified:
    team/moy/mfcr2/channels/chan_zap.c
    team/moy/mfcr2/configure
    team/moy/mfcr2/configure.ac
    team/moy/mfcr2/include/asterisk/autoconfig.h.in
    team/moy/mfcr2/makeopts.in

Modified: team/moy/mfcr2/channels/chan_zap.c
URL: http://svn.digium.com/view/asterisk/team/moy/mfcr2/channels/chan_zap.c?view=diff&rev=114872&r1=114871&r2=114872
==============================================================================
--- team/moy/mfcr2/channels/chan_zap.c (original)
+++ team/moy/mfcr2/channels/chan_zap.c Tue Apr 29 22:25:30 2008
@@ -44,6 +44,7 @@
 	<depend>tonezone</depend>
 	<use>pri</use>
 	<use>ss7</use>
+	<use>openr2</use>
  ***/
 
 #include "asterisk.h"
@@ -67,6 +68,10 @@
 
 #ifdef HAVE_SS7
 #include <libss7.h>
+#endif
+
+#ifdef HAVE_OPENR2
+#include <openr2.h>
 #endif
 
 #include "asterisk/lock.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)
@@ -379,6 +386,43 @@
 static int cur_networkindicator = -1;
 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
 
@@ -710,6 +754,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;
@@ -865,6 +918,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
@@ -1057,6 +1111,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);
 
@@ -1431,6 +1879,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:
@@ -2366,6 +2816,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;
@@ -2506,6 +2957,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;
@@ -3182,6 +3661,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
@@ -3235,7 +3721,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);
@@ -3421,6 +3907,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;
@@ -4101,8 +4593,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
@@ -4352,7 +4842,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)
@@ -5281,6 +5780,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;
@@ -5325,6 +5830,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 */
@@ -7725,7 +8247,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;
@@ -8064,6 +8586,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
@@ -8208,6 +8743,62 @@
 
 				linksets[span].called_nai = conf.ss7.called_nai;
 				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
@@ -8535,7 +9126,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);
 			}
@@ -8662,6 +9257,14 @@
 		/* Trust SS7 */
 		if (p->ss7) {
 			if (p->ss7call)
+				return 0;
+			else
+				return 1;
+		}
+#endif
+#ifdef HAVE_OPENR2
+		if (p->mfcr2) {
+			if (p->mfcr2call)
 				return 0;
 			else
 				return 1;
@@ -8956,6 +9559,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 */
@@ -9823,6 +10431,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)
@@ -11696,6 +12371,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 <group> | context <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 <group> */
+				if (tmp->group != targetnum) {
+					tmp = tmp->next;
+					continue;
+				}
+				break;
+			case 2: /* mfcr2 show channels context <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 <loglevel> <channel>\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] <channel>\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 <channel>\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;
@@ -12027,6 +12986,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);
@@ -12685,7 +13667,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
 
@@ -12696,6 +13678,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));
@@ -13106,7 +14097,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
@@ -13203,6 +14194,11 @@
 				return -1;
 			}
 		}
+#ifdef HAVE_OPENR2
+		if (!reload && r2links[mfcr2_cur_context_index].protocol_context) {
+			mfcr2_cur_context_index++;
+		}	
+#endif
 	}
 
 	return 0;
@@ -13660,6 +14656,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

[... 177 lines stripped ...]



More information about the asterisk-commits mailing list