Index: channels/chan_dahdi.c
===================================================================
--- channels/chan_dahdi.c	(.../tags/1.4.22)	(revision 160381)
+++ channels/chan_dahdi.c	(.../team/moy/mfcr2-1.4)	(revision 160381)
@@ -43,6 +43,7 @@
 	<depend>tonezone</depend>
 	<depend>res_features</depend>
 	<use>pri</use>
+	<use>openr2</use>
  ***/
 
 #include "asterisk.h"
@@ -71,6 +72,10 @@
 #include <libpri.h>
 #endif
 
+#ifdef HAVE_OPENR2
+#include <openr2.h>
+#endif
+
 #include "asterisk/lock.h"
 #include "asterisk/channel.h"
 #include "asterisk/config.h"
@@ -157,6 +162,9 @@
 #ifdef HAVE_PRI
                " w/PRI"
 #endif
+#ifdef HAVE_OPENR2
+	       " w/OPENR2"
+#endif
 ;
 
 #define SIG_EM		DAHDI_SIG_EM
@@ -175,6 +183,7 @@
 #define SIG_FXOGS	DAHDI_SIG_FXOGS
 #define SIG_FXOKS	DAHDI_SIG_FXOKS
 #define SIG_PRI		DAHDI_SIG_CLEAR
+#define SIG_MFCR2	DAHDI_SIG_CAS
 #define	SIG_SF		DAHDI_SIG_SF
 #define SIG_SFWINK 	(0x0100000 | DAHDI_SIG_SF)
 #define SIG_SF_FEATD	(0x0200000 | DAHDI_SIG_SF)
@@ -289,6 +298,39 @@
 
 static int ringt_base = DEFAULT_RINGT;
 
+#ifdef HAVE_OPENR2
+
+struct zt_mfcr2 {
+	pthread_t master;		       /*!< Thread of master */
+	openr2_context_t *protocol_context;    /*!< OpenR2 context handle */ 
+	struct dahdi_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 = OR2_VAR_UNKNOWN;
+static int mfcr2_cur_mfback_timeout = -1;
+static int mfcr2_cur_metering_pulse_timeout = -1;
+static int mfcr2_cur_max_ani = 10;
+static int mfcr2_cur_max_dnis = 4;
+static int mfcr2_cur_get_ani_first = -1;
+static int mfcr2_cur_context_index = 0;
+static int mfcr2_cur_call_files = 0;
+static int mfcr2_cur_allow_collect_calls = 0;
+static int mfcr2_cur_charge_calls = 1;
+static int mfcr2_cur_forced_release = 0;
+static int mfcr2_cur_double_answer = 0;
+static int mfcr2_cur_immediate_accept = -1;
+static char mfcr2_cur_logdir[OR2_MAX_PATH];
+static char mfcr2_cur_r2proto_file[OR2_MAX_PATH];
+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))
@@ -577,6 +619,19 @@
 	int prioffset;
 	int logicalspan;
 #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;
+	int mfcr2_charge_calls;
+	int mfcr2_allow_collect_calls;
+	int mfcr2_forced_release;
+	int mfcr2_dnis_index;
+	int mfcr2_ani_index;
+	int mfcr2_dnis_matched;
+#endif
 	int polarity;
 	int dsp_features;
 	char begindigit;
@@ -865,6 +920,422 @@
 #endif		
 }
 
