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