/* * 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_wanpipe.c -- WANPIPE PRI Channel Module * */ #include #include #include #include //#define DOTRACE static const char modname[] = "mod_wanpipe"; #define STRLEN 15 static switch_memory_pool_t *module_pool = NULL; typedef enum { PFLAG_ANSWER = (1 << 0), PFLAG_HANGUP = (1 << 1), } PFLAGS; typedef enum { PPFLAG_RING = (1 << 0), } PPFLAGS; typedef enum { TFLAG_MEDIA = (1 << 0), TFLAG_INBOUND = (1 << 1), TFLAG_OUTBOUND = (1 << 2), TFLAG_INCOMING = (1 << 3), TFLAG_PARSE_INCOMING = (1 << 4), TFLAG_ACTIVATE = (1 << 5), TFLAG_DTMF = (1 << 6), TFLAG_DESTROY = (1 << 7), TFLAG_ABORT = (1 << 8), TFLAG_SWITCH = (1 << 9), TFLAG_NOSIG = (1 << 10), TFLAG_BYE = (1 << 11) } TFLAGS; #define DEFAULT_MTU 160 static struct { int debug; int panic; int mtu; int dtmf_on; int dtmf_off; int supress_dtmf_tone; int configured_spans; char *dialplan; switch_hash_t *call_hash; switch_mutex_t *hash_mutex; } globals; struct wanpipe_pri_span { int span; int dchan; unsigned int bchans; int node; int mtu; int pswitch; char *dialplan; unsigned int l1; unsigned int dp; struct sangoma_pri spri; }; struct wpsock { int fd; char *name; struct private_object *tech_pvt; }; typedef struct wpsock wpsock_t; #define MAX_SPANS 128 static struct wanpipe_pri_span *SPANS[MAX_SPANS]; struct private_object { unsigned int flags; /* FLAGS */ switch_frame_t read_frame; /* Frame for Writing */ switch_core_session_t *session; switch_codec_t read_codec; switch_codec_t write_codec; unsigned char databuf[SWITCH_RECCOMMENDED_BUFFER_SIZE]; struct sangoma_pri *spri; sangoma_api_hdr_t hdrframe; switch_caller_profile_t *caller_profile; wpsock_t *wpsock; int callno; int span; int cause; q931_call *call; teletone_dtmf_detect_state_t dtmf_detect; teletone_generation_session_t tone_session; switch_buffer_t *dtmf_buffer; unsigned int skip_read_frames; unsigned int skip_write_frames; switch_mutex_t *flag_mutex; #ifdef DOTRACE int fd; int fd2; #endif }; struct channel_map { switch_core_session_t *map[36]; }; static int wp_close(struct private_object *tech_pvt) { int ret = 0; switch_mutex_lock(globals.hash_mutex); if (tech_pvt->wpsock && tech_pvt->wpsock->tech_pvt == tech_pvt) { tech_pvt->wpsock->tech_pvt = NULL; ret = 1; } switch_mutex_unlock(globals.hash_mutex); return ret; } static int wp_open(struct private_object *tech_pvt, int span, int chan) { int fd; wpsock_t *sock; char name[25]; snprintf(name, sizeof(name), "s%dc%d", span, chan); switch_mutex_lock(globals.hash_mutex); if ((sock = switch_core_hash_find(globals.call_hash, name))) { fd = sock->fd; } else { if ((fd = sangoma_open_tdmapi_span_chan(span, chan)) > -1) { if ((sock = malloc(sizeof(*sock)))) { memset(sock, 0, sizeof(*sock)); sock->fd = fd; sock->name = strdup(name); switch_core_hash_insert(globals.call_hash, sock->name, sock); } } } if (sock) { if (sock->tech_pvt) { if (tech_pvt->session) { switch_channel_t *channel = switch_core_session_get_channel(tech_pvt->session); switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); } } sock->tech_pvt = tech_pvt; tech_pvt->wpsock = sock; } switch_mutex_unlock(globals.hash_mutex); return sock ? 1 : 0; } static int wp_restart(int span, int chan) { wpsock_t *sock; int fd; char name[25]; snprintf(name, sizeof(name), "s%dc%d", span, chan); switch_mutex_lock(globals.hash_mutex); if ((sock = switch_core_hash_find(globals.call_hash, name))) { switch_core_hash_delete(globals.call_hash, sock->name); fd = sock->fd; } else { fd = sangoma_open_tdmapi_span_chan(span, chan); } switch_mutex_unlock(globals.hash_mutex); if (fd > -1) { close(fd); return 0; } return -1; } static void set_global_dialplan(char *dialplan) { if (globals.dialplan) { free(globals.dialplan); globals.dialplan = NULL; } globals.dialplan = strdup(dialplan); } static int str2node(char *node) { if (!strcasecmp(node, "cpe")) return PRI_CPE; if (!strcasecmp(node, "network")) return PRI_NETWORK; return -1; } static int str2switch(char *swtype) { if (!strcasecmp(swtype, "ni2")) return PRI_SWITCH_NI2; if (!strcasecmp(swtype, "dms100")) return PRI_SWITCH_DMS100; if (!strcasecmp(swtype, "lucent5e")) return PRI_SWITCH_LUCENT5E; if (!strcasecmp(swtype, "att4ess")) return PRI_SWITCH_ATT4ESS; if (!strcasecmp(swtype, "euroisdn")) return PRI_SWITCH_EUROISDN_E1; if (!strcasecmp(swtype, "gr303eoc")) return PRI_SWITCH_GR303_EOC; if (!strcasecmp(swtype, "gr303tmc")) return PRI_SWITCH_GR303_TMC; return -1; } static int str2l1(char *l1) { if (!strcasecmp(l1, "alaw")) return PRI_LAYER_1_ALAW; return PRI_LAYER_1_ULAW; } static int str2dp(char *dp) { if (!strcasecmp(dp, "international")) return PRI_INTERNATIONAL_ISDN; if (!strcasecmp(dp, "national")) return PRI_NATIONAL_ISDN; if (!strcasecmp(dp, "local")) return PRI_LOCAL_ISDN; if (!strcasecmp(dp, "private")) return PRI_PRIVATE; if (!strcasecmp(dp, "unknown")) return PRI_UNKNOWN; return PRI_UNKNOWN; } static void set_global_dialplan(char *dialplan); static int str2node(char *node); static int str2switch(char *swtype); static switch_status_t wanpipe_on_init(switch_core_session_t *session); static switch_status_t wanpipe_on_hangup(switch_core_session_t *session); static switch_status_t wanpipe_on_loopback(switch_core_session_t *session); static switch_status_t wanpipe_on_transmit(switch_core_session_t *session); static switch_status_t wanpipe_outgoing_channel(switch_core_session_t *session, switch_caller_profile_t *outbound_profile, switch_core_session_t **new_session, switch_memory_pool_t *pool); static switch_status_t wanpipe_read_frame(switch_core_session_t *session, switch_frame_t **frame, int timeout, switch_io_flag_t flags, int stream_id); static switch_status_t wanpipe_write_frame(switch_core_session_t *session, switch_frame_t *frame, int timeout, switch_io_flag_t flags, int stream_id); static int on_info(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event); static int on_hangup(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event); static int on_ring(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event); static int check_flags(struct sangoma_pri *spri); static int on_restart(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event); static int on_anything(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event); static void *pri_thread_run(switch_thread_t *thread, void *obj); static switch_status_t config_wanpipe(int reload); /* State methods they get called when the state changes to the specific state returning SWITCH_STATUS_SUCCESS tells the core to execute the standard state method next so if you fully implement the state you can return SWITCH_STATUS_FALSE to skip it. */ static switch_status_t wanpipe_on_init(switch_core_session_t *session) { struct private_object *tech_pvt; switch_channel_t *channel = NULL; wanpipe_tdm_api_t tdm_api = {{0}}; int err = 0; int mtu_mru; unsigned int rate = 8000; int new_mtu = ((globals.mtu / 8) / 2); channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); tech_pvt->read_frame.data = tech_pvt->databuf; err = sangoma_tdm_set_codec(tech_pvt->wpsock->fd, &tdm_api, WP_SLINEAR); mtu_mru = sangoma_tdm_get_usr_mtu_mru(tech_pvt->wpsock->fd, &tdm_api); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "WANPIPE INIT MTU is %d\n", mtu_mru); if (mtu_mru != globals.mtu) { sangoma_tdm_set_usr_period(tech_pvt->wpsock->fd, &tdm_api, 40); err = sangoma_tdm_set_usr_period(tech_pvt->wpsock->fd, &tdm_api, new_mtu); mtu_mru = sangoma_tdm_get_usr_mtu_mru(tech_pvt->wpsock->fd, &tdm_api); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "ADJUSTED MTU AFTER SETTING IT TO %d is %d %d [%s]\n", new_mtu, mtu_mru, err, strerror(err)); if (mtu_mru != globals.mtu) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failure to adjust MTU\n"); switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); return SWITCH_STATUS_FALSE; } } if (switch_core_codec_init (&tech_pvt->read_codec, "L16", NULL, rate, 20, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s Cannot set read codec\n", switch_channel_get_name(channel)); switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); return SWITCH_STATUS_FALSE; } if (switch_core_codec_init (&tech_pvt->write_codec, "L16", NULL, rate, 20, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s Cannot set read codec\n", switch_channel_get_name(channel)); switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); return SWITCH_STATUS_FALSE; } tech_pvt->read_frame.rate = rate; tech_pvt->read_frame.codec = &tech_pvt->read_codec; switch_core_session_set_read_codec(session, &tech_pvt->read_codec); switch_core_session_set_write_codec(session, &tech_pvt->write_codec); #ifdef DOTRACE tech_pvt->fd = open("/tmp/wp-in.raw", O_WRONLY | O_TRUNC | O_CREAT); tech_pvt->fd2 = open("/tmp/wp-out.raw", O_WRONLY | O_TRUNC | O_CREAT); #endif /* Setup artificial DTMF stuff */ memset(&tech_pvt->tone_session, 0, sizeof(tech_pvt->tone_session)); teletone_init_session(&tech_pvt->tone_session, 1024, NULL, NULL); if (globals.debug) { tech_pvt->tone_session.debug = globals.debug; tech_pvt->tone_session.debug_stream = stdout; } tech_pvt->tone_session.rate = rate; tech_pvt->tone_session.duration = globals.dtmf_on * (tech_pvt->tone_session.rate / 1000); tech_pvt->tone_session.wait = globals.dtmf_off * (tech_pvt->tone_session.rate / 1000); teletone_dtmf_detect_init (&tech_pvt->dtmf_detect, rate); if (switch_test_flag(tech_pvt, TFLAG_NOSIG)) { switch_channel_answer(channel); } /* Move Channel's State Machine to RING */ switch_channel_set_state(channel, CS_RING); return SWITCH_STATUS_SUCCESS; } static switch_status_t wanpipe_on_ring(switch_core_session_t *session) { switch_channel_t *channel = NULL; struct private_object *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, "WANPIPE RING\n"); return SWITCH_STATUS_SUCCESS; } static switch_status_t wanpipe_on_hangup(switch_core_session_t *session) { struct private_object *tech_pvt; switch_channel_t *channel = NULL; struct channel_map *chanmap = NULL; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); switch_set_flag_locked(tech_pvt, TFLAG_BYE); if (!switch_test_flag(tech_pvt, TFLAG_NOSIG)) { chanmap = tech_pvt->spri->private_info; } //sangoma_socket_close(&tech_pvt->wpsock->fd); wp_close(tech_pvt); switch_core_codec_destroy(&tech_pvt->read_codec); switch_core_codec_destroy(&tech_pvt->write_codec); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "WANPIPE HANGUP\n"); if (!switch_test_flag(tech_pvt, TFLAG_NOSIG)) { pri_hangup(tech_pvt->spri->pri, tech_pvt->call, tech_pvt->cause); pri_destroycall(tech_pvt->spri->pri, tech_pvt->call); if (chanmap->map[tech_pvt->callno]) { chanmap->map[tech_pvt->callno] = NULL; } /* pri_hangup(tech_pvt->spri->pri, tech_pvt->hangup_event.hangup.call ? tech_pvt->hangup_event.hangup.call : tech_pvt->ring_event.ring.call, tech_pvt->cause); pri_destroycall(tech_pvt->spri->pri, tech_pvt->hangup_event.hangup.call ? tech_pvt->hangup_event.hangup.call : tech_pvt->ring_event.ring.call); */ } teletone_destroy_session(&tech_pvt->tone_session); switch_buffer_destroy(&tech_pvt->dtmf_buffer); return SWITCH_STATUS_SUCCESS; } static switch_status_t wanpipe_on_loopback(switch_core_session_t *session) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "WANPIPE LOOPBACK\n"); return SWITCH_STATUS_SUCCESS; } static switch_status_t wanpipe_on_transmit(switch_core_session_t *session) { struct private_object *tech_pvt; switch_channel_t *channel; 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, "WANPIPE TRANSMIT\n"); return SWITCH_STATUS_SUCCESS; } static switch_status_t wanpipe_answer_channel(switch_core_session_t *session) { struct private_object *tech_pvt; switch_channel_t *channel = NULL; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); if (switch_test_flag(tech_pvt, TFLAG_INBOUND) && !switch_test_flag(tech_pvt, TFLAG_NOSIG)) { pri_answer(tech_pvt->spri->pri, tech_pvt->call, 0, 1); } return SWITCH_STATUS_SUCCESS; } static switch_status_t wanpipe_read_frame(switch_core_session_t *session, switch_frame_t **frame, int timeout, switch_io_flag_t flags, int stream_id) { struct private_object *tech_pvt; switch_channel_t *channel = NULL; uint8_t *bp; int bytes = 0, res = 0; char digit_str[80]; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); if (switch_test_flag(tech_pvt, TFLAG_BYE) || tech_pvt->wpsock->fd < 0) { return SWITCH_STATUS_GENERR; } bp = tech_pvt->databuf; *frame = NULL; memset(tech_pvt->databuf, 0, sizeof(tech_pvt->databuf)); while (bytes < globals.mtu) { if (switch_test_flag(tech_pvt, TFLAG_BYE) || tech_pvt->wpsock->fd < 0) { return SWITCH_STATUS_GENERR; } if (sangoma_socket_waitfor(tech_pvt->wpsock->fd, 1000, POLLIN | POLLERR | POLLHUP) <= 0) { return SWITCH_STATUS_GENERR; } if ((res = sangoma_readmsg_socket(tech_pvt->wpsock->fd, &tech_pvt->hdrframe, sizeof(tech_pvt->hdrframe), bp, sizeof(tech_pvt->databuf) - bytes, 0)) < 0) { if (errno == EBUSY) { continue; } else { return SWITCH_STATUS_GENERR; } } bytes += res; bp += bytes; } if (!bytes || switch_test_flag(tech_pvt, TFLAG_BYE) || tech_pvt->wpsock->fd < 0) { return SWITCH_STATUS_GENERR; } tech_pvt->read_frame.datalen = bytes; tech_pvt->read_frame.samples = bytes / 2; res = teletone_dtmf_detect (&tech_pvt->dtmf_detect, tech_pvt->read_frame.data, tech_pvt->read_frame.samples); res = teletone_dtmf_get(&tech_pvt->dtmf_detect, digit_str, sizeof(digit_str)); if(digit_str[0]) { switch_channel_queue_dtmf(channel, digit_str); if (globals.debug) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "DTMF DETECTED: [%s]\n", digit_str); } if (globals.supress_dtmf_tone) { memset(tech_pvt->read_frame.data, 0, tech_pvt->read_frame.datalen); } } if (tech_pvt->skip_read_frames > 0) { memset(tech_pvt->read_frame.data, 0, tech_pvt->read_frame.datalen); tech_pvt->skip_read_frames--; } #ifdef DOTRACE write(tech_pvt->fd2, tech_pvt->read_frame.data, (int) tech_pvt->read_frame.datalen); #endif //printf("read %d\n", tech_pvt->read_frame.datalen); *frame = &tech_pvt->read_frame; return SWITCH_STATUS_SUCCESS; } static switch_status_t wanpipe_write_frame(switch_core_session_t *session, switch_frame_t *frame, int timeout, switch_io_flag_t flags, int stream_id) { struct private_object *tech_pvt; switch_channel_t *channel = NULL; int result = 0; int bytes = frame->datalen; uint8_t *bp = frame->data; unsigned char dtmf[1024]; int bread, bwrote = 0; switch_status_t status = SWITCH_STATUS_SUCCESS; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); while (tech_pvt->dtmf_buffer && bwrote < frame->datalen && bytes > 0 && switch_buffer_inuse(tech_pvt->dtmf_buffer) > 0) { if (switch_test_flag(tech_pvt, TFLAG_BYE) || tech_pvt->wpsock->fd < 0) { return SWITCH_STATUS_GENERR; } if ((bread = switch_buffer_read(tech_pvt->dtmf_buffer, dtmf, globals.mtu)) < globals.mtu) { while (bread < globals.mtu) { dtmf[bread++] = 0; } } if (sangoma_socket_waitfor(tech_pvt->wpsock->fd, 1000, POLLOUT | POLLERR | POLLHUP) <= 0) { return SWITCH_STATUS_GENERR; } #ifdef DOTRACE write(tech_pvt->fd, dtmf, (int) bread); #endif result = sangoma_sendmsg_socket(tech_pvt->wpsock->fd, &tech_pvt->hdrframe, sizeof(tech_pvt->hdrframe), dtmf, bread, 0); if (result < 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Bad Write %d bytes returned %d (%s)!\n", bread, result, strerror(errno)); if (errno == EBUSY) { continue; } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Write Failed!\n"); status = SWITCH_STATUS_GENERR; break; } else { bytes -= result; bwrote += result; bp += result; result = 0; } } if (switch_test_flag(tech_pvt, TFLAG_BYE) || tech_pvt->wpsock->fd < 0) { return SWITCH_STATUS_GENERR; } if (tech_pvt->skip_write_frames) { tech_pvt->skip_write_frames--; return SWITCH_STATUS_SUCCESS; } while (bytes > 0) { unsigned int towrite; #if 1 if (sangoma_socket_waitfor(tech_pvt->wpsock->fd, 1000, POLLOUT | POLLERR | POLLHUP) <= 0) { return SWITCH_STATUS_GENERR; } #endif #ifdef DOTRACE write(tech_pvt->fd, bp, (int) globals.mtu); #endif towrite = bytes >= globals.mtu ? globals.mtu : bytes; if (towrite < globals.mtu) { int diff = globals.mtu - towrite; memset(bp + towrite, 0, diff); towrite = globals.mtu; } result = sangoma_sendmsg_socket(tech_pvt->wpsock->fd, &tech_pvt->hdrframe, sizeof(tech_pvt->hdrframe), bp, towrite, 0); if (result < 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Bad Write frame len %u write %d bytes returned %d (%s)!\n", towrite, globals.mtu, result, strerror(errno)); if (errno == EBUSY) { continue; } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Write Failed!\n"); status = SWITCH_STATUS_GENERR; break; } else { bytes -= result; bp += result; result = 0; } } //printf("write %d %d\n", frame->datalen, status); return status; } static switch_status_t wanpipe_send_dtmf(switch_core_session_t *session, char *digits) { struct private_object *tech_pvt; switch_channel_t *channel = NULL; switch_status_t status = SWITCH_STATUS_SUCCESS; int wrote = 0; char *cur = NULL; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); if (!tech_pvt->dtmf_buffer) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Allocate DTMF Buffer...."); if (switch_buffer_create_dynamic(&tech_pvt->dtmf_buffer, 1024, 3192, 0) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_ERROR, "Failed to allocate DTMF Buffer!\n"); return SWITCH_STATUS_FALSE; } else { switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "SUCCESS!\n"); } } for (cur = digits; *cur; cur++) { if ((wrote = teletone_mux_tones(&tech_pvt->tone_session, &tech_pvt->tone_session.TONES[(int)*cur]))) { switch_buffer_write(tech_pvt->dtmf_buffer, tech_pvt->tone_session.buffer, wrote * 2); } } tech_pvt->skip_read_frames = 200; return status; } static switch_status_t wanpipe_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg) { return SWITCH_STATUS_FALSE; } static switch_status_t wanpipe_kill_channel(switch_core_session_t *session, int sig) { struct private_object *tech_pvt; switch_channel_t *channel = NULL; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); switch_mutex_lock(tech_pvt->flag_mutex); switch_set_flag(tech_pvt, TFLAG_BYE); switch_clear_flag(tech_pvt, TFLAG_MEDIA); switch_mutex_unlock(tech_pvt->flag_mutex); //sangoma_socket_close(&tech_pvt->wpsock->fd); //wp_close(tech_pvt); return SWITCH_STATUS_SUCCESS; } static const switch_io_routines_t wanpipe_io_routines = { /*.outgoing_channel */ wanpipe_outgoing_channel, /*.answer_channel */ wanpipe_answer_channel, /*.read_frame */ wanpipe_read_frame, /*.write_frame */ wanpipe_write_frame, /*.kill_channel */ wanpipe_kill_channel, /*.waitfor_read */ NULL, /*.waitfor_read */ NULL, /*.send_dtmf*/ wanpipe_send_dtmf, /*.receive_message*/ wanpipe_receive_message }; static const switch_state_handler_table_t wanpipe_state_handlers = { /*.on_init */ wanpipe_on_init, /*.on_ring */ wanpipe_on_ring, /*.on_execute */ NULL, /*.on_hangup */ wanpipe_on_hangup, /*.on_loopback */ wanpipe_on_loopback, /*.on_transmit */ wanpipe_on_transmit }; static const switch_endpoint_interface_t wanpipe_endpoint_interface = { /*.interface_name */ "wanpipe", /*.io_routines */ &wanpipe_io_routines, /*.state_handlers */ &wanpipe_state_handlers, /*.private */ NULL, /*.next */ NULL }; static const switch_loadable_module_interface_t wanpipe_module_interface = { /*.module_name */ modname, /*.endpoint_interface */ &wanpipe_endpoint_interface, /*.timer_interface */ NULL, /*.dialplan_interface */ NULL, /*.codec_interface */ NULL, /*.application_interface */ NULL }; static switch_status_t wanpipe_outgoing_channel(switch_core_session_t *session, switch_caller_profile_t *outbound_profile, switch_core_session_t **new_session, switch_memory_pool_t *pool) { char *bchan = NULL; char name[128] = ""; if (outbound_profile && outbound_profile->destination_number) { bchan = strchr(outbound_profile->destination_number, '='); } else { return SWITCH_STATUS_FALSE; } if (bchan) { bchan++; if (!bchan) { return SWITCH_STATUS_FALSE; } outbound_profile->destination_number++; } else if (!globals.configured_spans) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error No Spans Configured.\n"); return SWITCH_STATUS_FALSE; } if ((*new_session = switch_core_session_request(&wanpipe_endpoint_interface, pool))) { struct private_object *tech_pvt; switch_channel_t *channel; switch_core_session_add_stream(*new_session, NULL); if ((tech_pvt = (struct private_object *) switch_core_session_alloc(*new_session, sizeof(struct private_object)))) { memset(tech_pvt, 0, sizeof(*tech_pvt)); switch_mutex_init(&tech_pvt->flag_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(*new_session)); channel = switch_core_session_get_channel(*new_session); switch_core_session_set_private(*new_session, tech_pvt); tech_pvt->session = *new_session; } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Hey where is my memory pool?\n"); switch_core_session_destroy(new_session); return SWITCH_STATUS_GENERR; } if (outbound_profile) { switch_caller_profile_t *caller_profile; struct sangoma_pri *spri; int span = 0, autospan = 0, autochan = 0; char *num, *p; int channo = 0; struct channel_map *chanmap = NULL; caller_profile = switch_caller_profile_clone(*new_session, outbound_profile); if (!bchan) { num = caller_profile->destination_number; if ((p = strchr(num, '/'))) { *p++ = '\0'; if (*num != 'a') { if (num && *num > 47 && *num < 58) { span = atoi(num); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invlid Syntax\n"); switch_core_session_destroy(new_session); return SWITCH_STATUS_GENERR; } } else { span = 0; autospan = 1; } num = p; if ((p = strchr(num, '/'))) { *p++ = '\0'; if (*num == 'a') { autochan = 1; } else if (*num == 'A') { autochan = -1; } else if (num && *num > 47 && *num < 58) { channo = atoi(num); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invlid Syntax\n"); switch_core_session_destroy(new_session); return SWITCH_STATUS_GENERR; } caller_profile->destination_number = p; } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invlid Syntax\n"); switch_core_session_destroy(new_session); return SWITCH_STATUS_GENERR; } } } tech_pvt->caller_profile = caller_profile; if (bchan) { int chan, span; if (sangoma_span_chan_fromif(bchan, &span, &chan)) { if (!wp_open(tech_pvt, span, chan)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't open fd for s%dc%d! [%s]\n", span, chan, strerror(errno)); switch_core_session_destroy(new_session); return SWITCH_STATUS_GENERR; } switch_set_flag_locked(tech_pvt, TFLAG_NOSIG); snprintf(name, sizeof(name), "WanPipe/%s/nosig-%04x", bchan, rand() & 0xffff); switch_channel_set_name(channel, name); switch_channel_set_caller_profile(channel, caller_profile); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid address\n"); switch_core_session_destroy(new_session); return SWITCH_STATUS_GENERR; } } else { do { if (autospan) { span++; } if ((spri = &SPANS[span]->spri) && switch_test_flag(spri, SANGOMA_PRI_READY)) { chanmap = spri->private_info; if (channo == 0) { if (autochan > 0) { for(channo = 1; channo < SANGOMA_MAX_CHAN_PER_SPAN; channo++) { if ((SPANS[span]->bchans & (1 << channo)) && !chanmap->map[channo]) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Choosing channel %d\n", channo); break; } } } else if (autochan < 0) { for(channo = SANGOMA_MAX_CHAN_PER_SPAN; channo > 0; channo--) { if ((SPANS[span]->bchans & (1 << channo)) && !chanmap->map[channo]) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Choosing channel %d\n", channo); break; } } } if (channo <= 0 || channo == (SANGOMA_MAX_CHAN_PER_SPAN)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No Free Channels!\n"); channo = 0; } } if (channo) { break; } } } while(autospan && span < MAX_SPANS && !spri && !channo); if (!spri || channo == 0 || channo == (SANGOMA_MAX_CHAN_PER_SPAN)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No Free Channels!\n"); switch_core_session_destroy(new_session); return SWITCH_STATUS_GENERR; } if (spri && (tech_pvt->call = pri_new_call(spri->pri))) { struct pri_sr *sr; snprintf(name, sizeof(name), "WanPipe/s%dc%d/%s-%04x", spri->span, channo, caller_profile->destination_number, rand() & 0xffff); switch_channel_set_name(channel, name); switch_channel_set_caller_profile(channel, caller_profile); sr = pri_sr_new(); pri_sr_set_channel(sr, channo, 0, 0); pri_sr_set_bearer(sr, 0, SPANS[span]->l1); pri_sr_set_called(sr, caller_profile->destination_number, SPANS[span]->dp, 1); pri_sr_set_caller(sr, caller_profile->caller_id_number, caller_profile->caller_id_name, SPANS[span]->dp, PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN); pri_sr_set_redirecting(sr, caller_profile->caller_id_number, SPANS[span]->dp, PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN, PRI_REDIR_UNCONDITIONAL); if (pri_setup(spri->pri, tech_pvt->call , sr)) { switch_core_session_destroy(new_session); pri_sr_free(sr); return SWITCH_STATUS_GENERR; } if (!wp_open(tech_pvt, spri->span, channo)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't open fd!\n"); switch_core_session_destroy(new_session); pri_sr_free(sr); return SWITCH_STATUS_GENERR; } pri_sr_free(sr); chanmap->map[channo] = *new_session; tech_pvt->spri = spri; } } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Doh! no caller profile\n"); switch_core_session_destroy(new_session); return SWITCH_STATUS_GENERR; } switch_channel_set_flag(channel, CF_OUTBOUND); switch_set_flag_locked(tech_pvt, TFLAG_OUTBOUND); switch_channel_set_state(channel, CS_INIT); return SWITCH_STATUS_SUCCESS; } return SWITCH_STATUS_GENERR; } static void s_pri_error(struct pri *pri, char *s) { if (globals.debug) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, s); } } static void s_pri_message(struct pri *pri, char *s) { s_pri_error(pri, s); } SWITCH_MOD_DECLARE(switch_status_t) switch_module_load(const switch_loadable_module_interface_t **interface, char *filename) { switch_status_t status = SWITCH_STATUS_SUCCESS; memset(SPANS, 0, sizeof(SPANS)); pri_set_error(s_pri_error); pri_set_message(s_pri_message); if (switch_core_new_memory_pool(&module_pool) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n"); return SWITCH_STATUS_TERM; } memset(&globals, 0, sizeof(globals)); switch_core_hash_init(&globals.call_hash, module_pool); switch_mutex_init(&globals.hash_mutex, SWITCH_MUTEX_NESTED, module_pool); /* start the pri's */ if ((status = config_wanpipe(0)) != SWITCH_STATUS_SUCCESS) { return status; } /* connect my internal structure to the blank pointer passed to me */ *interface = &wanpipe_module_interface; /* indicate that the module should continue to be loaded */ return status; } /*event Handlers */ static int on_info(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "number is: %s\n", event->ring.callednum); if (strlen(event->ring.callednum) > 3) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "final number is: %s\n", event->ring.callednum); pri_answer(spri->pri, event->ring.call, 0, 1); } return 0; } static int on_hangup(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event) { struct channel_map *chanmap; switch_core_session_t *session; struct private_object *tech_pvt; chanmap = spri->private_info; if ((session = chanmap->map[event->hangup.channel])) { switch_channel_t *channel = NULL; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); if (!tech_pvt->call) { tech_pvt->call = event->hangup.call; } tech_pvt->cause = event->hangup.cause; switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); chanmap->map[event->hangup.channel] = NULL; } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "-- Hanging up channel s%dc%d\n", spri->span, event->hangup.channel); return 0; } static int on_answer(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event) { switch_core_session_t *session; switch_channel_t *channel; struct channel_map *chanmap; chanmap = spri->private_info; if ((session = chanmap->map[event->answer.channel])) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "-- Answer on channel s%dc%d\n", spri->span, event->answer.channel); channel = switch_core_session_get_channel(session); assert(channel != NULL); switch_channel_answer(channel); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "-- Answer on channel s%dc%d but it's not in use?\n", spri->span, event->answer.channel); } return 0; } static int on_proceed(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event) { switch_core_session_t *session; switch_channel_t *channel; struct channel_map *chanmap; chanmap = spri->private_info; if ((session = chanmap->map[event->proceeding.channel])) { switch_caller_profile_t *originator; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "-- Proceeding on channel s%dc%d\n", spri->span, event->proceeding.channel); channel = switch_core_session_get_channel(session); assert(channel != NULL); if ((originator = switch_channel_get_originator_caller_profile(channel))) { switch_core_session_message_t msg; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "-- Passing progress to Originator %s\n", originator->chan_name); msg.message_id = SWITCH_MESSAGE_INDICATE_PROGRESS; msg.from = switch_channel_get_name(channel); switch_core_session_message_send(originator->uuid, &msg); switch_channel_set_flag(channel, CF_EARLY_MEDIA); } //switch_channel_answer(channel); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "-- Proceeding on channel s%dc%d but it's not in use?\n", spri->span, event->proceeding.channel); } return 0; } #if 0 static int on_ringing(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event) { switch_core_session_t *session; switch_channel_t *channel; struct channel_map *chanmap; struct private_object *tech_pvt; chanmap = spri->private_info; if ((session = chanmap->map[event->ringing.channel])) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "-- Ringing on channel s%dc%d\n", spri->span, event->ringing.channel); channel = switch_core_session_get_channel(session); assert(channel != NULL); pri_proceeding(spri->pri, event->ringing.call, event->ringing.channel, 0); pri_acknowledge(spri->pri, event->ringing.call, event->ringing.channel, 0); tech_pvt = switch_core_session_get_private(session); if (!tech_pvt->call) { tech_pvt->call = event->ringing.call; } tech_pvt->callno = event->ring.channel; tech_pvt->span = spri->span; } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "-- Ringing on channel s%dc%d but it's not in use?\n", spri->span, event->ringing.channel); } return 0; } #endif static int on_ring(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event) { char name[128]; switch_core_session_t *session; switch_channel_t *channel; struct channel_map *chanmap; chanmap = spri->private_info; if (chanmap->map[event->ring.channel]) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "--Duplicate Ring on channel s%dc%d (ignored)\n", spri->span, event->ring.channel); return 0; } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "-- Ring on channel s%dc%d (from %s to %s)\n", spri->span, event->ring.channel, event->ring.callingnum, event->ring.callednum); pri_proceeding(spri->pri, event->ring.call, event->ring.channel, 0); pri_acknowledge(spri->pri, event->ring.call, event->ring.channel, 0); if ((session = switch_core_session_request(&wanpipe_endpoint_interface, NULL))) { struct private_object *tech_pvt; char aniiistr[4] = ""; //wanpipe_tdm_api_t tdm_api; switch_core_session_add_stream(session, NULL); if ((tech_pvt = (struct private_object *) switch_core_session_alloc(session, sizeof(struct private_object)))) { memset(tech_pvt, 0, sizeof(*tech_pvt)); switch_mutex_init(&tech_pvt->flag_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session)); channel = switch_core_session_get_channel(session); switch_core_session_set_private(session, tech_pvt); sprintf(name, "s%dc%d", spri->span, event->ring.channel); switch_channel_set_name(channel, name); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Hey where is my memory pool?\n"); switch_core_session_destroy(&session); return 0; } if (event->ring.ani2 >= 0) { snprintf(aniiistr, 5, "%.2d", event->ring.ani2); } if ((tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session), NULL, globals.dialplan, "FreeSWITCH", event->ring.callingnum, event->ring.callingani, switch_strlen_zero(aniiistr) ? NULL : aniiistr, NULL, NULL, (char *)modname, NULL, event->ring.callednum))) { switch_channel_set_caller_profile(channel, tech_pvt->caller_profile); } switch_set_flag_locked(tech_pvt, TFLAG_INBOUND); tech_pvt->spri = spri; tech_pvt->cause = -1; if (!tech_pvt->call) { tech_pvt->call = event->ring.call; } tech_pvt->callno = event->ring.channel; tech_pvt->span = spri->span; if (!wp_open(tech_pvt, spri->span, event->ring.channel)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't open fd!\n"); } //sangoma_tdm_set_hw_period(fd, &tdm_api, 480); chanmap->map[event->ring.channel] = session; switch_channel_set_state(channel, CS_INIT); switch_core_session_thread_launch(session); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot Create new Inbound Channel!\n"); } return 0; } static int check_flags(struct sangoma_pri *spri) { return 0; } static int on_restart(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event) { switch_core_session_t *session; struct channel_map *chanmap; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "-- Restarting s%dc%d\n", spri->span, event->restart.channel); if (event->restart.channel < 1) { return 0; } chanmap = spri->private_info; if ((session = chanmap->map[event->restart.channel])) { switch_channel_t *channel; channel = switch_core_session_get_channel(session); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Hanging Up channel %s\n", switch_channel_get_name(channel)); switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); } wp_restart(spri->span, event->restart.channel); return 0; } static int on_dchan_up(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event) { if (!switch_test_flag(spri, SANGOMA_PRI_READY)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Span %d D-Chan UP!\n", spri->span); switch_set_flag(spri, SANGOMA_PRI_READY); } return 0; } static int on_dchan_down(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event) { if (switch_test_flag(spri, SANGOMA_PRI_READY)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Span %d D-Chan DOWN!\n", spri->span); switch_clear_flag(spri, SANGOMA_PRI_READY); } return 0; } static int on_anything(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Caught Event span %d %u (%s)\n", spri->span, event_type, sangoma_pri_event_str(event_type)); return 0; } static void *pri_thread_run(switch_thread_t *thread, void *obj) { struct sangoma_pri *spri = obj; struct channel_map chanmap; switch_event_t *s_event; SANGOMA_MAP_PRI_EVENT((*spri), SANGOMA_PRI_EVENT_ANY, on_anything); SANGOMA_MAP_PRI_EVENT((*spri), SANGOMA_PRI_EVENT_RING, on_ring); //SANGOMA_MAP_PRI_EVENT((*spri), SANGOMA_PRI_EVENT_RINGING, on_ringing); //SANGOMA_MAP_PRI_EVENT((*spri), SANGOMA_PRI_EVENT_SETUP_ACK, on_proceed); SANGOMA_MAP_PRI_EVENT((*spri), SANGOMA_PRI_EVENT_PROCEEDING, on_proceed); SANGOMA_MAP_PRI_EVENT((*spri), SANGOMA_PRI_EVENT_ANSWER, on_answer); SANGOMA_MAP_PRI_EVENT((*spri), SANGOMA_PRI_EVENT_DCHAN_UP, on_dchan_up); SANGOMA_MAP_PRI_EVENT((*spri), SANGOMA_PRI_EVENT_DCHAN_DOWN, on_dchan_down); SANGOMA_MAP_PRI_EVENT((*spri), SANGOMA_PRI_EVENT_HANGUP_REQ, on_hangup); SANGOMA_MAP_PRI_EVENT((*spri), SANGOMA_PRI_EVENT_HANGUP, on_hangup); SANGOMA_MAP_PRI_EVENT((*spri), SANGOMA_PRI_EVENT_INFO_RECEIVED, on_info); SANGOMA_MAP_PRI_EVENT((*spri), SANGOMA_PRI_EVENT_RESTART, on_restart); spri->on_loop = check_flags; spri->private_info = &chanmap; if (switch_event_create(&s_event, SWITCH_EVENT_PUBLISH) == SWITCH_STATUS_SUCCESS) { switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "service", "_pri._tcp"); switch_event_fire(&s_event); } sangoma_run_pri(spri); free(spri); return NULL; } static void pri_thread_launch(struct sangoma_pri *spri) { switch_thread_t *thread; switch_threadattr_t *thd_attr = NULL; switch_threadattr_create(&thd_attr, module_pool); switch_threadattr_detach_set(thd_attr, 1); switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); switch_thread_create(&thread, thd_attr, pri_thread_run, spri, module_pool); } static switch_status_t config_wanpipe(int reload) { char *cf = "wanpipe.conf"; int current_span = 0; switch_xml_t cfg, xml, settings, param, span; globals.mtu = DEFAULT_MTU; globals.dtmf_on = 150; globals.dtmf_off = 50; 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 (!strcmp(var, "debug")) { globals.debug = atoi(val); } else if (!strcmp(var, "mtu")) { globals.mtu = atoi(val); } else if (!strcmp(var, "dtmf-on")) { globals.dtmf_on = atoi(val); } else if (!strcmp(var, "dtmf-off")) { globals.dtmf_off = atoi(val); } else if (!strcmp(var, "supress-dtmf-tone")) { globals.supress_dtmf_tone = switch_true(val); } } } for (span = switch_xml_child(cfg, "span"); span; span = span->next) { for (param = switch_xml_child(span, "param"); param; param = param->next) { char *var = (char *) switch_xml_attr_soft(param, "name"); char *val = (char *) switch_xml_attr_soft(param, "value"); if (!strcmp(var, "span")) { current_span = atoi(val); if (current_span <= 0 || current_span > MAX_SPANS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid SPAN!\n"); current_span = 0; continue; } if (!SPANS[current_span]) { if (!(SPANS[current_span] = switch_core_alloc(module_pool, sizeof(*SPANS[current_span])))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "MEMORY ERROR\n"); break;; } SPANS[current_span]->span = current_span; } } else { if (!current_span) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid option %s when no span defined.\n", var); continue; } if (!strcmp(var, "dchan")) { SPANS[current_span]->dchan = atoi(val); } else if (!strcmp(var, "bchan")) { char from[128]; char *to; switch_copy_string(from, val, sizeof(from)); if ((to = strchr(from, '-'))) { int fromi, toi, x = 0; *to++ = '\0'; fromi = atoi(from); toi = atoi(to); if (fromi > 0 && toi > 0 && fromi < toi && fromi < MAX_SPANS && toi < MAX_SPANS) { for(x = fromi; x <= toi; x++) { SPANS[current_span]->bchans |= (1 << x); } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid bchan range!\n"); } } else { int i = atoi(val); if (i > 0 && i < 31) { SPANS[current_span]->bchans |= (1 << i); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid bchan!\n"); } } } else if (!strcmp(var, "node")) { SPANS[current_span]->node = str2node(val); } else if (!strcmp(var, "switch")) { SPANS[current_span]->pswitch = str2switch(val); } else if (!strcmp(var, "dp")) { SPANS[current_span]->dp = str2dp(val); } else if (!strcmp(var, "l1")) { SPANS[current_span]->l1 = str2l1(val); } else if (!strcmp(var, "dialplan")) { set_global_dialplan(val); } else if (!strcmp(var, "mtu")) { int mtu = atoi(val); if (mtu >= 10 && mtu < 960) { SPANS[current_span]->mtu = mtu; } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid MTU (%s)!\n", val); } } } } } switch_xml_free(xml); if (!globals.dialplan) { set_global_dialplan("default"); } globals.configured_spans = 0; for(current_span = 1; current_span < MAX_SPANS; current_span++) { if (SPANS[current_span]) { if (!SPANS[current_span]->l1) { SPANS[current_span]->l1 = PRI_LAYER_1_ULAW; } if (sangoma_init_pri(&SPANS[current_span]->spri, current_span, SPANS[current_span]->dchan, SPANS[current_span]->pswitch, SPANS[current_span]->node, globals.debug)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot launch span %d\n", current_span); continue; } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Launch span %d\n", current_span); pri_thread_launch(&SPANS[current_span]->spri); globals.configured_spans++; } } return SWITCH_STATUS_SUCCESS; } /* For Emacs: * Local Variables: * mode:c * indent-tabs-mode:nil * tab-width:4 * c-basic-offset:4 * End: * For VIM: * vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab: */