+static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpbx, int index, int law, int transfercapability);
+#ifdef HAVE_OPENR2
+static void init_mfcr2_globals(void)
+{
+	int r;
+	mfcr2_cur_context_index = 0;
+	mfcr2_cur_variant = OR2_VAR_UNKNOWN;
+	mfcr2_cur_mfback_timeout = -1;
+	mfcr2_cur_metering_pulse_timeout = -1;
+	mfcr2_cur_max_ani = 10;
+	mfcr2_cur_max_dnis = 4;
+	mfcr2_cur_get_ani_first = -1;
+	mfcr2_cur_call_files = 0;
+	mfcr2_cur_allow_collect_calls = 0;
+	mfcr2_cur_forced_release = 0;
+	mfcr2_cur_double_answer = 0;
+	mfcr2_cur_immediate_accept = -1;
+	mfcr2_cur_loglevel = OR2_LOG_ERROR | OR2_LOG_WARNING;
+	mfcr2_cur_category = OR2_CALLING_PARTY_CATEGORY_NATIONAL_SUBSCRIBER;
+	memset(mfcr2_cur_logdir, 0, sizeof(mfcr2_cur_logdir));
+	memset(mfcr2_cur_r2proto_file, 0, sizeof(mfcr2_cur_r2proto_file));
+	memset(r2links, 0, sizeof(r2links));
+	for (r = 0; r < NUM_SPANS; r++) {
+		r2links[r].master = AST_PTHREADT_NULL;
+	}
+}
+
+static openr2_calling_party_category_t dahdi_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 dahdi_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 dahdi_r2_on_call_init(openr2_chan_t *r2chan)
+{
+	struct dahdi_pvt *p = openr2_chan_get_client_data(r2chan);
+	ast_mutex_lock(&p->lock);
+	if (p->mfcr2call) {
+		ast_mutex_unlock(&p->lock);
+		/* TODO: This can happen when some other thread just finished zt_request requesting this very same
+		interface but has not yet seized the line (zt_call), and the far end wins and seize the line,
+		can we avoid this somehow?, at this point when zt_call send the seize, it is likely that since
+		the other end will see our seize as a forced release and drop the call, we will see an invalid
+		pattern that will be seen and treated as protocol error. */
+		ast_log(LOG_ERROR, "Collision of calls on chan %d detected!.\n", openr2_chan_get_number(r2chan));
+		return;
+	}
+	p->mfcr2call = 1;
+	/* better safe than sorry ... */
+	p->cid_name[0] = 0;
+	p->cid_num[0] = 0;
+	p->rdnis[0] = 0;
+	p->exten[0] = 0;
+	p->mfcr2_ani_index = 0;
+	p->mfcr2_dnis_index = 0;
+	p->mfcr2_dnis_matched = 0;
+	ast_mutex_unlock(&p->lock);
+	ast_log(LOG_NOTICE, "New MFC/R2 call detected on chan %d.\n", openr2_chan_get_number(r2chan));
+}
+
+static void handle_alarms(struct dahdi_pvt *p, int alarms);
+static int get_alarms(struct dahdi_pvt *p);
+static void dahdi_r2_on_hardware_alarm(openr2_chan_t *r2chan, int alarm)
+{
+	int res;
+	struct dahdi_pvt *p = openr2_chan_get_client_data(r2chan);
+	ast_mutex_lock(&p->lock);
+	p->inalarm = alarm ? 1 : 0;
+	if (p->inalarm) {
+		res = get_alarms(p);
+		/* unknown_alarm may be set here */
+		handle_alarms(p, res);
+	} else {
+		if (!p->unknown_alarm) {
+			ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", p->channel);
+			manager_event(EVENT_FLAG_SYSTEM, "AlarmClear", "Channel: %d\r\n", p->channel);
+		} else {
+			p->unknown_alarm = 0;
+		}
+	}
+	ast_mutex_unlock(&p->lock);
+	ast_log(LOG_WARNING, "Zap alarm on chan %d.\n", openr2_chan_get_number(r2chan));
+}
+
+static void dahdi_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 dahdi_r2_on_protocol_error(openr2_chan_t *r2chan, openr2_protocol_error_t reason)
+{
+	struct dahdi_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;
+	} 
+	ast_mutex_lock(&p->lock);
+	p->mfcr2call = 0;
+	ast_mutex_unlock(&p->lock);
+}
+
+static void dahdi_r2_on_call_offered(openr2_chan_t *r2chan, const char *ani, const char *dnis, openr2_calling_party_category_t category)
+{
+	struct dahdi_pvt *p;
+	ast_log(LOG_NOTICE, "MFC/R2 call offered on chan %d. ANI = %s, DNIS = %s, Category = %s\n", 
+			openr2_chan_get_number(r2chan), ani ? ani : "(restricted)", dnis, openr2_proto_get_category_string(category));
+	p = openr2_chan_get_client_data(r2chan);
+	if (!p->mfcr2_allow_collect_calls && category == OR2_CALLING_PARTY_CATEGORY_COLLECT_CALL) {
+		ast_log(LOG_NOTICE, "Rejecting MFC/R2 collect call\n");
+		openr2_chan_disconnect_call(r2chan, OR2_CAUSE_COLLECT_CALL_REJECTED);
+		return;
+	}
+	ast_mutex_lock(&p->lock);
+	p->mfcr2_recvd_category = category;
+	/* if we're not supposed to use CID, clear whatever we have */
+	if (!p->use_callerid) {
+		ast_log(LOG_DEBUG, "No CID allowed in configuration, CID is being cleared!\n");
+		p->cid_num[0] = 0;
+		p->cid_name[0] = 0;
+	}
+	/* if we're supposed to answer immediately, clear DNIS and set 's' exten */
+	if (p->immediate || !openr2_context_get_max_dnis(openr2_chan_get_context(r2chan))) {
+		ast_log(LOG_DEBUG, "Setting exten => s because of immediate or 0 DNIS configured\n");
+		p->exten[0] = 's';
+		p->exten[1] = 0;
+	}
+	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_UNALLOCATED_NUMBER);
+	} else {
+		if (p->mfcr2_charge_calls) {
+			openr2_chan_accept_call(r2chan, OR2_CALL_WITH_CHARGE);
+		} else {
+			openr2_chan_accept_call(r2chan, OR2_CALL_NO_CHARGE);
+		}	
+	}	
+}
+
+static void dahdi_r2_on_call_end(openr2_chan_t *r2chan)
+{
+	struct dahdi_pvt *p = openr2_chan_get_client_data(r2chan);
+	ast_log(LOG_NOTICE, "MFC/R2 call end on chan %d\n", p->channel);
+	ast_mutex_lock(&p->lock);
+	p->mfcr2call = 0;
+	ast_mutex_unlock(&p->lock);
+}
+
+static void dahdi_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 dahdi_enable_ec(struct dahdi_pvt *p);
+static void dahdi_r2_on_call_accepted(openr2_chan_t *r2chan, openr2_call_mode_t mode)
+{
+	struct dahdi_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);
+	dahdi_enable_ec(p);
+	if (OR2_DIR_BACKWARD == openr2_chan_get_direction(r2chan)) {
+		c = dahdi_new(p, AST_STATE_RING, 1, SUB_REAL, DAHDI_LAW_ALAW, 0);
+		if (c) {
+			dahdi_r2_update_monitor_count(p->mfcr2, 0);
+			/* chan_dahdi 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;
+		p->dialing = 0;
+		/* chan_dahdi will take care of reading from now on, tell the library to forget about it */
+		openr2_chan_disable_read(r2chan);
+	}	
+}
+
+static void dahdi_r2_on_call_answered(openr2_chan_t *r2chan)
+{
+	struct dahdi_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 dahdi_r2_on_call_read(openr2_chan_t *r2chan, const unsigned char *buf, int buflen)
+{
+	/*ast_log(LOG_DEBUG, "Read data from dahdi channel %d\n", openr2_chan_get_number(r2chan));*/
+}
+
+/*static int dahdi_r2_cause_to_ast_cause(openr2_call_disconnect_cause_t cause)
+{
+	switch (cause) {
+	case OR2_CAUSE_BUSY_NUMBER:
+		return AST_CAUSE_BUSY;
+	case OR2_CAUSE_NETWORK_CONGESTION:
+		return AST_CAUSE_CONGESTION;
+	case OR2_CAUSE_OUT_OF_ORDER:
+		return AST_CAUSE_DESTINATION_OUT_OF_ORDER;
+	case OR2_CAUSE_UNALLOCATED_NUMBER:
+		return AST_CAUSE_UNREGISTERED;
+	case OR2_CAUSE_NO_ANSWER:
+		return AST_CAUSE_NO_ANSWER;
+	case OR2_CAUSE_NORMAL_CLEARING:
+		return AST_CAUSE_NORMAL_CLEARING;
+	case OR2_CAUSE_UNSPECIFIED:
+	default:
+		return AST_CAUSE_NOTDEFINED;
+	}	
+}*/
+
+static void dahdi_r2_on_call_disconnect(openr2_chan_t *r2chan, openr2_call_disconnect_cause_t cause)
+{
+	struct dahdi_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));
+	ast_mutex_lock(&p->lock);
+	if (p->owner) {
+		/* when we have an owner we don't call openr2_chan_disconnect_call here, that will
+		   be done in zt_hangup */
+		if (p->owner->_state == AST_STATE_UP) {
+			p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+			ast_mutex_unlock(&p->lock);
+		} else if (openr2_chan_get_direction(r2chan) == OR2_DIR_FORWARD) {
+			/* being the forward side we must report what happened to the call to whoever requested it */
+			switch (cause) {
+			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_UNALLOCATED_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;
+			}
+			ast_mutex_unlock(&p->lock);
+		} else {
+			ast_mutex_unlock(&p->lock);
+			/* being the backward side and not UP yet, we only need to request hangup */
+			/* TODO: what about doing this same thing when were AST_STATE_UP? */
+			ast_queue_hangup(p->owner);
+		}	
+	} else {
+		ast_mutex_unlock(&p->lock);
+		/* no owner, therefore we can't use zt_hangup to disconnect, do it right now */
+		openr2_chan_disconnect_call(r2chan, OR2_CAUSE_NORMAL_CLEARING);
+	}
+}
+
+static void dahdi_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:
+	case OR2_LOG_EX_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 dahdi_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 dahdi_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 dahdi_r2_on_context_log(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);
+	dahdi_r2_write_log(level, completemsg);
+}
+
+static void dahdi_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);
+	dahdi_r2_write_log(level, completemsg);
+}
+
+static int dahdi_r2_on_dnis_digit_received(openr2_chan_t *r2chan, char digit)
+{
+	struct dahdi_pvt *p = openr2_chan_get_client_data(r2chan);
+	/* if 'immediate' is set, let's stop requesting DNIS */
+	if (p->immediate) {
+		return 0;
+	}
+	p->exten[p->mfcr2_dnis_index] = digit;
+	p->rdnis[p->mfcr2_dnis_index] = digit;
+	p->mfcr2_dnis_index++;
+	p->exten[p->mfcr2_dnis_index] = 0;
+	p->rdnis[p->mfcr2_dnis_index] = 0;
+	/*
+	ast_log(LOG_DEBUG, "Got digit %c in dahdi, dnis so far: %s\n", digit, p->exten);
+	int ret;
+	ret = ast_exists_extension(NULL, p->context, p->exten, 1, p->cid_num);
+	ast_log(LOG_DEBUG, "ast_exists_extension(%s, %s, 1, %s) = %d\n", p->context, p->exten, p->cid_num, ret);
+	ret = ast_matchmore_extension(NULL, p->context, p->exten, 1, p->cid_num);
+	ast_log(LOG_DEBUG, "ast_matchmore_extension(%s, %s, 1, %s) = %d\n", p->context, p->exten, p->cid_num, ret);
+	*/
+	/* if the DNIS is a match and cannot match more, stop requesting DNIS */
+	if ((p->mfcr2_dnis_matched || 
+	    (ast_exists_extension(NULL, p->context, p->exten, 1, p->cid_num) && (p->mfcr2_dnis_matched = 1))) &&
+	    !ast_matchmore_extension(NULL, p->context, p->exten, 1, p->cid_num)) {
+		return 0;
+	}
+	/* otherwise keep going */
+	return 1;
+}
+
+static void dahdi_r2_on_ani_digit_received(openr2_chan_t *r2chan, char digit)
+{
+	struct dahdi_pvt *p = openr2_chan_get_client_data(r2chan);
+	p->cid_num[p->mfcr2_ani_index] = digit;
+	p->cid_name[p->mfcr2_ani_index] = digit;
+	p->mfcr2_ani_index++;
+	p->cid_num[p->mfcr2_ani_index] = 0;
+	p->cid_name[p->mfcr2_ani_index] = 0;
+}
+
+static openr2_event_interface_t dahdi_r2_event_iface = {
+	.on_call_init = dahdi_r2_on_call_init,
+	.on_call_offered = dahdi_r2_on_call_offered,
+	.on_call_accepted = dahdi_r2_on_call_accepted,
+	.on_call_answered = dahdi_r2_on_call_answered,
+	.on_call_disconnect = dahdi_r2_on_call_disconnect,
+	.on_call_end = dahdi_r2_on_call_end,
+	.on_call_read = dahdi_r2_on_call_read,
+	.on_hardware_alarm = dahdi_r2_on_hardware_alarm,
+	.on_os_error = dahdi_r2_on_os_error,
+	.on_protocol_error = dahdi_r2_on_protocol_error,
+	.on_line_blocked = dahdi_r2_on_line_blocked,
+	.on_line_idle = dahdi_r2_on_line_idle,
+	.on_context_log = dahdi_r2_on_context_log,
+	.on_dnis_digit_received = dahdi_r2_on_dnis_digit_received,
+	.on_ani_digit_received = dahdi_r2_on_ani_digit_received,
+	/* so far we do nothing with billing pulses */
+	.on_billing_pulse_received = NULL
+};
+
+static inline int16_t dahdi_r2_alaw_to_linear(uint8_t sample)
+{
+	return AST_ALAW(sample);
+}
+
+static inline uint8_t dahdi_r2_linear_to_alaw(int sample)
+{
+	return AST_LIN2A(sample);
+}
+
+static openr2_transcoder_interface_t dahdi_r2_transcode_iface = {
+	dahdi_r2_alaw_to_linear,
+	dahdi_r2_linear_to_alaw
+};
+
+#endif /* HAVE_OPENR2 */
+
 static int restore_gains(struct dahdi_pvt *p);
 
 static void swap_subs(struct dahdi_pvt *p, int a, int b)
