/* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005/2006, Anthony Minessale II * * Version: MPL 1.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * * The Initial Developer of the Original Code is * Anthony Minessale II * Portions created by the Initial Developer are Copyright (C) * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Anthony Minessale II * * * mod_openzap.c -- OPENZAP Endpoint Module * */ #include #include "openzap.h" #ifndef __FUNCTION__ #define __FUNCTION__ __SWITCH_FUNC__ #endif SWITCH_MODULE_LOAD_FUNCTION(mod_openzap_load); SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_openzap_shutdown); SWITCH_MODULE_DEFINITION(mod_openzap, mod_openzap_load, mod_openzap_shutdown, NULL); switch_endpoint_interface_t *openzap_endpoint_interface; static switch_memory_pool_t *module_pool = NULL; typedef enum { ANALOG_OPTION_NONE = 0, ANALOG_OPTION_3WAY = (1 << 0), ANALOG_OPTION_CALL_SWAP = (1 << 1) } analog_option_t; struct span_config { zap_span_t *span; char dialplan[80]; char context[80]; char dial_regex[256]; char fail_dial_regex[256]; char hold_music[256]; char type[256]; analog_option_t analog_options; }; static struct span_config SPAN_CONFIG[ZAP_MAX_SPANS_INTERFACE] = {{0}}; typedef enum { TFLAG_IO = (1 << 0), TFLAG_DTMF = (1 << 1), TFLAG_CODEC = (1 << 2), TFLAG_BREAK = (1 << 3), TFLAG_HOLD = (1 << 4) } TFLAGS; static struct { int debug; char *dialplan; char *codec_string; char *codec_order[SWITCH_MAX_CODECS]; int codec_order_last; char *codec_rates_string; char *codec_rates[SWITCH_MAX_CODECS]; int codec_rates_last; unsigned int flags; int fd; int calls; char hold_music[256]; switch_mutex_t *mutex; analog_option_t analog_options; } globals; struct private_object { unsigned int flags; switch_codec_t read_codec; switch_codec_t write_codec; switch_frame_t read_frame; unsigned char databuf[SWITCH_RECOMMENDED_BUFFER_SIZE]; switch_frame_t cng_frame; unsigned char cng_databuf[SWITCH_RECOMMENDED_BUFFER_SIZE]; switch_core_session_t *session; switch_caller_profile_t *caller_profile; unsigned int codec; unsigned int codecs; unsigned short samprate; switch_mutex_t *mutex; switch_mutex_t *flag_mutex; zap_channel_t *zchan; uint32_t wr_error; }; typedef struct private_object private_t; static switch_status_t channel_on_init(switch_core_session_t *session); static switch_status_t channel_on_hangup(switch_core_session_t *session); static switch_status_t channel_on_routing(switch_core_session_t *session); static switch_status_t channel_on_exchange_media(switch_core_session_t *session); static switch_status_t channel_on_soft_execute(switch_core_session_t *session); static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event, switch_caller_profile_t *outbound_profile, switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags); static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id); static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id); static switch_status_t channel_kill_channel(switch_core_session_t *session, int sig); static switch_core_session_t *zap_channel_get_session(zap_channel_t *channel, int32_t id) { switch_core_session_t *session = NULL; if (id > ZAP_MAX_TOKENS) { return NULL; } if (!switch_strlen_zero(channel->tokens[id])) { if (!(session = switch_core_session_locate(channel->tokens[id]))) { zap_channel_clear_token(channel, channel->tokens[id]); } } return session; } static void stop_hold(switch_core_session_t *session_a, const char *uuid) { switch_core_session_t *session; switch_channel_t *channel, *channel_a; ; if (!uuid) { return; } if ((session = switch_core_session_locate(uuid))) { channel = switch_core_session_get_channel(session); if (switch_channel_test_flag(channel, CF_HOLD)) { channel_a = switch_core_session_get_channel(session_a); switch_ivr_unhold(session); switch_channel_clear_flag(channel_a, CF_SUSPEND); switch_channel_clear_flag(channel_a, CF_HOLD); } else { switch_channel_stop_broadcast(channel); switch_channel_wait_for_flag(channel, CF_BROADCAST, SWITCH_FALSE, 2000, NULL); } switch_core_session_rwunlock(session); } } static void start_hold(zap_channel_t *zchan, switch_core_session_t *session_a, const char *uuid, const char *stream) { switch_core_session_t *session; switch_channel_t *channel, *channel_a; if (!uuid) { return; } if ((session = switch_core_session_locate(uuid))) { channel = switch_core_session_get_channel(session); if (switch_strlen_zero(stream)) { if (!strcasecmp(globals.hold_music, "indicate_hold")) { stream = "indicate_hold"; } if (!strcasecmp(SPAN_CONFIG[zchan->span->span_id].hold_music, "indicate_hold")) { stream = "indicate_hold"; } } if (switch_strlen_zero(stream)) { stream = switch_channel_get_variable(channel, SWITCH_HOLD_MUSIC_VARIABLE); } if (switch_strlen_zero(stream)) { stream = SPAN_CONFIG[zchan->span->span_id].hold_music; } if (switch_strlen_zero(stream)) { stream = globals.hold_music; } if (switch_strlen_zero(stream) && !(stream = switch_channel_get_variable(channel, SWITCH_HOLD_MUSIC_VARIABLE))) { stream = globals.hold_music; } if (!switch_strlen_zero(stream)) { if (!strcasecmp(stream, "indicate_hold")) { channel_a = switch_core_session_get_channel(session_a); switch_ivr_hold_uuid(uuid, NULL, 0); switch_channel_set_flag(channel_a, CF_SUSPEND); switch_channel_set_flag(channel_a, CF_HOLD); } else { switch_ivr_broadcast(switch_core_session_get_uuid(session), stream, SMF_ECHO_ALEG | SMF_LOOP); } } switch_core_session_rwunlock(session); } } static void cycle_foreground(zap_channel_t *zchan, int flash, const char *bcast) { uint32_t i = 0; switch_core_session_t *session; switch_channel_t *channel; private_t *tech_pvt; for (i = 0; i < zchan->token_count; i++) { if ((session = zap_channel_get_session(zchan, i))) { const char *buuid; tech_pvt = switch_core_session_get_private(session); channel = switch_core_session_get_channel(session); buuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE); if (zchan->token_count == 1 && flash) { if (switch_test_flag(tech_pvt, TFLAG_HOLD)) { stop_hold(session, buuid); switch_clear_flag_locked(tech_pvt, TFLAG_HOLD); } else { start_hold(zchan, session, buuid, bcast); switch_set_flag_locked(tech_pvt, TFLAG_HOLD); } } else if (i) { start_hold(zchan, session, buuid, bcast); switch_set_flag_locked(tech_pvt, TFLAG_HOLD); } else { stop_hold(session, buuid); switch_clear_flag_locked(tech_pvt, TFLAG_HOLD); if (!switch_channel_test_flag(channel, CF_ANSWERED)) { switch_channel_mark_answered(channel); } } switch_core_session_rwunlock(session); } } } static switch_status_t tech_init(private_t *tech_pvt, switch_core_session_t *session, zap_channel_t *zchan) { char *dname = NULL; uint32_t interval = 0, srate = 8000; zap_codec_t codec; tech_pvt->zchan = zchan; tech_pvt->read_frame.data = tech_pvt->databuf; tech_pvt->read_frame.buflen = sizeof(tech_pvt->databuf); tech_pvt->cng_frame.data = tech_pvt->cng_databuf; tech_pvt->cng_frame.buflen = sizeof(tech_pvt->cng_databuf); tech_pvt->cng_frame.flags = SFF_CNG; tech_pvt->cng_frame.codec = &tech_pvt->read_codec; memset(tech_pvt->cng_frame.data, 255, tech_pvt->cng_frame.buflen); switch_mutex_init(&tech_pvt->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session)); switch_mutex_init(&tech_pvt->flag_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session)); switch_core_session_set_private(session, tech_pvt); tech_pvt->session = session; zap_channel_command(zchan, ZAP_COMMAND_GET_INTERVAL, &interval); zap_channel_command(zchan, ZAP_COMMAND_GET_CODEC, &codec); switch(codec) { case ZAP_CODEC_ULAW: { dname = "PCMU"; } break; case ZAP_CODEC_ALAW: { dname = "PCMA"; } break; case ZAP_CODEC_SLIN: { dname = "L16"; } break; default: abort(); } if (switch_core_codec_init(&tech_pvt->read_codec, dname, NULL, srate, interval, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n"); return SWITCH_STATUS_GENERR; } else { if (switch_core_codec_init(&tech_pvt->write_codec, dname, NULL, srate, interval, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n"); switch_core_codec_destroy(&tech_pvt->read_codec); return SWITCH_STATUS_GENERR; } } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set codec %s %dms\n", dname, interval); switch_core_session_set_read_codec(tech_pvt->session, &tech_pvt->read_codec); switch_core_session_set_write_codec(tech_pvt->session, &tech_pvt->write_codec); switch_set_flag_locked(tech_pvt, TFLAG_CODEC); tech_pvt->read_frame.codec = &tech_pvt->read_codec; return SWITCH_STATUS_SUCCESS; } static switch_status_t channel_on_init(switch_core_session_t *session) { switch_channel_t *channel; private_t *tech_pvt = NULL; tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); channel = switch_core_session_get_channel(session); assert(channel != NULL); switch_set_flag_locked(tech_pvt, TFLAG_IO); /* Move channel's state machine to ROUTING */ switch_channel_set_state(channel, CS_ROUTING); switch_mutex_lock(globals.mutex); globals.calls++; switch_mutex_unlock(globals.mutex); zap_channel_init(tech_pvt->zchan); return SWITCH_STATUS_SUCCESS; } static switch_status_t channel_on_routing(switch_core_session_t *session) { switch_channel_t *channel = NULL; private_t *tech_pvt = NULL; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL ROUTING\n", switch_channel_get_name(channel)); return SWITCH_STATUS_SUCCESS; } static switch_status_t channel_on_execute(switch_core_session_t *session) { switch_channel_t *channel = NULL; private_t *tech_pvt = NULL; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL EXECUTE\n", switch_channel_get_name(channel)); return SWITCH_STATUS_SUCCESS; } static switch_status_t channel_on_hangup(switch_core_session_t *session) { switch_channel_t *channel = NULL; private_t *tech_pvt = NULL; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); zap_channel_clear_token(tech_pvt->zchan, switch_core_session_get_uuid(session)); switch (tech_pvt->zchan->type) { case ZAP_CHAN_TYPE_FXO: case ZAP_CHAN_TYPE_EM: { zap_set_state_locked(tech_pvt->zchan, ZAP_CHANNEL_STATE_HANGUP); } break; case ZAP_CHAN_TYPE_FXS: { if (tech_pvt->zchan->state != ZAP_CHANNEL_STATE_BUSY && tech_pvt->zchan->state != ZAP_CHANNEL_STATE_DOWN) { if (tech_pvt->zchan->token_count) { cycle_foreground(tech_pvt->zchan, 0, NULL); } else { zap_set_state_locked(tech_pvt->zchan, ZAP_CHANNEL_STATE_HANGUP); } } } break; case ZAP_CHAN_TYPE_B: { if (tech_pvt->zchan->state != ZAP_CHANNEL_STATE_DOWN && tech_pvt->zchan->state != ZAP_CHANNEL_STATE_TERMINATING) { tech_pvt->zchan->caller_data.hangup_cause = switch_channel_get_cause_q850(channel); if (tech_pvt->zchan->caller_data.hangup_cause < 1 || tech_pvt->zchan->caller_data.hangup_cause > 127) { tech_pvt->zchan->caller_data.hangup_cause = ZAP_CAUSE_DESTINATION_OUT_OF_ORDER; } zap_set_state_locked(tech_pvt->zchan, ZAP_CHANNEL_STATE_HANGUP); } } break; default: { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled type for channel %s\n", switch_channel_get_name(channel)); } break; } switch_clear_flag_locked(tech_pvt, TFLAG_IO); if (tech_pvt->read_codec.implementation) { switch_core_codec_destroy(&tech_pvt->read_codec); } if (tech_pvt->write_codec.implementation) { switch_core_codec_destroy(&tech_pvt->write_codec); } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL HANGUP\n", switch_channel_get_name(channel)); switch_mutex_lock(globals.mutex); globals.calls--; if (globals.calls < 0) { globals.calls = 0; } switch_mutex_unlock(globals.mutex); return SWITCH_STATUS_SUCCESS; } static switch_status_t channel_kill_channel(switch_core_session_t *session, int sig) { switch_channel_t *channel = NULL; private_t *tech_pvt = NULL; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); switch (sig) { case SWITCH_SIG_KILL: switch_clear_flag_locked(tech_pvt, TFLAG_IO); break; case SWITCH_SIG_BREAK: switch_set_flag_locked(tech_pvt, TFLAG_BREAK); break; default: break; } return SWITCH_STATUS_SUCCESS; } static switch_status_t channel_on_exchange_media(switch_core_session_t *session) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CHANNEL EXCHANGE_MEDIA\n"); return SWITCH_STATUS_SUCCESS; } static switch_status_t channel_on_soft_execute(switch_core_session_t *session) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CHANNEL SOFT_EXECUTE\n"); return SWITCH_STATUS_SUCCESS; } static switch_status_t channel_send_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf) { private_t *tech_pvt = NULL; char tmp[2] = ""; tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); tmp[0] = dtmf->digit; zap_channel_command(tech_pvt->zchan, ZAP_COMMAND_SEND_DTMF, tmp); return SWITCH_STATUS_SUCCESS; } static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id) { switch_channel_t *channel = NULL; private_t *tech_pvt = NULL; zap_size_t len; zap_wait_flag_t wflags = ZAP_READ; char dtmf[128] = ""; zap_status_t status; int total_to; int chunk, do_break = 0; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); assert(tech_pvt->zchan != NULL); chunk = tech_pvt->zchan->effective_interval * 2; total_to = chunk * 2; top: if (switch_channel_test_flag(channel, CF_SUSPEND)) { do_break = 1; } if (switch_test_flag(tech_pvt, TFLAG_BREAK)) { switch_clear_flag_locked(tech_pvt, TFLAG_BREAK); do_break = 1; } if (switch_test_flag(tech_pvt, TFLAG_HOLD) || do_break) { switch_yield(tech_pvt->zchan->effective_interval * 1000); *frame = &tech_pvt->cng_frame; tech_pvt->cng_frame.datalen = tech_pvt->zchan->packet_len; tech_pvt->cng_frame.samples = tech_pvt->cng_frame.datalen; if (tech_pvt->zchan->effective_codec == ZAP_CODEC_SLIN) { tech_pvt->cng_frame.samples /= 2; } return SWITCH_STATUS_SUCCESS; } if (!switch_test_flag(tech_pvt, TFLAG_IO)) { goto fail; } wflags = ZAP_READ; status = zap_channel_wait(tech_pvt->zchan, &wflags, chunk); if (status == ZAP_FAIL) { goto fail; } if (status == ZAP_TIMEOUT) { if (!switch_test_flag(tech_pvt, TFLAG_HOLD)) { total_to -= chunk; if (total_to <= 0) { goto fail; } } goto top; } if (!(wflags & ZAP_READ)) { goto fail; } len = tech_pvt->read_frame.buflen; if (zap_channel_read(tech_pvt->zchan, tech_pvt->read_frame.data, &len) != ZAP_SUCCESS) { goto fail; } *frame = &tech_pvt->read_frame; tech_pvt->read_frame.datalen = len; tech_pvt->read_frame.samples = tech_pvt->read_frame.datalen; if (tech_pvt->zchan->effective_codec == ZAP_CODEC_SLIN) { tech_pvt->read_frame.samples /= 2; } if (zap_channel_dequeue_dtmf(tech_pvt->zchan, dtmf, sizeof(dtmf))) { switch_dtmf_t _dtmf = { 0, SWITCH_DEFAULT_DTMF_DURATION }; char *p; for (p = dtmf; p && *p; p++) { if (is_dtmf(*p)) { _dtmf.digit = *p; zap_log(ZAP_LOG_DEBUG, "queue DTMF [%c]\n", *p); switch_channel_queue_dtmf(channel, &_dtmf); } } } return SWITCH_STATUS_SUCCESS; fail: switch_clear_flag_locked(tech_pvt, TFLAG_IO); return SWITCH_STATUS_GENERR; } static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id) { switch_channel_t *channel = NULL; private_t *tech_pvt = NULL; zap_size_t len; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); assert(tech_pvt->zchan != NULL); if (switch_test_flag(tech_pvt, TFLAG_HOLD)) { return SWITCH_STATUS_SUCCESS; } if (!switch_test_flag(tech_pvt, TFLAG_IO)) { goto fail; } len = frame->datalen; if (zap_channel_write(tech_pvt->zchan, frame->data, frame->buflen, &len) != ZAP_SUCCESS) { if (++tech_pvt->wr_error > 10) { goto fail; } } else { tech_pvt->wr_error = 0; } return SWITCH_STATUS_SUCCESS; fail: switch_clear_flag_locked(tech_pvt, TFLAG_IO); return SWITCH_STATUS_GENERR; } static switch_status_t channel_receive_message_b(switch_core_session_t *session, switch_core_session_message_t *msg) { switch_channel_t *channel; private_t *tech_pvt; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = (private_t *) switch_core_session_get_private(session); assert(tech_pvt != NULL); switch (msg->message_id) { case SWITCH_MESSAGE_INDICATE_RINGING: { if (switch_channel_test_flag(channel, CF_OUTBOUND)) { zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_PROGRESS); } else { zap_set_state_locked_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_PROGRESS); } } break; case SWITCH_MESSAGE_INDICATE_PROGRESS: { if (switch_channel_test_flag(channel, CF_OUTBOUND)) { zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_PROGRESS); zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_MEDIA); } else { /* Don't skip messages in the ISDN call setup * TODO: make the isdn stack smart enough to handle that itself * until then, this is here for safety... */ if (tech_pvt->zchan->state < ZAP_CHANNEL_STATE_PROGRESS) { zap_set_state_locked_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_PROGRESS); } zap_set_state_locked_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_PROGRESS_MEDIA); } } break; case SWITCH_MESSAGE_INDICATE_ANSWER: { if (switch_channel_test_flag(channel, CF_OUTBOUND)) { zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_ANSWERED); } else { /* Don't skip messages in the ISDN call setup * TODO: make the isdn stack smart enough to handle that itself * until then, this is here for safety... */ if (tech_pvt->zchan->state < ZAP_CHANNEL_STATE_PROGRESS) { zap_set_state_locked_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_PROGRESS); } if (tech_pvt->zchan->state < ZAP_CHANNEL_STATE_PROGRESS_MEDIA) { zap_set_state_locked_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_PROGRESS_MEDIA); } zap_set_state_locked_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_UP); } } break; default: break; } return SWITCH_STATUS_SUCCESS; } static switch_status_t channel_receive_message_fxo(switch_core_session_t *session, switch_core_session_message_t *msg) { switch_channel_t *channel; private_t *tech_pvt; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = (private_t *) switch_core_session_get_private(session); assert(tech_pvt != NULL); switch (msg->message_id) { case SWITCH_MESSAGE_INDICATE_PROGRESS: case SWITCH_MESSAGE_INDICATE_ANSWER: if (switch_channel_test_flag(channel, CF_OUTBOUND)) { zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_ANSWERED); zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_PROGRESS); zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_MEDIA); } else { zap_set_state_locked(tech_pvt->zchan, ZAP_CHANNEL_STATE_UP); } break; default: break; } return SWITCH_STATUS_SUCCESS; } static switch_status_t channel_receive_message_fxs(switch_core_session_t *session, switch_core_session_message_t *msg) { switch_channel_t *channel; private_t *tech_pvt; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = (private_t *) switch_core_session_get_private(session); assert(tech_pvt != NULL); switch (msg->message_id) { case SWITCH_MESSAGE_INDICATE_PROGRESS: case SWITCH_MESSAGE_INDICATE_ANSWER: if (switch_channel_test_flag(channel, CF_OUTBOUND)) { zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_ANSWERED); zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_PROGRESS); zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_MEDIA); } else { zap_set_state_locked(tech_pvt->zchan, ZAP_CHANNEL_STATE_UP); } break; case SWITCH_MESSAGE_INDICATE_RINGING: zap_set_state_locked(tech_pvt->zchan, ZAP_CHANNEL_STATE_RING); break; default: break; } return SWITCH_STATUS_SUCCESS; } static switch_status_t channel_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg) { private_t *tech_pvt; switch_status_t status; tech_pvt = (private_t *) switch_core_session_get_private(session); assert(tech_pvt != NULL); switch (tech_pvt->zchan->type) { case ZAP_CHAN_TYPE_FXS: case ZAP_CHAN_TYPE_EM: status = channel_receive_message_fxs(session, msg); break; case ZAP_CHAN_TYPE_FXO: status = channel_receive_message_fxo(session, msg); break; case ZAP_CHAN_TYPE_B: status = channel_receive_message_b(session, msg); break; default: status = SWITCH_STATUS_FALSE; break; } return status; } switch_state_handler_table_t openzap_state_handlers = { /*.on_init */ channel_on_init, /*.on_routing */ channel_on_routing, /*.on_execute */ channel_on_execute, /*.on_hangup */ channel_on_hangup, /*.on_exchange_media */ channel_on_exchange_media, /*.on_soft_execute */ channel_on_soft_execute }; switch_io_routines_t openzap_io_routines = { /*.outgoing_channel */ channel_outgoing_channel, /*.read_frame */ channel_read_frame, /*.write_frame */ channel_write_frame, /*.kill_channel */ channel_kill_channel, /*.send_dtmf */ channel_send_dtmf, /*.receive_message*/ channel_receive_message }; /* Make sure when you have 2 sessions in the same scope that you pass the appropriate one to the routines that allocate memory or you will have 1 channel with memory allocated from another channel's pool! */ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event, switch_caller_profile_t *outbound_profile, switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags) { char *p, *dest = NULL; int span_id = 0, chan_id = 0; zap_channel_t *zchan = NULL; switch_call_cause_t cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; char name[128]; zap_status_t status; int direction = ZAP_TOP_DOWN; zap_caller_data_t caller_data = {{ 0 }}; if (!outbound_profile) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing caller profile\n"); return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; } if (switch_strlen_zero(outbound_profile->destination_number)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid dial string\n"); return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; } dest = outbound_profile->destination_number; span_id = atoi(dest); if ((p = strchr(dest, '/'))) { if (*p++) { if (*p == 'A') { direction = ZAP_BOTTOM_UP; } else if (*p == 'a') { direction = ZAP_TOP_DOWN; } else { chan_id = atoi(p); } if ((p = strchr(p, '/')) && *++p) { dest = p; } } } if (!span_id) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing span\n"); return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; } if (chan_id < 0) { direction = ZAP_BOTTOM_UP; chan_id = 0; } if (switch_test_flag(outbound_profile, SWITCH_CPF_SCREEN)) { caller_data.screen = 1; } if (switch_test_flag(outbound_profile, SWITCH_CPF_HIDE_NUMBER)) { caller_data.pres = 1; } if (!switch_strlen_zero(dest)) { zap_set_string(caller_data.ani.digits, dest); } #if 0 if (!switch_strlen_zero(outbound_profile->rdnis)) { zap_set_string(caller_data.rdnis.digits, outbound_profile->rdnis); } #endif zap_set_string(caller_data.cid_name, outbound_profile->caller_id_name); zap_set_string(caller_data.cid_num.digits, outbound_profile->caller_id_number); if (chan_id) { status = zap_channel_open(span_id, chan_id, &zchan); } else { status = zap_channel_open_any(span_id, direction, &caller_data, &zchan); } if (status != ZAP_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No channels available\n"); return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; } if ((*new_session = switch_core_session_request(openzap_endpoint_interface, pool)) != 0) { private_t *tech_pvt; switch_channel_t *channel; switch_caller_profile_t *caller_profile; switch_core_session_add_stream(*new_session, NULL); if ((tech_pvt = (private_t *) switch_core_session_alloc(*new_session, sizeof(private_t))) != 0) { channel = switch_core_session_get_channel(*new_session); tech_init(tech_pvt, *new_session, zchan); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Hey where is my memory pool?\n"); switch_core_session_destroy(new_session); cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; goto fail; } snprintf(name, sizeof(name), "OpenZAP/%u:%u/%s", zchan->span_id, zchan->chan_id, dest); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Connect outbound channel %s\n", name); switch_channel_set_name(channel, name); zchan->caller_data = caller_data; caller_profile = switch_caller_profile_clone(*new_session, outbound_profile); switch_channel_set_caller_profile(channel, caller_profile); tech_pvt->caller_profile = caller_profile; switch_channel_set_flag(channel, CF_OUTBOUND); switch_channel_set_state(channel, CS_INIT); if (zap_channel_add_token(zchan, switch_core_session_get_uuid(*new_session), zchan->token_count) != ZAP_SUCCESS) { switch_core_session_destroy(new_session); cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; goto fail; } if (zap_channel_outgoing_call(zchan) != ZAP_SUCCESS) { if (tech_pvt->read_codec.implementation) { switch_core_codec_destroy(&tech_pvt->read_codec); } if (tech_pvt->write_codec.implementation) { switch_core_codec_destroy(&tech_pvt->write_codec); } switch_core_session_destroy(new_session); cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; goto fail; } return SWITCH_CAUSE_SUCCESS; } fail: if (zchan) { zap_channel_done(zchan); } return cause; } zap_status_t zap_channel_from_event(zap_sigmsg_t *sigmsg, switch_core_session_t **sp) { switch_core_session_t *session = NULL; private_t *tech_pvt = NULL; switch_channel_t *channel = NULL; char name[128]; *sp = NULL; if (!(session = switch_core_session_request(openzap_endpoint_interface, NULL))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Initilization Error!\n"); return ZAP_FAIL; } switch_core_session_add_stream(session, NULL); tech_pvt = (private_t *) switch_core_session_alloc(session, sizeof(private_t)); assert(tech_pvt != NULL); channel = switch_core_session_get_channel(session); if (tech_init(tech_pvt, session, sigmsg->channel) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Initilization Error!\n"); switch_core_session_destroy(&session); return ZAP_FAIL; } *sigmsg->channel->caller_data.collected = '\0'; if (switch_strlen_zero(sigmsg->channel->caller_data.cid_name)) { switch_set_string(sigmsg->channel->caller_data.cid_name, sigmsg->channel->chan_name); } if (switch_strlen_zero(sigmsg->channel->caller_data.cid_num.digits)) { if (!switch_strlen_zero(sigmsg->channel->caller_data.ani.digits)) { switch_set_string(sigmsg->channel->caller_data.cid_num.digits, sigmsg->channel->caller_data.ani.digits); } else { switch_set_string(sigmsg->channel->caller_data.cid_num.digits, sigmsg->channel->chan_number); } } tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session), "OpenZAP", SPAN_CONFIG[sigmsg->channel->span_id].dialplan, sigmsg->channel->caller_data.cid_name, sigmsg->channel->caller_data.cid_num.digits, NULL, sigmsg->channel->caller_data.ani.digits, sigmsg->channel->caller_data.aniII, sigmsg->channel->caller_data.rdnis.digits, (char *) modname, SPAN_CONFIG[sigmsg->channel->span_id].context, sigmsg->channel->caller_data.dnis.digits); assert(tech_pvt->caller_profile != NULL); if (sigmsg->channel->caller_data.screen == 1 || sigmsg->channel->caller_data.screen == 3) { switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_SCREEN); } if (sigmsg->channel->caller_data.pres) { switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NAME | SWITCH_CPF_HIDE_NUMBER); } snprintf(name, sizeof(name), "OpenZAP/%u:%u/%s", sigmsg->channel->span_id, sigmsg->channel->chan_id, tech_pvt->caller_profile->destination_number); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Connect inbound channel %s\n", name); switch_channel_set_name(channel, name); switch_channel_set_caller_profile(channel, tech_pvt->caller_profile); switch_channel_set_state(channel, CS_INIT); if (switch_core_session_thread_launch(session) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error spawning thread\n"); switch_core_session_destroy(&session); return ZAP_FAIL; } if (zap_channel_add_token(sigmsg->channel, switch_core_session_get_uuid(session), 0) != ZAP_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error adding token\n"); switch_core_session_destroy(&session); return ZAP_FAIL; } *sp = session; return ZAP_SUCCESS; } static ZIO_SIGNAL_CB_FUNCTION(on_fxo_signal) { switch_core_session_t *session = NULL; switch_channel_t *channel = NULL; zap_status_t status; zap_log(ZAP_LOG_DEBUG, "got FXO sig %d:%d [%s]\n", sigmsg->channel->span_id, sigmsg->channel->chan_id, zap_signal_event2str(sigmsg->event_id)); switch(sigmsg->event_id) { case ZAP_SIGEVENT_PROGRESS_MEDIA: { if ((session = zap_channel_get_session(sigmsg->channel, 0))) { switch_core_session_signal_lock(session); channel = switch_core_session_get_channel(session); switch_channel_mark_pre_answered(channel); switch_core_session_signal_unlock(session); switch_core_session_rwunlock(session); } } break; case ZAP_SIGEVENT_STOP: { while((session = zap_channel_get_session(sigmsg->channel, 0))) { switch_core_session_signal_lock(session); zap_channel_clear_token(sigmsg->channel, 0); channel = switch_core_session_get_channel(session); switch_channel_hangup(channel, sigmsg->channel->caller_data.hangup_cause); zap_channel_clear_token(sigmsg->channel, switch_core_session_get_uuid(session)); switch_core_session_signal_unlock(session); switch_core_session_rwunlock(session); } } break; case ZAP_SIGEVENT_UP: { if ((session = zap_channel_get_session(sigmsg->channel, 0))) { channel = switch_core_session_get_channel(session); switch_channel_mark_answered(channel); switch_core_session_rwunlock(session); } } break; case ZAP_SIGEVENT_START: { status = zap_channel_from_event(sigmsg, &session); if (status != ZAP_SUCCESS) { zap_set_state_locked(sigmsg->channel, ZAP_CHANNEL_STATE_DOWN); } } break; default: { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled type for channel %d:%d\n", sigmsg->channel->span_id, sigmsg->channel->chan_id); } break; } return ZAP_SUCCESS; } static ZIO_SIGNAL_CB_FUNCTION(on_fxs_signal) { switch_core_session_t *session = NULL; switch_channel_t *channel = NULL; zap_status_t status = ZAP_SUCCESS; zap_log(ZAP_LOG_DEBUG, "got FXS sig [%s]\n", zap_signal_event2str(sigmsg->event_id)); switch(sigmsg->event_id) { case ZAP_SIGEVENT_UP: { if ((session = zap_channel_get_session(sigmsg->channel, 0))) { switch_core_session_signal_lock(session); channel = switch_core_session_get_channel(session); switch_channel_mark_answered(channel); switch_core_session_signal_unlock(session); switch_core_session_rwunlock(session); } } break; case ZAP_SIGEVENT_PROGRESS: { if ((session = zap_channel_get_session(sigmsg->channel, 0))) { channel = switch_core_session_get_channel(session); switch_channel_mark_ring_ready(channel); switch_core_session_rwunlock(session); } } break; case ZAP_SIGEVENT_START: { zap_clear_flag_locked(sigmsg->channel, ZAP_CHANNEL_HOLD); status = zap_channel_from_event(sigmsg, &session); if (status != ZAP_SUCCESS) { zap_set_state_locked(sigmsg->channel, ZAP_CHANNEL_STATE_BUSY); } } break; case ZAP_SIGEVENT_STOP: { switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING; if (sigmsg->channel->token_count) { switch_core_session_t *session_a, *session_b, *session_t = NULL; switch_channel_t *channel_a = NULL, *channel_b = NULL; int digits = !switch_strlen_zero(sigmsg->channel->caller_data.collected); const char *br_a_uuid = NULL, *br_b_uuid = NULL; private_t *tech_pvt = NULL; if ((session_a = switch_core_session_locate(sigmsg->channel->tokens[0]))) { switch_core_session_signal_lock(session_a); channel_a = switch_core_session_get_channel(session_a); br_a_uuid = switch_channel_get_variable(channel_a, SWITCH_SIGNAL_BOND_VARIABLE); tech_pvt = switch_core_session_get_private(session_a); stop_hold(session_a, switch_channel_get_variable(channel_a, SWITCH_SIGNAL_BOND_VARIABLE)); switch_clear_flag_locked(tech_pvt, TFLAG_HOLD); } if ((session_b = switch_core_session_locate(sigmsg->channel->tokens[1]))) { switch_core_session_signal_lock(session_b); channel_b = switch_core_session_get_channel(session_b); br_b_uuid = switch_channel_get_variable(channel_b, SWITCH_SIGNAL_BOND_VARIABLE); tech_pvt = switch_core_session_get_private(session_b); stop_hold(session_a, switch_channel_get_variable(channel_b, SWITCH_SIGNAL_BOND_VARIABLE)); switch_clear_flag_locked(tech_pvt, TFLAG_HOLD); } if (channel_a && channel_b && !switch_channel_test_flag(channel_a, CF_OUTBOUND) && !switch_channel_test_flag(channel_b, CF_OUTBOUND)) { cause = SWITCH_CAUSE_ATTENDED_TRANSFER; if (br_a_uuid && br_b_uuid) { switch_ivr_uuid_bridge(br_a_uuid, br_b_uuid); } else if (br_a_uuid && digits) { session_t = switch_core_session_locate(br_a_uuid); switch_core_session_signal_lock(session_t); } else if (br_b_uuid && digits) { session_t = switch_core_session_locate(br_b_uuid); switch_core_session_signal_lock(session_t); } } if (session_t) { switch_ivr_session_transfer(session_t, sigmsg->channel->caller_data.collected, NULL, NULL); switch_core_session_signal_unlock(session_t); switch_core_session_rwunlock(session_t); } if (session_a) { switch_core_session_signal_unlock(session_a); switch_core_session_rwunlock(session_a); } if (session_b) { switch_core_session_signal_unlock(session_b); switch_core_session_rwunlock(session_b); } } while((session = zap_channel_get_session(sigmsg->channel, 0))) { switch_core_session_signal_lock(session); channel = switch_core_session_get_channel(session); switch_channel_hangup(channel, cause); zap_channel_clear_token(sigmsg->channel, switch_core_session_get_uuid(session)); switch_core_session_signal_unlock(session); switch_core_session_rwunlock(session); } zap_channel_clear_token(sigmsg->channel, NULL); } break; case ZAP_SIGEVENT_ADD_CALL: { cycle_foreground(sigmsg->channel, 1, NULL); } break; case ZAP_SIGEVENT_FLASH: { if (zap_test_flag(sigmsg->channel, ZAP_CHANNEL_HOLD) && sigmsg->channel->token_count == 1) { switch_core_session_t *session; if ((session = zap_channel_get_session(sigmsg->channel, 0))) { const char *buuid; switch_channel_t *channel; private_t *tech_pvt; tech_pvt = switch_core_session_get_private(session); channel = switch_core_session_get_channel(session); buuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE); zap_set_state_locked(sigmsg->channel, ZAP_CHANNEL_STATE_UP); stop_hold(session, buuid); switch_clear_flag_locked(tech_pvt, TFLAG_HOLD); switch_core_session_rwunlock(session); } } else if (sigmsg->channel->token_count == 2 && (SPAN_CONFIG[sigmsg->span_id].analog_options & ANALOG_OPTION_3WAY)) { if (zap_test_flag(sigmsg->channel, ZAP_CHANNEL_3WAY)) { zap_clear_flag(sigmsg->channel, ZAP_CHANNEL_3WAY); if ((session = zap_channel_get_session(sigmsg->channel, 1))) { switch_core_session_signal_lock(session); channel = switch_core_session_get_channel(session); switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); zap_channel_clear_token(sigmsg->channel, switch_core_session_get_uuid(session)); switch_core_session_signal_unlock(session); switch_core_session_rwunlock(session); } cycle_foreground(sigmsg->channel, 1, NULL); } else { char *cmd; cmd = switch_mprintf("three_way::%s", sigmsg->channel->tokens[0]); zap_set_flag(sigmsg->channel, ZAP_CHANNEL_3WAY); cycle_foreground(sigmsg->channel, 1, cmd); free(cmd); } } else if ((SPAN_CONFIG[sigmsg->span_id].analog_options & ANALOG_OPTION_CALL_SWAP) || (SPAN_CONFIG[sigmsg->span_id].analog_options & ANALOG_OPTION_3WAY) ) { cycle_foreground(sigmsg->channel, 1, NULL); if (sigmsg->channel->token_count == 1) { zap_set_flag_locked(sigmsg->channel, ZAP_CHANNEL_HOLD); zap_set_state_locked(sigmsg->channel, ZAP_CHANNEL_STATE_DIALTONE); } } } break; case ZAP_SIGEVENT_COLLECTED_DIGIT: { char *dtmf = sigmsg->raw_data; char *regex = SPAN_CONFIG[sigmsg->channel->span->span_id].dial_regex; char *fail_regex = SPAN_CONFIG[sigmsg->channel->span->span_id].fail_dial_regex; if (switch_strlen_zero(regex)) { regex = NULL; } if (switch_strlen_zero(fail_regex)) { fail_regex = NULL; } zap_log(ZAP_LOG_DEBUG, "got DTMF sig [%s]\n", dtmf); switch_set_string(sigmsg->channel->caller_data.collected, dtmf); if ((regex || fail_regex) && !switch_strlen_zero(dtmf)) { switch_regex_t *re = NULL; int ovector[30]; int match = 0; if (fail_regex) { match = switch_regex_perform(dtmf, fail_regex, &re, ovector, sizeof(ovector) / sizeof(ovector[0])); status = match ? ZAP_SUCCESS : ZAP_BREAK; switch_regex_safe_free(re); } if (status == ZAP_SUCCESS && regex) { match = switch_regex_perform(dtmf, regex, &re, ovector, sizeof(ovector) / sizeof(ovector[0])); status = match ? ZAP_BREAK : ZAP_SUCCESS; } switch_regex_safe_free(re); } } break; default: { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled type for channel %d:%d\n", sigmsg->channel->span_id, sigmsg->channel->chan_id); } break; } return status; } static ZIO_SIGNAL_CB_FUNCTION(on_clear_channel_signal) { switch_core_session_t *session = NULL; switch_channel_t *channel = NULL; zap_log(ZAP_LOG_DEBUG, "got clear channel sig [%s]\n", zap_signal_event2str(sigmsg->event_id)); switch(sigmsg->event_id) { case ZAP_SIGEVENT_START: { zap_tone_type_t tt = ZAP_TONE_DTMF; if (zap_channel_command(sigmsg->channel, ZAP_COMMAND_ENABLE_DTMF_DETECT, &tt) != ZAP_SUCCESS) { zap_log(ZAP_LOG_ERROR, "TONE ERROR\n"); } return zap_channel_from_event(sigmsg, &session); } break; case ZAP_SIGEVENT_STOP: case ZAP_SIGEVENT_RESTART: { while((session = zap_channel_get_session(sigmsg->channel, 0))) { //switch_core_session_signal_lock(session); channel = switch_core_session_get_channel(session); switch_channel_hangup(channel, sigmsg->channel->caller_data.hangup_cause); zap_channel_clear_token(sigmsg->channel, switch_core_session_get_uuid(session)); //switch_core_session_signal_unlock(session); switch_core_session_rwunlock(session); } } break; case ZAP_SIGEVENT_UP: { if ((session = zap_channel_get_session(sigmsg->channel, 0))) { zap_tone_type_t tt = ZAP_TONE_DTMF; //switch_core_session_signal_lock(session); channel = switch_core_session_get_channel(session); switch_channel_mark_answered(channel); if (zap_channel_command(sigmsg->channel, ZAP_COMMAND_ENABLE_DTMF_DETECT, &tt) != ZAP_SUCCESS) { zap_log(ZAP_LOG_ERROR, "TONE ERROR\n"); } //switch_core_session_signal_unlock(session); switch_core_session_rwunlock(session); } } case ZAP_SIGEVENT_PROGRESS_MEDIA: { if ((session = zap_channel_get_session(sigmsg->channel, 0))) { //switch_core_session_signal_lock(session); channel = switch_core_session_get_channel(session); switch_channel_mark_pre_answered(channel); //switch_core_session_signal_unlock(session); switch_core_session_rwunlock(session); } } break; case ZAP_SIGEVENT_PROGRESS: { if ((session = zap_channel_get_session(sigmsg->channel, 0))) { //switch_core_session_signal_lock(session); channel = switch_core_session_get_channel(session); switch_channel_mark_ring_ready(channel); //switch_core_session_signal_unlock(session); switch_core_session_rwunlock(session); } } break; default: { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled type for channel %d:%d\n", sigmsg->channel->span_id, sigmsg->channel->chan_id); } break; } return ZAP_SUCCESS; } static ZIO_SIGNAL_CB_FUNCTION(on_analog_signal) { switch_status_t status = SWITCH_STATUS_FALSE; switch (sigmsg->channel->type) { case ZAP_CHAN_TYPE_FXO: case ZAP_CHAN_TYPE_EM: { status = on_fxo_signal(sigmsg); } break; case ZAP_CHAN_TYPE_FXS: { status = on_fxs_signal(sigmsg); } break; default: { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled type for channel %d:%d\n", sigmsg->channel->span_id, sigmsg->channel->chan_id); } break; } return status; } static void zap_logger(const char *file, const char *func, int line, int level, const char *fmt, ...) { char *data = NULL; va_list ap; va_start(ap, fmt); if (switch_vasprintf(&data, fmt, ap) != -1) { switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, (char *)func, line, NULL, level, data); free(data); } va_end(ap); } static uint32_t enable_analog_option(const char *str, uint32_t current_options) { if (!strcasecmp(str, "3-way")) { current_options |= ANALOG_OPTION_3WAY; current_options &= ~ANALOG_OPTION_CALL_SWAP; } else if (!strcasecmp(str, "call-swap")) { current_options |= ANALOG_OPTION_CALL_SWAP; current_options &= ~ANALOG_OPTION_3WAY; } return current_options; } static switch_status_t load_config(void) { char *cf = "openzap.conf"; switch_xml_t cfg, xml, settings, param, spans, myspan; memset(&globals, 0, sizeof(globals)); switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, module_pool); if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", cf); return SWITCH_STATUS_TERM; } if ((settings = switch_xml_child(cfg, "settings"))) { for (param = switch_xml_child(settings, "param"); param; param = param->next) { char *var = (char *) switch_xml_attr_soft(param, "name"); char *val = (char *) switch_xml_attr_soft(param, "value"); if (!strcasecmp(var, "debug")) { globals.debug = atoi(val); } else if (!strcasecmp(var, "hold-music")) { switch_set_string(globals.hold_music, val); } else if (!strcasecmp(var, "enable-analog-option")) { globals.analog_options = enable_analog_option(val, globals.analog_options); } } } if ((spans = switch_xml_child(cfg, "analog_spans"))) { for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { char *id = (char *) switch_xml_attr_soft(myspan, "id"); char *name = (char *) switch_xml_attr(myspan, "name"); char *context = "default"; char *dialplan = "XML"; char *tonegroup = NULL; char *digit_timeout = NULL; char *max_digits = NULL; char *dial_regex = NULL; char *hold_music = NULL; char *fail_dial_regex = NULL; uint32_t span_id = 0, to = 0, max = 0; zap_span_t *span = NULL; analog_option_t analog_options = ANALOG_OPTION_NONE; zap_status_t zstatus = ZAP_FAIL; for (param = switch_xml_child(myspan, "param"); param; param = param->next) { char *var = (char *) switch_xml_attr_soft(param, "name"); char *val = (char *) switch_xml_attr_soft(param, "value"); if (!strcasecmp(var, "tonegroup")) { tonegroup = val; } else if (!strcasecmp(var, "digit_timeout") || !strcasecmp(var, "digit-timeout")) { digit_timeout = val; } else if (!strcasecmp(var, "context")) { context = val; } else if (!strcasecmp(var, "dialplan")) { dialplan = val; } else if (!strcasecmp(var, "dial-regex")) { dial_regex = val; } else if (!strcasecmp(var, "fail-dial-regex")) { fail_dial_regex = val; } else if (!strcasecmp(var, "hold-music")) { hold_music = val; } else if (!strcasecmp(var, "max_digits") || !strcasecmp(var, "max-digits")) { max_digits = val; } else if (!strcasecmp(var, "enable-analog-option")) { analog_options = enable_analog_option(val, analog_options); } } if (!id) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n"); continue; } if (!tonegroup) { tonegroup = "us"; } if (digit_timeout) { to = atoi(digit_timeout); } if (max_digits) { max = atoi(max_digits); } if (name) { zstatus = zap_span_find_by_name(name, &span); } else { if (switch_is_number(id)) { span_id = atoi(id); zstatus = zap_span_find(span_id, &span); } if (zstatus != ZAP_SUCCESS) { zstatus = zap_span_find_by_name(id, &span); } } if (zstatus != ZAP_SUCCESS) { zap_log(ZAP_LOG_ERROR, "Error finding OpenZAP span %d\n", span_id); continue; } if (!span_id) { span_id = span->span_id; } if (zap_configure_span("analog", span, on_analog_signal, "tonemap", tonegroup, "digit_timeout", &to, "max_dialstr", &max, TAG_END) != ZAP_SUCCESS) { zap_log(ZAP_LOG_ERROR, "Error starting OpenZAP span %d\n", span_id); continue; } SPAN_CONFIG[span->span_id].span = span; switch_set_string(SPAN_CONFIG[span->span_id].context, context); switch_set_string(SPAN_CONFIG[span->span_id].dialplan, dialplan); SPAN_CONFIG[span->span_id].analog_options = analog_options | globals.analog_options; if (dial_regex) { switch_set_string(SPAN_CONFIG[span->span_id].dial_regex, dial_regex); } if (fail_dial_regex) { switch_set_string(SPAN_CONFIG[span->span_id].fail_dial_regex, fail_dial_regex); } if (hold_music) { switch_set_string(SPAN_CONFIG[span->span_id].hold_music, hold_music); } switch_copy_string(SPAN_CONFIG[span->span_id].type, "analog", sizeof(SPAN_CONFIG[span->span_id].type)); zap_span_start(span); } } if ((spans = switch_xml_child(cfg, "analog_em_spans"))) { for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { char *id = (char *) switch_xml_attr_soft(myspan, "id"); char *context = "default"; char *dialplan = "XML"; char *tonegroup = NULL; char *digit_timeout = NULL; char *max_digits = NULL; char *dial_regex = NULL; char *hold_music = NULL; char *fail_dial_regex = NULL; uint32_t span_id = 0, to = 0, max = 0; zap_span_t *span = NULL; analog_option_t analog_options = ANALOG_OPTION_NONE; for (param = switch_xml_child(myspan, "param"); param; param = param->next) { char *var = (char *) switch_xml_attr_soft(param, "name"); char *val = (char *) switch_xml_attr_soft(param, "value"); if (!strcasecmp(var, "tonegroup")) { tonegroup = val; } else if (!strcasecmp(var, "digit_timeout") || !strcasecmp(var, "digit-timeout")) { digit_timeout = val; } else if (!strcasecmp(var, "context")) { context = val; } else if (!strcasecmp(var, "dialplan")) { dialplan = val; } else if (!strcasecmp(var, "dial-regex")) { dial_regex = val; } else if (!strcasecmp(var, "fail-dial-regex")) { fail_dial_regex = val; } else if (!strcasecmp(var, "hold-music")) { hold_music = val; } else if (!strcasecmp(var, "max_digits") || !strcasecmp(var, "max-digits")) { max_digits = val; } else if (!strcasecmp(var, "enable-analog-option")) { analog_options = enable_analog_option(val, analog_options); } } if (!id) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n"); continue; } span_id = atoi(id); if (!tonegroup) { tonegroup = "us"; } if (digit_timeout) { to = atoi(digit_timeout); } if (max_digits) { max = atoi(max_digits); } if (zap_span_find(span_id, &span) != ZAP_SUCCESS) { zap_log(ZAP_LOG_ERROR, "Error finding OpenZAP span %d\n", span_id); continue; } if (zap_configure_span("analog_em", span, on_analog_signal, "tonemap", tonegroup, "digit_timeout", &to, "max_dialstr", &max, TAG_END) != ZAP_SUCCESS) { zap_log(ZAP_LOG_ERROR, "Error starting OpenZAP span %d\n", span_id); continue; } SPAN_CONFIG[span->span_id].span = span; switch_set_string(SPAN_CONFIG[span->span_id].context, context); switch_set_string(SPAN_CONFIG[span->span_id].dialplan, dialplan); SPAN_CONFIG[span->span_id].analog_options = analog_options | globals.analog_options; if (dial_regex) { switch_set_string(SPAN_CONFIG[span->span_id].dial_regex, dial_regex); } if (fail_dial_regex) { switch_set_string(SPAN_CONFIG[span->span_id].fail_dial_regex, fail_dial_regex); } if (hold_music) { switch_set_string(SPAN_CONFIG[span->span_id].hold_music, hold_music); } switch_copy_string(SPAN_CONFIG[span->span_id].type, "analog_em", sizeof(SPAN_CONFIG[span->span_id].type)); zap_span_start(span); } } if ((spans = switch_xml_child(cfg, "pri_spans"))) { for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { char *id = (char *) switch_xml_attr_soft(myspan, "id"); char *context = "default"; char *dialplan = "XML"; //Q921NetUser_t mode = Q931_TE; //Q931Dialect_t dialect = Q931_Dialect_National; char *mode = NULL; char *dialect = NULL; uint32_t span_id = 0; zap_span_t *span = NULL; char *tonegroup = NULL; uint32_t opts = 0; for (param = switch_xml_child(myspan, "param"); param; param = param->next) { char *var = (char *) switch_xml_attr_soft(param, "name"); char *val = (char *) switch_xml_attr_soft(param, "value"); if (!strcasecmp(var, "tonegroup")) { tonegroup = val; } else if (!strcasecmp(var, "mode")) { mode = val; } else if (!strcasecmp(var, "dialect")) { dialect = val; } else if (!strcasecmp(var, "context")) { context = val; } else if (!strcasecmp(var, "suggest-channel") && switch_true(val)) { opts |= 1; } else if (!strcasecmp(var, "dialplan")) { dialplan = val; } } if (!id) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n"); continue; } span_id = atoi(id); if (!tonegroup) { tonegroup = "us"; } if (zap_span_find(span_id, &span) != ZAP_SUCCESS) { zap_log(ZAP_LOG_ERROR, "Error finding OpenZAP span %d\n", span_id); continue; } if (zap_configure_span("isdn", span, on_clear_channel_signal, "mode", mode, "dialect", dialect, TAG_END) != ZAP_SUCCESS) { zap_log(ZAP_LOG_ERROR, "Error starting OpenZAP span %d mode: %d dialect: %d error: %s\n", span_id, mode, dialect, span->last_error); continue; } SPAN_CONFIG[span->span_id].span = span; switch_copy_string(SPAN_CONFIG[span->span_id].context, context, sizeof(SPAN_CONFIG[span->span_id].context)); switch_copy_string(SPAN_CONFIG[span->span_id].dialplan, dialplan, sizeof(SPAN_CONFIG[span->span_id].dialplan)); switch_copy_string(SPAN_CONFIG[span->span_id].type, "isdn", sizeof(SPAN_CONFIG[span->span_id].type)); zap_span_start(span); } } if ((spans = switch_xml_child(cfg, "boost_spans"))) { for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { char *id = (char *) switch_xml_attr_soft(myspan, "id"); char *context = "default"; char *dialplan = "XML"; uint32_t span_id = 0; zap_span_t *span = NULL; char *tonegroup = NULL; char *local_ip = NULL; int local_port = 0; char *remote_ip = NULL; int remote_port = 0; for (param = switch_xml_child(myspan, "param"); param; param = param->next) { char *var = (char *) switch_xml_attr_soft(param, "name"); char *val = (char *) switch_xml_attr_soft(param, "value"); if (!strcasecmp(var, "tonegroup")) { tonegroup = val; } else if (!strcasecmp(var, "local-ip")) { local_ip = val; } else if (!strcasecmp(var, "local-port")) { local_port = atoi(val); } else if (!strcasecmp(var, "remote-ip")) { remote_ip = val; } else if (!strcasecmp(var, "remote-port")) { remote_port = atoi(val); } else if (!strcasecmp(var, "context")) { context = val; } else if (!strcasecmp(var, "dialplan")) { dialplan = val; } } if (!id) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param\n"); continue; } span_id = atoi(id); if (!tonegroup) { tonegroup = "us"; } if (zap_span_find(span_id, &span) != ZAP_SUCCESS) { zap_log(ZAP_LOG_ERROR, "Error finding OpenZAP span %d\n", span_id); continue; } if (zap_configure_span("ss7_boost", span, on_clear_channel_signal, "local_ip", local_ip, "local_port", &local_port, "remote_ip", remote_ip, "remote_port", &remote_port, TAG_END) != ZAP_SUCCESS) { zap_log(ZAP_LOG_ERROR, "Error starting OpenZAP span %d error: %s\n", span_id, span->last_error); continue; } SPAN_CONFIG[span->span_id].span = span; switch_copy_string(SPAN_CONFIG[span->span_id].context, context, sizeof(SPAN_CONFIG[span->span_id].context)); switch_copy_string(SPAN_CONFIG[span->span_id].dialplan, dialplan, sizeof(SPAN_CONFIG[span->span_id].dialplan)); zap_span_start(span); switch_copy_string(SPAN_CONFIG[span->span_id].type, "ss7 (boost)", sizeof(SPAN_CONFIG[span->span_id].type)); } } switch_xml_free(xml); return SWITCH_STATUS_SUCCESS; } void dump_chan(zap_span_t *span, uint32_t chan_id, switch_stream_handle_t *stream) { stream->write_function(stream, "span_id: %u\n" "chan_id: %u\n" "physical_span_id: %u\n" "physical_chan_id: %u\n" "type: %s\n" "state: %s\n" "last_state: %s\n" "cid_date: %s\n" "cid_name: %s\n" "cid_num: %s\n" "ani: %s\n" "aniII: %s\n" "dnis: %s\n" "rdnis: %s\n" "cause: %s\n\n", span->channels[chan_id]->span_id, span->channels[chan_id]->chan_id, span->channels[chan_id]->physical_span_id, span->channels[chan_id]->physical_chan_id, zap_chan_type2str(span->channels[chan_id]->type), zap_channel_state2str(span->channels[chan_id]->state), zap_channel_state2str(span->channels[chan_id]->last_state), span->channels[chan_id]->caller_data.cid_date, span->channels[chan_id]->caller_data.cid_name, span->channels[chan_id]->caller_data.cid_num.digits, span->channels[chan_id]->caller_data.ani.digits, span->channels[chan_id]->caller_data.aniII, span->channels[chan_id]->caller_data.dnis.digits, span->channels[chan_id]->caller_data.rdnis.digits, switch_channel_cause2str(span->channels[chan_id]->caller_data.hangup_cause) ); } #define OZ_SYNTAX "list || dump []" SWITCH_STANDARD_API(oz_function) { char *mycmd = NULL, *argv[10] = { 0 }; int argc = 0; if (!switch_strlen_zero(cmd) && (mycmd = strdup(cmd))) { argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); } if (!argc) { stream->write_function(stream, "%s", OZ_SYNTAX); goto end; } if (!strcasecmp(argv[0], "dump")) { if (argc < 2) { stream->write_function(stream, "-ERR Usage: oz dump []\n"); goto end; } else { int32_t span_id, chan_id = 0; zap_span_t *span; span_id = atoi(argv[1]); if (argc > 2) { chan_id = atoi(argv[2]); } if (!(span_id && (span = SPAN_CONFIG[span_id].span))) { stream->write_function(stream, "-ERR invalid span\n"); } else { if (chan_id) { dump_chan(span, chan_id, stream); } else { int j; stream->write_function(stream, "+OK\n"); for(j = 1; j <= span->chan_count; j++) { dump_chan(span, j, stream); } } } } } else if (!strcasecmp(argv[0], "list")) { int j; for (j = 0 ; j < ZAP_MAX_SPANS_INTERFACE; j++) { if (SPAN_CONFIG[j].span) { char *flags = "none"; if (SPAN_CONFIG[j].analog_options & ANALOG_OPTION_3WAY) { flags = "3way"; } else if (SPAN_CONFIG[j].analog_options & ANALOG_OPTION_CALL_SWAP) { flags = "call swap"; } stream->write_function(stream, "+OK\n" "span: %u\n" "type: %s\n" "chan_count: %u\n" "dialplan: %s\n" "context: %s\n" "dial_regex: %s\n" "fail_dial_regex: %s\n" "hold_music: %s\n" "analog_options %s\n", j, SPAN_CONFIG[j].type, SPAN_CONFIG[j].span->chan_count, SPAN_CONFIG[j].dialplan, SPAN_CONFIG[j].context, SPAN_CONFIG[j].dial_regex, SPAN_CONFIG[j].fail_dial_regex, SPAN_CONFIG[j].hold_music, flags ); } } } else { stream->write_function(stream, "-ERR Usage: oz list || dump []\n"); } end: switch_safe_free(mycmd); return SWITCH_STATUS_SUCCESS; } SWITCH_STANDARD_APP(disable_ec_function) { private_t *tech_pvt; int x = 0; if (!switch_core_session_check_interface(session, openzap_endpoint_interface)) { zap_log(ZAP_LOG_ERROR, "This application is only for OpenZAP channels.\n"); return; } tech_pvt = switch_core_session_get_private(session); zap_channel_command(tech_pvt->zchan, ZAP_COMMAND_DISABLE_ECHOCANCEL, &x); zap_channel_command(tech_pvt->zchan, ZAP_COMMAND_DISABLE_ECHOTRAIN, &x); zap_log(ZAP_LOG_ERROR, "Echo Canceller Disabled\n"); } SWITCH_MODULE_LOAD_FUNCTION(mod_openzap_load) { switch_api_interface_t *commands_api_interface; switch_application_interface_t *app_interface; module_pool = pool; zap_global_set_logger(zap_logger); if (zap_global_init() != ZAP_SUCCESS) { zap_log(ZAP_LOG_ERROR, "Error loading OpenZAP\n"); return SWITCH_STATUS_TERM; } if (load_config() != SWITCH_STATUS_SUCCESS) { zap_global_destroy(); return SWITCH_STATUS_TERM; } *module_interface = switch_loadable_module_create_module_interface(pool, modname); openzap_endpoint_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE); openzap_endpoint_interface->interface_name = "openzap"; openzap_endpoint_interface->io_routines = &openzap_io_routines; openzap_endpoint_interface->state_handler = &openzap_state_handlers; SWITCH_ADD_API(commands_api_interface, "oz", "OpenZAP commands", oz_function, OZ_SYNTAX); SWITCH_ADD_APP(app_interface, "disable_ec", "Disable Echo Canceller", "Disable Echo Canceller", disable_ec_function, "", SAF_NONE); /* indicate that the module should continue to be loaded */ return SWITCH_STATUS_SUCCESS; } SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_openzap_shutdown) { zap_global_destroy(); return SWITCH_STATUS_NOUNLOAD; } /* For Emacs: * Local Variables: * mode:c * indent-tabs-mode:t * tab-width:4 * c-basic-offset:4 * End: * For VIM: * vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab: */