@@ -1245,6 +1716,8 @@
 		return "FXO Kewlstart";
 	case SIG_PRI:
 		return "ISDN PRI";
+	case SIG_MFCR2:
+		return "MFC/R2";
 	case SIG_SF:
 		return "SF (Tone) Immediate";
 	case SIG_SFWINK:
@@ -2085,6 +2558,7 @@
 		ast_setstate(ast, AST_STATE_UP);
 		break;		
 	case SIG_PRI:
+	case SIG_MFCR2:
 		/* We'll get it in a moment -- but use dialdest to store pre-setup_ack digits */
 		p->dialdest[0] = '\0';
 		break;
@@ -2093,6 +2567,35 @@
 		ast_mutex_unlock(&p->lock);
 		return -1;
 	}
+#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;
+		}
+		p->dialing = 1;
+		callres = openr2_chan_make_call(p->r2chan, l, (c + strip), dahdi_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;
@@ -2678,6 +3181,17 @@
 		if (res < 0) 
 			ast_log(LOG_WARNING, "Unable to set law on channel %d to default: %s\n", p->channel, strerror(errno));
 		/* Perform low level hangup if no owner left */
+#ifdef HAVE_OPENR2
+		if (p->mfcr2) {
+			ast_log(LOG_DEBUG, "disconnecting MFC/R2 call on chan %d\n", p->channel);
+			if (openr2_chan_get_direction(p->r2chan) == OR2_DIR_BACKWARD && p->mfcr2_forced_release) {
+				openr2_chan_disconnect_call(p->r2chan, OR2_CAUSE_FORCED_RELEASE);
+			} else {
+				openr2_chan_disconnect_call(p->r2chan, OR2_CAUSE_NORMAL_CLEARING);
+			}
+			dahdi_r2_update_monitor_count(p->mfcr2, 1);
+		}	
+#endif
 #ifdef HAVE_PRI
 		if (p->pri) {
 #ifdef SUPPORT_USERUSER
@@ -2731,7 +3245,7 @@
 			}
 		}
 #endif
-		if (p->sig && (p->sig != SIG_PRI))
+		if (p->sig && (p->sig != SIG_PRI) && (p->sig != SIG_MFCR2))
 			res = dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_ONHOOK);
 		if (res < 0) {
 			ast_log(LOG_WARNING, "Unable to hangup line %s\n", ast->name);
@@ -2910,6 +3424,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;
@@ -3588,8 +4108,6 @@
 
 static void *ss_thread(void *data);
 
-static struct ast_channel *dahdi_new(struct dahdi_pvt *, int, int, int, int, int);
-
 static int attempt_transfer(struct dahdi_pvt *p)
 {
 	/* In order to transfer, we need at least one of the channels to
@@ -3861,13 +4379,29 @@
 			break;
 #endif
 		case DAHDI_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_chan_handle_cas(p->r2chan);
+			}
+#else
 			ast_log(LOG_WARNING, "Recieved bits changed on %s signalling?\n", sig2str(p->sig));
+#endif
 		case DAHDI_EVENT_PULSE_START:
 			/* Stop tone if there's a pulse start and the PBX isn't started */
 			if (!ast->pbx)
 				tone_zone_play_tone(p->subs[index].dfd, -1);
 			break;	
 		case DAHDI_EVENT_DIALCOMPLETE:
+#ifdef HAVE_OPENR2
+			if ((p->sig & SIG_MFCR2) && p->r2chan && ast->_state != AST_STATE_UP) {
+				/* we don't need to do anything for this event for R2 signaling 
+				   if the call is being setup */
+				break;
+			}
+#endif
 			if (p->inalarm) break;
 			if ((p->radio || (p->oprmode < 0))) break;
 			if (ioctl(p->subs[index].dfd,DAHDI_DIALING,&x) == -1) {
@@ -3948,6 +4482,10 @@
 				break;
 			}
 #endif
+#ifdef HAVE_OPENR2
+			if (p->sig == SIG_MFCR2)
+				break;
+#endif
 		case DAHDI_EVENT_ONHOOK:
 			if (p->radio) {
 				p->subs[index].f.frametype = AST_FRAME_CONTROL;
@@ -4809,6 +5347,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;
@@ -4853,6 +5397,24 @@
 		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 */
@@ -6976,7 +7538,7 @@
 		count = 0;
 		i = iflist;
 		while (i) {
-			if ((i->subs[SUB_REAL].dfd > -1) && i->sig && (!i->radio)) {
+			if ((i->subs[SUB_REAL].dfd > -1) && i->sig && (!i->radio) && !(i->sig & SIG_MFCR2)) {
 				if (!i->owner && !i->subs[SUB_REAL].owner) {
 					/* This needs to be watched, as it lacks an owner */
 					pfds[count].fd = i->subs[SUB_REAL].dfd;
@@ -7156,6 +7718,17 @@
 	return 0;
 }
 
+#ifdef HAVE_OPENR2
+static struct zt_mfcr2 *mfcr2_get_context(int id)
+{
+	if ((id < 0) || (id >= (sizeof(r2links)/sizeof(r2links[0])))) {
+		ast_log(LOG_ERROR, "No more R2 links available!.\n");
+		return NULL;
+	}
+	return &r2links[id];
+}
+#endif
+
 #ifdef HAVE_PRI
 static int pri_resolve_span(int *span, int channel, int offset, struct dahdi_spaninfo *si)
 {
@@ -7506,6 +8079,84 @@
 				tmp->prioffset = 0;
 			}
 #endif
+#ifdef HAVE_OPENR2
+			if (chan_sig == SIG_MFCR2 && reloading != 1) {
+				char logdir[OR2_MAX_PATH];
+				struct zt_mfcr2 *dahdi_r2;
+				int threshold = 0;
+				int snres = 0;
+				dahdi_r2 = mfcr2_get_context(mfcr2_cur_context_index);
+				if (!dahdi_r2) {
+					ast_log(LOG_WARNING, "Cannot get another R2 DAHDI context!\n");
+				} else if (!dahdi_r2->protocol_context){
+					char tmplogdir[] = "/tmp";
+					dahdi_r2->protocol_context = openr2_context_new(NULL, &dahdi_r2_event_iface, 
+							&dahdi_r2_transcode_iface, mfcr2_cur_variant, mfcr2_cur_max_ani, mfcr2_cur_max_dnis);
+					if (!dahdi_r2->protocol_context) {
+						ast_log(LOG_ERROR, "Cannot create OpenR2 protocol context.\n");
+						destroy_dahdi_pvt(&tmp);
+						return NULL;
+					} 
+					openr2_context_set_log_level(dahdi_r2->protocol_context, mfcr2_cur_loglevel);
+					openr2_context_set_ani_first(dahdi_r2->protocol_context, mfcr2_cur_get_ani_first);
+					openr2_context_set_mf_threshold(dahdi_r2->protocol_context, threshold);
+					openr2_context_set_mf_back_timeout(dahdi_r2->protocol_context, mfcr2_cur_mfback_timeout);
+					openr2_context_set_metering_pulse_timeout(dahdi_r2->protocol_context, mfcr2_cur_metering_pulse_timeout);
+					openr2_context_set_double_answer(dahdi_r2->protocol_context, mfcr2_cur_double_answer);
+					openr2_context_set_immediate_accept(dahdi_r2->protocol_context, mfcr2_cur_immediate_accept);
+					if (ast_strlen_zero(mfcr2_cur_logdir)) {
+						if (openr2_context_set_log_directory(dahdi_r2->protocol_context, tmplogdir)) {
+							ast_log(LOG_ERROR, "Failed setting default MFC/R2 log directory %s\n", tmplogdir);
+						}
+					} 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 %s\n", tmplogdir);
+							if (openr2_context_set_log_directory(dahdi_r2->protocol_context, logdir)) {
+								ast_log(LOG_ERROR, "Failed setting default MFC/R2 log directory %s\n", tmplogdir);
+							}
+						} else {
+							if (openr2_context_set_log_directory(dahdi_r2->protocol_context, logdir)) {
+								ast_log(LOG_ERROR, "Failed setting MFC/R2 log directory %s\n", logdir);
+							}
+						}	
+					}
+					if (!ast_strlen_zero(mfcr2_cur_r2proto_file)) {
+						if (openr2_context_configure_from_advanced_file(dahdi_r2->protocol_context, mfcr2_cur_r2proto_file)) {
+							ast_log(LOG_ERROR, "Failed to configure r2context from advanced configuration file %s\n", mfcr2_cur_r2proto_file);
+						}
+					}
+					ast_cond_init(&dahdi_r2->do_monitor, NULL);
+					ast_mutex_init(&dahdi_r2->monitored_count_lock);
+					dahdi_r2->monitored_count = 0;
+				} 
+				if (dahdi_r2) {
+					/* TODO: should we check numchans overflow, or is it already done by DAHDI? */
+					dahdi_r2->pvts[dahdi_r2->numchans++] = tmp;
+					tmp->r2chan = openr2_chan_new_from_fd(dahdi_r2->protocol_context, tmp->subs[SUB_REAL].dfd, NULL, NULL);
+					if (!tmp->r2chan) {
+						ast_log(LOG_ERROR, "Cannot create OpenR2 channel.\n");
+						destroy_dahdi_pvt(&tmp);
+						return NULL;
+					}
+					openr2_chan_set_client_data(tmp->r2chan, tmp);
+					openr2_chan_set_logging_func(tmp->r2chan, dahdi_r2_on_chan_log);
+					openr2_chan_set_log_level(tmp->r2chan, mfcr2_cur_loglevel);
+					if (mfcr2_cur_call_files) {
+						openr2_chan_enable_call_files(tmp->r2chan);
+					}
+					tmp->mfcr2_category = mfcr2_cur_category;
+					tmp->mfcr2 = dahdi_r2;
+					tmp->mfcr2call = 0;
+					tmp->mfcr2_charge_calls = mfcr2_cur_charge_calls;
+					tmp->mfcr2_ani_index = 0;
+					tmp->mfcr2_dnis_index = 0;
+					tmp->mfcr2_allow_collect_calls = mfcr2_cur_allow_collect_calls;
+					tmp->mfcr2_forced_release = mfcr2_cur_forced_release;
+					dahdi_r2->monitored_count++;
+				}
+			}
+#endif
 		} else {
 			chan_sig = tmp->sig;
 			memset(&p, 0, sizeof(p));
@@ -7691,7 +8342,7 @@
 				ast_dsp_digitmode(tmp->dsp, DSP_DIGITMODE_DTMF | tmp->dtmfrelax);
 			update_conf(tmp);
 			if (!here) {
-				if (chan_sig != SIG_PRI)
+				if ((chan_sig != SIG_PRI) && (chan_sig != SIG_MFCR2))
 					/* Hang it up to be sure it's good */
 					dahdi_set_hook(tmp->subs[SUB_REAL].dfd, DAHDI_ONHOOK);
 			}
@@ -7813,6 +8464,15 @@
 				return 1;
 		}
 #endif
+#ifdef HAVE_OPENR2
+		/* Trust MFC/R2 */
+		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))
@@ -8092,6 +8752,19 @@
 				}
 			}
 #endif			
+#ifdef HAVE_OPENR2
+			if (p->mfcr2) {
+				ast_mutex_lock(&p->lock);
+				if (p->mfcr2call) {
+					ast_mutex_unlock(&p->lock);
+					ast_log(LOG_DEBUG, "Yay!, someone just beat us in the race for channel %d.\n", p->channel);
+					goto next;
+				}
+				p->mfcr2call = 1;
+				ast_mutex_unlock(&p->lock);
+				dahdi_r2_update_monitor_count(p->mfcr2, 0);
+			}
+#endif
 			if (p->channel == CHAN_PSEUDO) {
 				p = chandup(p);
 				if (!p) {
@@ -8167,7 +8840,81 @@
 	return tmp;
 }
 
+#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;
+	int quit_loop = 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_chan_set_idle(mfcr2->pvts[i]->r2chan);
+		openr2_chan_handle_cas(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]->r2chan) {
+				ast_log(LOG_DEBUG, "Wow, no r2chan on channel %d\n", mfcr2->pvts[i]->channel);
+				quit_loop = 1;
+				break;
+			}
+			openr2_chan_enable_read(mfcr2->pvts[i]->r2chan);
+			pollers[i].events = POLLIN | POLLPRI;
+			pollers[i].fd = mfcr2->pvts[i]->subs[SUB_REAL].dfd;
+		}
+		ast_mutex_unlock(&mfcr2->monitored_count_lock);
+		if (quit_loop) {
+			break;
+		}
+		/* 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);
+	}
+	ast_log(LOG_NOTICE, "Quitting MFC/R2 monitor thread\n");
+	return 0;
+}
+#endif
+
+
 #ifdef HAVE_PRI
 static struct dahdi_pvt *pri_find_crv(struct dahdi_pri *pri, int crv)
 {
@@ -10031,6 +10778,330 @@
 
 #endif /* HAVE_PRI */
 
+#ifdef HAVE_OPENR2
+
+static int handle_mfcr2_version(int fd, int argc, char *argv[])
+{
+	ast_cli(fd, "OpenR2 version: %s, revision: %s\n", openr2_get_version(), openr2_get_revision());
+	return RESULT_SUCCESS;
+}
+
+static int handle_mfcr2_show_variants(int fd, int argc, char *argv[]) 
+{
+#define FORMAT "%4s %40s\n"
+	int numvariants = 0;
+	int i;
+	const openr2_variant_entry_t *variants;
+	if (!(variants = openr2_proto_get_variant_list(&numvariants))) {
+		ast_cli(fd, "Failed to get list of variants.\n");
+		return RESULT_FAILURE;
+	}
+	ast_cli(fd, FORMAT, "Variant Code", "Country");
+	for (i = 0; i < numvariants; i++) {
+		ast_cli(fd, FORMAT, variants[i].name, variants[i].country);
+	}
+	return RESULT_SUCCESS;
+#undef FORMAT
+}	
+
+static int handle_mfcr2_show_channels(int fd, int argc, char *argv[])
+{
+#define FORMAT "%4s %-7.7s %-7.7s %-8.8s %-9.9s %-16.16s %-8.8s %-8.8s\n"
+	int filtertype = 0;
+	int targetnum = 0;
+	char channo[5];
+	char anino[5];
+	char dnisno[5];
+	struct dahdi_pvt *p;
+	openr2_context_t *r2context;
+	openr2_variant_t r2variant;
+	if (!((argc == 3) || (argc == 5))) {
+		return RESULT_SHOWUSAGE;
+	}
+	if (argc == 5) {
+		if (!strcasecmp(argv[3], "group")) {
+			targetnum = atoi(argv[4]);
+			if ((targetnum < 0) || (targetnum > 63))
+				return RESULT_SHOWUSAGE;
+			targetnum = 1 << targetnum;
+			filtertype = 1;
+		} else if (!strcasecmp(argv[3], "context")) {
+			filtertype = 2;
+		} else {
+			return RESULT_SHOWUSAGE;
+		}
+	}
+	ast_cli(fd, FORMAT, "Chan", "Variant", "Max ANI", "Max DNIS", "ANI First", "Immediate Accept", "Tx CAS", "Rx CAS");
+	ast_mutex_lock(&iflock);
+	p = iflist;
+	while (p) {
+		if (!(p->sig & SIG_MFCR2) || !p->r2chan) {
+			p = p->next;
+			continue;
+		}
+		if (filtertype) {
+			switch(filtertype) {
+			case 1: /* mfcr2 show channels group <group> */
+				if (p->group != targetnum) {
+					p = p->next;
+					continue;
+				}
+				break;
+			case 2: /* mfcr2 show channels context <context> */
+				if (strcasecmp(p->context, argv[4])) {
+					p= p->next;
+					continue;
+				}
+				break;
+			default:
+				;
+			}
+		}
+		r2context = openr2_chan_get_context(p->r2chan);
+		r2variant = openr2_context_get_variant(r2context);
+		snprintf(channo, sizeof(channo), "%d", p->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(fd, FORMAT, channo, openr2_proto_get_variant_string(r2variant), 
+				anino, dnisno, openr2_context_get_ani_first(r2context) ? "Yes" : "No",  
+				openr2_context_get_immediate_accept(r2context) ? "Yes" : "No",
+				openr2_chan_get_tx_cas_string(p->r2chan), openr2_chan_get_rx_cas_string(p->r2chan));
+		p = p->next;
+	}
+	ast_mutex_unlock(&iflock);
+	return RESULT_SUCCESS;
+#undef FORMAT
+}
+
+static int handle_mfcr2_set_debug(int fd, int argc, char *argv[])
+{
+	struct dahdi_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;
+	if (argc < 4) {
+		return RESULT_SHOWUSAGE;
+	}
+	channo = (argc == 5) ? atoi(argv[4]) : -1;
+	logval = ast_strdupa(argv[3]);
+	toklevel = strtok_r(logval, ",", &saveptr);
+	if (-1 == (tmplevel = openr2_log_get_level(toklevel))) {
+		ast_cli(fd, "Invalid MFC/R2 logging level '%s'.\n", argv[3]);
+		return RESULT_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(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->r2chan) {
+			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(fd, "MFC/R2 debugging set to '%s' for channel %d.\n", argv[3], p->channel);
+			break;
+		} else {
+			p = p->next;
+		}
+	}
+	if ((channo != -1) && !p) {
+		ast_cli(fd, "MFC/R2 channel %d not found.\n", channo);
+	}
+	if (channo == -1) {
+		ast_cli(fd, "MFC/R2 debugging set to '%s' for all channels.\n", argv[3]);
+	}
+	ast_mutex_unlock(&iflock);
+	return RESULT_SUCCESS;
+}
+
+static int handle_mfcr2_call_files(int fd, int argc, char *argv[])
+{
+	struct dahdi_pvt *p = NULL;
+	int channo = 0;
+	if (argc < 4) {
+		return RESULT_SHOWUSAGE;
+	}
+	channo = (argc == 5) ? atoi(argv[4]) : -1;
+	ast_mutex_lock(&iflock);
+	p = iflist;
+	while (p) {
+		if (!(p->sig & SIG_MFCR2) || !p->r2chan) {
+			p = p->next;
+			continue;
+		}
+		if ((channo != -1) && (p->channel != channo )) {
+			p = p->next;
+			continue;
+		}
+		if (ast_true(argv[3])) {
+			openr2_chan_enable_call_files(p->r2chan);
+		} else {
+			openr2_chan_disable_call_files(p->r2chan);
+		}
+		if (channo != -1) {
+			if (ast_true(argv[3])) {
+				ast_cli(fd, "MFC/R2 call files enabled for channel %d.\n", p->channel);
+			} else {
+				ast_cli(fd, "MFC/R2 call files disabled for channel %d.\n", p->channel);
+			}
+			break;
+		} else {
+			p = p->next;
+		}
+	}
+	if ((channo != -1) && !p) {
+		ast_cli(fd, "MFC/R2 channel %d not found.\n", channo);
+	}
+	if (channo == -1) {
+		if (ast_true(argv[3])) {
+			ast_cli(fd, "MFC/R2 Call files enabled for all channels.\n");
+		} else {
+			ast_cli(fd, "MFC/R2 Call files disabled for all channels.\n");
+		}	
+	}
+	ast_mutex_unlock(&iflock);
+	return RESULT_SUCCESS;
+}	
+
+static int handle_mfcr2_set_idle(int fd, int argc, char *argv[])
+{
+	struct dahdi_pvt *p = NULL;
+	int channo = 0;
+	channo = (argc == 4) ? atoi(argv[3]) : -1;
+	ast_mutex_lock(&iflock);
+	p = iflist;
+	while (p) {
+		if (!(p->sig & SIG_MFCR2) || !p->r2chan) {
+			p = p->next;
+			continue;
+		}
+		if ((channo != -1) && (p->channel != channo )) {
+			p = p->next;
+			continue;
+		}
+		openr2_chan_set_idle(p->r2chan);
+		if (channo != -1) {
+			break;
+		} else {
+			p = p->next;
+		}
+	}
+	if ((channo != -1) && !p) {
+		ast_cli(fd, "MFC/R2 channel %d not found.\n", channo);
+	}
+	ast_mutex_unlock(&iflock);
+	return RESULT_SUCCESS;
+}
+
+static int handle_mfcr2_set_blocked(int fd, int argc, char *argv[])
+{
+	struct dahdi_pvt *p = NULL;
+	int channo = 0;
+	channo = (argc == 4) ? atoi(argv[3]) : -1;
+	ast_mutex_lock(&iflock);
+	p = iflist;
+	while (p) {
+		if (!(p->sig & SIG_MFCR2) || !p->r2chan) {
+			p = p->next;
+			continue;
+		}
+		if ((channo != -1) && (p->channel != channo )) {
+			p = p->next;
+			continue;
+		}
+		openr2_chan_set_blocked(p->r2chan);
+		if (channo != -1) {
+			break;
+		} else {
+			p = p->next;
+		}
+	}
+	if ((channo != -1) && !p) {
+		ast_cli(fd, "MFC/R2 channel %d not found.\n", channo);
+	}
+	ast_mutex_unlock(&iflock);
+	return RESULT_SUCCESS;
+}
+
+static const char dahdi_r2_version_help[] = 
+			"Usage: mfcr2 show version\n"
+			"       Shows the version of the OpenR2 library being used.\n";
+static const char dahdi_r2_variants_help[] = 
+			"Usage: mfcr2 show variants\n"
+			"       Show supported MFC/R2 variants.\n";
+static const char dahdi_r2_showchannels_help[] =
+			"Usage: mfcr2 show channels [group <group> | context <context>]\n"
+			"       Shows the zap channels configured with MFC/R2 signaling.\n";
+static const char dahdi_r2_setdebug_help[] =
+			"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";
+static const char dahdi_r2_callfiles_help[] =
+			"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";
+static const char dahdi_r2_setidle_help[] =
+			"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";
+static const char dahdi_r2_setblocked_help[] =
+			"Usage: mfcr2 set blocked <channel>\n"
+			"       DON'T USE THIS UNLESS YOU KNOW WHAT YOU ARE DOING.\n"
+			"       Force the given channel into BLOCKED state.\n"
+			"       If no channel is specified, all channels will be set to BLOCKED.\n";
+
+static struct ast_cli_entry dahdi_mfcr2_cli[] = {
+	{ { "mfcr2", "show", "version", NULL },
+	handle_mfcr2_version, "Show OpenR2 library version",
+	dahdi_r2_version_help },
+
+	{ { "mfcr2", "show", "variants", NULL },
+	handle_mfcr2_show_variants, "Show supported MFC/R2 variants",
+	dahdi_r2_variants_help },
+
+	{ { "mfcr2", "show", "channels", NULL },
+	handle_mfcr2_show_channels, "Show MFC/R2 channels",
+	dahdi_r2_showchannels_help },
+
+	{ { "mfcr2", "set", "debug", NULL },
+	handle_mfcr2_set_debug, "Set MFC/R2 channel logging level",
+	dahdi_r2_setdebug_help },
+
+	{ { "mfcr2", "call", "files", NULL },
+	handle_mfcr2_call_files, "Enable/Disable MFC/R2 call files",
+	dahdi_r2_callfiles_help },
+
+	{ { "mfcr2", "set", "idle", NULL },
+	handle_mfcr2_set_idle, "Reset MFC/R2 channel forcing it to IDLE",
+	dahdi_r2_setidle_help },
+
+	{ { "mfcr2", "set", "blocked", NULL },
+	handle_mfcr2_set_blocked, "Reset MFC/R2 channel forcing it to BLOCKED",
+	dahdi_r2_setblocked_help }
+
+};
+
+#endif /* HAVE_OPENR2 */
+
 static int dahdi_destroy_channel(int fd, int argc, char **argv)
 {
 	int channel;
@@ -10074,6 +11145,9 @@
 static int setup_dahdi(int reload);
 static int dahdi_restart(void)
 {
+#ifdef HAVE_OPENR2
+	int r;
+#endif
 #if defined(HAVE_PRI)
 	int i, j;
 #endif
@@ -10088,6 +11162,18 @@
 	if (option_verbose > 3)
 		ast_verbose("Initial softhangup of all DAHDI channels complete.\n");
 
+#ifdef HAVE_OPENR2
+	for (r = 0; r < NUM_SPANS; r++) {
+		if (r2links[r].master != AST_PTHREADT_NULL) {
+			ast_log(LOG_DEBUG, "Killing MFC/R2 monitor thread %p\n", &r2links[r].master);
+			pthread_cancel(r2links[r].master);
+			pthread_join(r2links[r].master, NULL);
+			openr2_context_delete(r2links[r].protocol_context);
+		}
+	}
+	init_mfcr2_globals();
+#endif
+
 	#if defined(HAVE_PRI)
 	for (i = 0; i < NUM_SPANS; i++) {
 		if (pris[i].master && (pris[i].master != AST_PTHREADT_NULL)) {
@@ -10322,6 +11408,35 @@
 				if (tmp->slaves[x])
 					ast_cli(fd, "Slave Channel: %d\n", tmp->slaves[x]->channel);
 			}
+#ifdef HAVE_OPENR2
+			if (tmp->mfcr2) {
+				char calldir[OR2_MAX_PATH];
+				openr2_context_t *r2context = openr2_chan_get_context(tmp->r2chan);
+				openr2_variant_t r2variant = openr2_context_get_variant(r2context);
+				ast_cli(fd, "MFC/R2 MF State: %s\n", openr2_chan_get_mf_state_string(tmp->r2chan));
+				ast_cli(fd, "MFC/R2 MF Group: %s\n", openr2_chan_get_mf_group_string(tmp->r2chan));
+				ast_cli(fd, "MFC/R2 State: %s\n", openr2_chan_get_r2_state_string(tmp->r2chan));
+				ast_cli(fd, "MFC/R2 Call State: %s\n", openr2_chan_get_call_state_string(tmp->r2chan));
+				ast_cli(fd, "MFC/R2 Call Files Enabled: %s\n", openr2_chan_get_call_files_enabled(tmp->r2chan) ? "Yes" : "No");
+				ast_cli(fd, "MFC/R2 Variant: %s\n", openr2_proto_get_variant_string(r2variant));
+				ast_cli(fd, "MFC/R2 Max ANI: %d\n", openr2_context_get_max_ani(r2context));
+				ast_cli(fd, "MFC/R2 Max DNIS: %d\n", openr2_context_get_max_dnis(r2context));
+				ast_cli(fd, "MFC/R2 Get ANI First: %s\n", openr2_context_get_ani_first(r2context) ? "Yes" : "No");
+				ast_cli(fd, "MFC/R2 Immediate Accept: %s\n", openr2_context_get_immediate_accept(r2context) ? "Yes" : "No");
+				ast_cli(fd, "MFC/R2 Charge Calls: %s\n", tmp->mfcr2_charge_calls ? "Yes" : "No");
+				ast_cli(fd, "MFC/R2 Allow Collect Calls: %s\n", tmp->mfcr2_allow_collect_calls ? "Yes" : "No");
+				ast_cli(fd, "MFC/R2 Forced Release: %s\n", tmp->mfcr2_forced_release ? "Yes" : "No");
+				ast_cli(fd, "MFC/R2 MF Back Timeout: %dms\n", openr2_context_get_mf_back_timeout(r2context));
+				ast_cli(fd, "MFC/R2 R2 Metering Pulse Timeout: %dms\n", openr2_context_get_metering_pulse_timeout(r2context));
+				ast_cli(fd, "MFC/R2 Rx CAS: %s\n", openr2_chan_get_rx_cas_string(tmp->r2chan));
+				ast_cli(fd, "MFC/R2 Tx CAS : %s\n", openr2_chan_get_tx_cas_string(tmp->r2chan));
+				ast_cli(fd, "MFC/R2 MF Tx Signal: %d\n", openr2_chan_get_tx_mf_signal(tmp->r2chan));
+				ast_cli(fd, "MFC/R2 MF Rx Signal: %d\n", openr2_chan_get_rx_mf_signal(tmp->r2chan));
+				ast_cli(fd, "MFC/R2 Call Files Directory: %s\n", openr2_context_get_log_directory(r2context, calldir, sizeof(calldir)));
+
+
+			}
+#endif
 #ifdef HAVE_PRI
 			if (tmp->pri) {
 				ast_cli(fd, "PRI Flags: ");
@@ -10807,7 +11922,9 @@
 static int __unload_module(void)
 {
 	struct dahdi_pvt *p;
-
+#ifdef HAVE_OPENR2
+	int r;
+#endif
 #ifdef HAVE_PRI
 	int i, j;
 	for (i = 0; i < NUM_SPANS; i++) {
@@ -10821,6 +11938,15 @@
 	}
 	ast_unregister_application(zap_send_keypad_facility_app);
 #endif
+#ifdef HAVE_OPENR2
+	for (r = 0; r < NUM_SPANS; r++) {
+		if (r2links[r].master != AST_PTHREADT_NULL) {
+			pthread_cancel(r2links[r].master);
+			pthread_join(r2links[r].master, NULL);
+		}
+	}
+	ast_cli_unregister_multiple(dahdi_mfcr2_cli, sizeof(dahdi_mfcr2_cli) / sizeof(dahdi_mfcr2_cli[0]));
+#endif
 	ast_cli_unregister_multiple(dahdi_cli, sizeof(dahdi_cli) / sizeof(struct ast_cli_entry));
 	local_astman_unregister("DialOffHook");
 	local_astman_unregister("Hangup");
@@ -10858,6 +11984,13 @@
 		}
 	}
 #endif
+#ifdef HAVE_OPENR2
+	for (r = 0; r < NUM_SPANS; r++) {
+		if (r2links[r].protocol_context) {
+			openr2_context_delete(r2links[r].protocol_context);
+		}
+	}
+#endif
 	ast_cond_destroy(&ss_thread_complete);
 	return 0;
 }
@@ -10957,6 +12090,11 @@
 				return -1;
 			}
 		}
+#ifdef HAVE_OPENR2
+		if (reload != 1 && r2links[mfcr2_cur_context_index].protocol_context) {
+			mfcr2_cur_context_index++;
+		}	
+#endif
 	}
 
 	return 0;
@@ -11315,6 +12453,10 @@
 				} else if (!strcasecmp(v->value, "featb")) {
 					confp->chan.sig = SIG_FEATB;
 					confp->chan.radio = 0;
+#ifdef HAVE_OPENR2
+				} else if (!strcasecmp(v->value, "mfcr2")) {
+					confp->chan.sig = SIG_MFCR2;
+#endif
 #ifdef HAVE_PRI
 				} else if (!strcasecmp(v->value, "pri_net")) {
 					confp->chan.radio = 0;
@@ -11504,6 +12646,83 @@
 				confp->pri.facilityenable = ast_true(v->value);
 #endif /* PRI_GETSET_TIMERS */
 #endif /* HAVE_PRI */
+#ifdef HAVE_OPENR2
+			} else if (!strcasecmp(v->name, "mfcr2_advanced_protocol_file")) {
+				ast_copy_string(mfcr2_cur_r2proto_file, v->value, sizeof(mfcr2_cur_r2proto_file));
+				ast_log(LOG_WARNING, "MFC/R2 Protocol file '%s' will be used, you only should use this if you *REALLY  KNOW WHAT YOU ARE DOING*.\n", mfcr2_cur_r2proto_file);
+			} 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 (OR2_VAR_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_mfback_timeout")) {
+				mfcr2_cur_mfback_timeout = atoi(v->value);
+				if (!mfcr2_cur_mfback_timeout) {
+					ast_log(LOG_WARNING, "MF timeout of 0? hum, I will protect you from your ignorance. Setting default.\n");
+					mfcr2_cur_mfback_timeout = -1;
+				} else if (mfcr2_cur_mfback_timeout > 0 && mfcr2_cur_mfback_timeout < 500) {
+					ast_log(LOG_WARNING, "MF timeout less than 500ms is not recommended, you have been warned!\n");
+				}
+			} else if (!strcasecmp(v->name, "mfcr2_metering_pulse_timeout")) {
+				mfcr2_cur_metering_pulse_timeout = atoi(v->value);
+				if (mfcr2_cur_metering_pulse_timeout > 500) {
+					ast_log(LOG_WARNING, "Metering pulse timeout greater than 500ms is not recommended, you have been warned!\n");
+				}
+			} else if (!strcasecmp(v->name, "mfcr2_get_ani_first")) {
+				mfcr2_cur_get_ani_first = ast_true(v->value);
+			} else if (!strcasecmp(v->name, "mfcr2_double_answer")) {
+				mfcr2_cur_double_answer = ast_true(v->value);
+			} else if (!strcasecmp(v->name, "mfcr2_charge_calls")) {
+				mfcr2_cur_charge_calls = ast_true(v->value);
+			} else if (!strcasecmp(v->name, "mfcr2_allow_collect_calls")) {
+				mfcr2_cur_allow_collect_calls = ast_true(v->value);
+			} else if (!strcasecmp(v->name, "mfcr2_forced_release")) {
+				mfcr2_cur_forced_release= ast_true(v->value);
+			} else if (!strcasecmp(v->name, "mfcr2_immediate_accept")) {
+				mfcr2_cur_immediate_accept = ast_true(v->value);
+			} else if (!strcasecmp(v->name, "mfcr2_call_files")) {
+				mfcr2_cur_call_files = ast_true(v->value);
+			} else if (!strcasecmp(v->name, "mfcr2_max_ani")) {
+				mfcr2_cur_max_ani = atoi(v->value);
+				if (mfcr2_cur_max_ani >= AST_MAX_EXTENSION) {
+					mfcr2_cur_max_ani = AST_MAX_EXTENSION - 1;
+				}
+			} else if (!strcasecmp(v->name, "mfcr2_max_dnis")) {
+				mfcr2_cur_max_dnis = atoi(v->value);
+				if (mfcr2_cur_max_dnis >= AST_MAX_EXTENSION) {
+					mfcr2_cur_max_dnis = AST_MAX_EXTENSION - 1;
+				}
+			} 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 = NULL;
+				char *saveptr = NULL;
+				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};
@@ -11806,6 +13025,21 @@
 		}
 	}
 #endif
+#ifdef HAVE_OPENR2
+	if (reload != 1) {
+		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_verbose(VERBOSE_PREFIX_2 "Starting R2 context on span %d\n", x + 1);
+				}
+			}
+		}
+	}
+#endif
 	/* And start the monitor for the first time */
 	restart_monitor();
 	return 0;
@@ -11841,6 +13075,9 @@
 	ast_register_application(zap_send_keypad_facility_app, zap_send_keypad_facility_exec,
 		zap_send_keypad_facility_synopsis, zap_send_keypad_facility_descrip);
 #endif
+#ifdef HAVE_OPENR2
+	init_mfcr2_globals();
+#endif
 	if ((res = setup_dahdi(0))) {
 		return AST_MODULE_LOAD_DECLINE;
 	}
@@ -11859,6 +13096,9 @@
 	ast_string_field_set(&inuse, name, "GR-303InUse");
 	ast_cli_register_multiple(dahdi_pri_cli, sizeof(dahdi_pri_cli) / sizeof(struct ast_cli_entry));
 #endif	
+#ifdef HAVE_OPENR2
+	ast_cli_register_multiple(dahdi_mfcr2_cli, sizeof(dahdi_mfcr2_cli)/sizeof(dahdi_mfcr2_cli[0]));
+#endif
 	ast_cli_register_multiple(dahdi_cli, sizeof(dahdi_cli) / sizeof(struct ast_cli_entry));
 	
 	memset(round_robin, 0, sizeof(round_robin));
Index: configure.ac
===================================================================
--- configure.ac	(.../tags/1.4.22)	(revision 160381)
+++ configure.ac	(.../team/moy/mfcr2-1.4)	(revision 160381)
@@ -198,6 +198,7 @@
 AST_EXT_LIB_SETUP([POPT], [popt], [popt])
 AST_EXT_LIB_SETUP([PGSQL], [PostgreSQL], [postgres])
 AST_EXT_LIB_SETUP([PRI], [ISDN PRI], [pri])
+AST_EXT_LIB_SETUP([OPENR2], [MFCR2], [openr2])
 AST_EXT_LIB_SETUP([PWLIB], [PWlib], [pwlib])
 AST_EXT_LIB_SETUP([OPENH323], [OpenH323], [h323])
 AST_EXT_LIB_SETUP([RADIUS], [Radius Client], [radius])
@@ -1249,6 +1250,8 @@
 
 AST_EXT_LIB_CHECK([PRI_INBANDDISCONNECT], [pri], [pri_set_inbanddisconnect], [libpri.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: include/asterisk/dahdi_compat.h
===================================================================
--- include/asterisk/dahdi_compat.h	(.../tags/1.4.22)	(revision 160381)
+++ include/asterisk/dahdi_compat.h	(.../team/moy/mfcr2-1.4)	(revision 160381)
@@ -133,6 +133,7 @@
 #define DAHDI_SETLINEAR ZT_SETLINEAR
 #define DAHDI_SET_PARAMS ZT_SET_PARAMS
 #define DAHDI_SETTONEZONE ZT_SETTONEZONE
+#define DAHDI_SIG_CAS ZT_SIG_CAS
 #define DAHDI_SIG_CLEAR ZT_SIG_CLEAR
 #define DAHDI_SIG_EM ZT_SIG_EM
 #define DAHDI_SIG_EM_E1 ZT_SIG_EM_E1
Index: configs/chan_dahdi.conf.sample
===================================================================
--- configs/chan_dahdi.conf.sample	(.../tags/1.4.22)	(revision 160381)
+++ configs/chan_dahdi.conf.sample	(.../team/moy/mfcr2-1.4)	(revision 160381)
@@ -182,6 +182,7 @@
 ; sf_featdmf:     SF Feature Group D (The real thing, MF (domestic, US))
 ; sf_featb:       SF Feature Group B (MF (domestic, US))
 ; e911:           E911 (MF) style signalling
+; mfcr2:          MFC/R2 Signalling. To specify the country variant see 'mfcr2_variant'
 ;
 ; The following are used for Radio interfaces:
 ; fxs_rx:         Receive audio/COR on an FXS kewlstart interface (FXO at the
@@ -659,6 +660,140 @@
 ; group = 2
 ; channel => 1-23
 
+; ---------------- Options for use with signalling=mfcr2 --------------
+
+; MFC/R2 variant. This depends on the OpenR2 supported variants
+; A list of values can be found at libopenr2.org
+; some valid values are:
+; ar (Argentina)
+; br (Brazil)
+; mx (Mexico)
+; ph (Philippines)
+; itu (per ITU spec)
+; mfcr2_variant=mx
+
+; whether or not to get the ANI before getting DNIS.
+; some telcos require ANI first some others do not care
+; if this go wrong, change this value
+; mfcr2_get_ani_first=no
+
+; Max amount of ANI to ask for
+; mfcr2_max_ani=10
+
+; Max amount of DNIS to ask for
+; mfcr2_max_dnis=4
+
+; Caller Category to send
+; national_subscriber
+; national_priority_subscriber
+; international_subscriber
+; international_priority_subscriber
+; collect_call
+; usually national_subscriber works just fine
+; you can change this setting from the dialplan
+; by setting the variable MFCR2_CATEGORY
+; (remember to set _MFCR2_CATEGORY from originating channels)
+; MFCR2_CATEGORY will also be a variable available
+; on incoming calls set to the value received from
+; the far end
+; mfcr2_category=national_subscriber
+
+; Call logging is stored at the Asterisk
+; logging directory specified in asterisk.conf
+; plus mfcr2/<whatever you put here>
+; if you specify 'span1' here and asterisk.conf has
+; as logging directory /var/log/asterisk then the full
+; path to your MFC/R2 call logs will be /var/log/asterisk/mfcr2/span1
+; (the directory will be automatically created if not present already)
+; remember to set mfcr2_call_files=yes
+; mfcr2_logdir=span1
+
+; whether or not to drop call files into mfcr2_logdir
+; mfcr2_call_files=yes|no
+
+; MFC/R2 valid logging values are: all,error,warning,debug,notice,cas,mf,stack,nothing
+; error,warning,debug and notice are self-descriptive
+; 'cas' is for logging ABCD CAS tx and rx 
+; 'mf' is for logging of the Multi Frequency tones
+; 'stack' is for very verbose output of the channel and context call stack, only useful
+; if you are debugging a crash or want to learn how the library works. The stack logging
+; will be only enabled if the openr2 library was compiled with -DOR2_TRACE_STACKS
+; You can mix up values, like: loglevel=error,debug,mf to log just error, debug and 
+; multi frequency messages
+; 'all' is a special value to log all the activity
+; 'nothing' is a clean-up value, in case you want to not log any activity for
+; a channel or group of channels 
+; BE AWARE that the level of output logged will ALSO depend on
+; the value you have in logger.conf, if you disable output in logger.conf
+; then it does not matter you specify 'all' here, nothing will be logged
+; so logger.conf has the last word on what is going to be logged
+; mfcr2_logging=all
+
+; MFC/R2 value in milliseconds for the MF timeout. Any negative value
+; means 'default', smaller values than 500ms are not recommended
+; and can cause malfunctioning. If you experience protocol error
+; due to MF timeout try incrementing this value in 500ms steps
+; mfcr2_mfback_timeout=-1
+
+; MFC/R2 value in milliseconds for the metering pulse timeout. 
+; Metering pulses are sent by some telcos for some R2 variants
+; during a call presumably for billing purposes to indicate costs,
+; however this pulses use the same signal that is used to indicate
+; call hangup, therefore a timeout is sometimes required to distinguish
+; between a *real* hangup and a billing pulse that should not
+; last more than 500ms, If you experience call drops after some 
+; minutes of being stablished try setting a value of some ms here, 
+; values greater than 500ms are not recommended.
+; BE AWARE that choosing the proper protocol mfcr2_variant parameter
+; implicitly sets a good recommended value for this timer, use this
+; parameter only when you *really* want to override the default, otherwise
+; just comment out this value or put a -1
+; Any negative value means 'default'. 
+; mfcr2_metering_pulse_timeout=-1
+
+; Brazil uses a special calling party category for collect calls (llamadas por cobrar)
+; instead of using the operator (as in Mexico). The R2 spec in Brazil says a special GB tone
+; should be used to reject collect calls. If you want to ALLOW collect calls specify 'yes',
+; if you want to BLOCK collect calls then say 'no'. Default is to block collect calls.
+; (see also 'mfcr2_double_answer')
+; mfcr2_allow_collect_calls=no
+
+; This feature is related but independent of mfcr2_allow_collect_calls
+; Some PBX's require a double-answer process to block collect calls, if
+; you ever have problems blocking collect calls using Group B signals (mfcr2_allow_collect_calls=no)
+; then you may want to try with mfcr2_double_answer=yes, this will cause that every answer signal
+; is changed by answer->clear back->answer (sort of a flash)
+; (see also 'mfcr2_allow_collect_calls')
+; mfcr2_double_answer=no
+
+; This feature allows to skip the use of Group B/II signals and go directly
+; to the accepted state for incoming calls
+; mfcr2_immediate_accept=no
+
+; WARNING: advanced users only! I really mean it
+; this parameter is commented by default because
+; YOU DON'T NEED IT UNLESS YOU REALLY GROK MFC/R2
+; READ COMMENTS on doc/r2proto.conf in openr2 package 
+; for more info
+; mfcr2_advanced_protocol_file=/path/to/r2proto.conf
+
+; Brazil use a special signal to force the release of the line (hangup) from the
+; backward perspective. When mfcr2_forced_release=no, the normal clear back signal
+; will be sent on hangup, which is OK for all mfcr2 variants I know of, except for
+; Brazilian variant, where the central will leave the line up for several seconds (30, 60)
+; which sometimes is not what people really want. When mfcr2_forced_release=yes, a different
+; signal will be sent to hangup the call indicating that the line should be released immediately
+; mfcr2_forced_release=no
+
+; Whether or not report to the other end 'accept call with charge'
+; This setting has no effect with most telecos, usually is safe
+; leave the default (yes), but once in a while when interconnecting with
+; old PBXs this may be useful.
+; Concretely this affects the Group B signal used to accept calls
+; mfcr2_charge_calls=yes
+
+; ---------------- END of options to be used with signalling=mfcr2
+
 ;
 
 ;  Used for distinctive ring support for x100p.
Index: makeopts.in
===================================================================
--- makeopts.in	(.../tags/1.4.22)	(revision 160381)
+++ makeopts.in	(.../team/moy/mfcr2-1.4)	(revision 160381)
@@ -132,6 +132,9 @@
 PRI_INCLUDE=@PRI_INCLUDE@
 PRI_LIB=@PRI_LIB@
 
+OPENR2_INCLUDE=@OPENR2_INCLUDE@
+OPENR2_LIB=@OPENR2_LIB@
+
 PWLIB_INCLUDE=@PWLIB_INCLUDE@
 PWLIB_LIB=@PWLIB_LIB@
 

