/* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005-2011, 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): * * Mathieu Rene * * tdm.c -- FreeTDM Controllable Channel Module * */ #include #include "freetdm.h" void ctdm_init(switch_loadable_module_interface_t *module_interface); /* Parameters */ #define kSPAN_ID "span" #define kCHAN_ID "chan" #define kSPAN_NAME "span_name" #define kPREBUFFER_LEN "prebuffer_len" #define kECHOCANCEL "echo_cancel" static struct { switch_memory_pool_t *pool; switch_endpoint_interface_t *endpoint_interface; } ctdm; typedef struct { int span_id; int chan_id; ftdm_channel_t *ftdm_channel; switch_core_session_t *session; switch_codec_t read_codec, write_codec; switch_frame_t read_frame; int prebuffer_len; unsigned char databuf[SWITCH_RECOMMENDED_BUFFER_SIZE]; } ctdm_private_t; static switch_status_t channel_on_init(switch_core_session_t *session); static switch_status_t channel_on_destroy(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, switch_call_cause_t *cancel_cause); 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_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg); static switch_status_t channel_receive_event(switch_core_session_t *session, switch_event_t *event); static switch_status_t channel_send_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf); static ftdm_status_t ctdm_span_prepare(ftdm_span_t *span); switch_state_handler_table_t ctdm_state_handlers = { .on_init = channel_on_init, .on_destroy = channel_on_destroy }; switch_io_routines_t ctdm_io_routines = { .send_dtmf = channel_send_dtmf, .outgoing_channel = channel_outgoing_channel, .read_frame = channel_read_frame, .write_frame = channel_write_frame, .receive_message = channel_receive_message, .receive_event = channel_receive_event }; static void ctdm_report_alarms(ftdm_channel_t *channel) { switch_event_t *event = NULL; ftdm_alarm_flag_t alarmflag = 0; if (switch_event_create(&event, SWITCH_EVENT_TRAP) != SWITCH_STATUS_SUCCESS) { ftdm_log(FTDM_LOG_ERROR, "failed to create alarms events\n"); return; } if (ftdm_channel_get_alarms(channel, &alarmflag) != FTDM_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to retrieve alarms %s:%d\n", ftdm_channel_get_span_name(channel), ftdm_channel_get_id(channel)); return; } switch_event_add_header(event, SWITCH_STACK_BOTTOM, "span-name", "%s", ftdm_channel_get_span_name(channel)); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "span-number", "%d", ftdm_channel_get_span_id(channel)); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "chan-number", "%d", ftdm_channel_get_id(channel)); if (alarmflag) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "condition", "ftdm-alarm-trap"); } else { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "condition", "ftdm-alarm-clear"); } if (alarmflag & FTDM_ALARM_RED) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "red"); } if (alarmflag & FTDM_ALARM_YELLOW) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "yellow"); } if (alarmflag & FTDM_ALARM_RAI) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "rai"); } if (alarmflag & FTDM_ALARM_BLUE) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "blue"); } if (alarmflag & FTDM_ALARM_AIS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "ais"); } if (alarmflag & FTDM_ALARM_GENERAL) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "general"); } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Reporting [%s] alarms for %s:%d\n", (alarmflag?"ftdm-alarm-trap":"ftdm-alarm-clear"), ftdm_channel_get_span_name(channel), ftdm_channel_get_id(channel)); switch_event_fire(&event); return; } static ftdm_channel_t *ctdm_get_channel_from_event(switch_event_t *event, ftdm_span_t *span) { uint32_t chan_id = 0; const char *chan_number = NULL; chan_number = switch_event_get_header(event, "chan-number"); if (zstr(chan_number)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No channel number specified\n"); return NULL; } chan_id = atoi(chan_number); if (!chan_id) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid channel number:%s\n", chan_number); return NULL; } return ftdm_span_get_channel_ph(span, chan_id); } static void ctdm_event_handler(switch_event_t *event) { ftdm_status_t status = FTDM_FAIL; switch(event->event_id) { case SWITCH_EVENT_TRAP: { ftdm_span_t *span = NULL; ftdm_channel_t *channel = NULL; const char *span_name = NULL; const char *cond = switch_event_get_header(event, "condition"); const char *command = switch_event_get_header(event, "command"); if (zstr(cond)) { return; } span_name = switch_event_get_header(event, "span-name"); if (ftdm_span_find_by_name(span_name, &span) != FTDM_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot find span [%s]\n", span_name); return; } if (!strcmp(cond, "mg-tdm-prepare")) { status = ctdm_span_prepare(span); if (status == FTDM_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s:prepared successfully\n", span_name); } else if (status != FTDM_EINVAL) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s:Failed to prepare span\n", span_name); } } else if (!strcmp(cond, "mg-tdm-check")) { channel = ctdm_get_channel_from_event(event, span); if (!channel) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not find channel\n"); return; } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Requesting alarm status for %s:%d\n", ftdm_channel_get_span_name(channel), ftdm_channel_get_id(channel)); ctdm_report_alarms(channel); } else if (!strcmp(cond, "mg-tdm-dtmfremoval")) { uint8_t enable = 0; channel = ctdm_get_channel_from_event(event, span); if (!channel) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not find channel\n"); return; } if (zstr(command)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s:No command specified for mg-tdm-dtmfremoval\n", span_name); return; } if (!strcmp(command, "enable")) { enable = 1; } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s DTMF-removal for %s:%d\n", enable ? "Enabling" : "Disabling", ftdm_channel_get_span_name(channel), ftdm_channel_get_id(channel)); ftdm_channel_command(channel, enable ? FTDM_COMMAND_ENABLE_DTMF_REMOVAL : FTDM_COMMAND_DISABLE_DTMF_REMOVAL, 0); } } break; default: break; } return; } void ctdm_init(switch_loadable_module_interface_t *module_interface) { switch_endpoint_interface_t *endpoint_interface; ctdm.pool = module_interface->pool; endpoint_interface = switch_loadable_module_create_interface(module_interface, SWITCH_ENDPOINT_INTERFACE); endpoint_interface->interface_name = "tdm"; endpoint_interface->io_routines = &ctdm_io_routines; endpoint_interface->state_handler = &ctdm_state_handlers; ctdm.endpoint_interface = endpoint_interface; switch_event_bind("mod_freetdm", SWITCH_EVENT_TRAP, SWITCH_EVENT_SUBCLASS_ANY, ctdm_event_handler, NULL); } static FIO_SIGNAL_CB_FUNCTION(on_signal_cb) { uint32_t chanid, spanid; switch_event_t *event = NULL; ftdm_alarm_flag_t alarmbits = FTDM_ALARM_NONE; chanid = ftdm_channel_get_id(sigmsg->channel); spanid = ftdm_channel_get_span_id(sigmsg->channel); switch(sigmsg->event_id) { case FTDM_SIGEVENT_ALARM_CLEAR: case FTDM_SIGEVENT_ALARM_TRAP: { if (ftdm_channel_get_alarms(sigmsg->channel, &alarmbits) != FTDM_SUCCESS) { ftdm_log(FTDM_LOG_ERROR, "failed to retrieve alarms\n"); return FTDM_FAIL; } if (switch_event_create(&event, SWITCH_EVENT_TRAP) != SWITCH_STATUS_SUCCESS) { ftdm_log(FTDM_LOG_ERROR, "failed to create alarms events\n"); return FTDM_FAIL; } if (sigmsg->event_id == FTDM_SIGEVENT_ALARM_CLEAR) { ftdm_log(FTDM_LOG_NOTICE, "Alarm cleared on channel %d:%d\n", spanid, chanid); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "condition", "ftdm-alarm-clear"); } else { ftdm_log(FTDM_LOG_NOTICE, "Alarm raised on channel %d:%d\n", spanid, chanid); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "condition", "ftdm-alarm-trap"); } } break; default: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unhandled event %d\n", sigmsg->event_id); break; } if (event) { switch_event_add_header(event, SWITCH_STACK_BOTTOM, "span-name", "%s", ftdm_channel_get_span_name(sigmsg->channel)); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "span-number", "%d", ftdm_channel_get_span_id(sigmsg->channel)); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "chan-number", "%d", ftdm_channel_get_id(sigmsg->channel)); if (alarmbits & FTDM_ALARM_RED) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "red"); } if (alarmbits & FTDM_ALARM_YELLOW) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "yellow"); } if (alarmbits & FTDM_ALARM_RAI) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "rai"); } if (alarmbits & FTDM_ALARM_BLUE) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "blue"); } if (alarmbits & FTDM_ALARM_AIS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "ais"); } if (alarmbits & FTDM_ALARM_GENERAL) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "general"); } switch_event_fire(&event); } return FTDM_SUCCESS; } static ftdm_status_t ctdm_span_prepare(ftdm_span_t *span) { if (ftdm_span_register_signal_cb(span, on_signal_cb) != FTDM_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register signal CB\n"); return FTDM_FAIL; } return ftdm_span_start(span); } 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, switch_call_cause_t *cancel_cause) { const char *szchanid = switch_event_get_header(var_event, kCHAN_ID), *span_name = switch_event_get_header(var_event, kSPAN_NAME), *szprebuffer_len = switch_event_get_header(var_event, kPREBUFFER_LEN); int chan_id; int span_id; switch_caller_profile_t *caller_profile; ftdm_span_t *span; ftdm_channel_t *chan; switch_channel_t *channel; char name[128]; const char *dname; ftdm_codec_t codec; uint32_t interval; ctdm_private_t *tech_pvt = NULL; if (zstr(szchanid) || zstr(span_name)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Both ["kSPAN_ID"] and ["kCHAN_ID"] have to be set.\n"); goto fail; } chan_id = atoi(szchanid); if (ftdm_span_find_by_name(span_name, &span) == FTDM_SUCCESS) { span_id = ftdm_span_get_id(span); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot find span [%s]\n", span_name); goto fail; } if (!(*new_session = switch_core_session_request(ctdm.endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, 0, pool))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't request session.\n"); goto fail; } channel = switch_core_session_get_channel(*new_session); if (ftdm_channel_open_ph(span_id, chan_id, &chan) != FTDM_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't open span or channel.\n"); goto fail; } switch_channel_set_flag(channel, CF_AUDIO); span = ftdm_channel_get_span(chan); tech_pvt = switch_core_session_alloc(*new_session, sizeof *tech_pvt); tech_pvt->chan_id = chan_id; tech_pvt->span_id = span_id; tech_pvt->ftdm_channel = chan; tech_pvt->session = *new_session; tech_pvt->read_frame.buflen = sizeof(tech_pvt->databuf); tech_pvt->read_frame.data = tech_pvt->databuf; tech_pvt->prebuffer_len = zstr(szprebuffer_len) ? 0 : atoi(szprebuffer_len); switch_core_session_set_private(*new_session, tech_pvt); caller_profile = switch_caller_profile_clone(*new_session, outbound_profile); switch_channel_set_caller_profile(channel, caller_profile); snprintf(name, sizeof(name), "tdm/%d:%d", span_id, chan_id); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Connect outbound channel %s\n", name); switch_channel_set_name(channel, name); switch_channel_set_state(channel, CS_INIT); if (FTDM_SUCCESS != ftdm_channel_command(chan, FTDM_COMMAND_GET_CODEC, &codec)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to retrieve channel codec.\n"); return SWITCH_CAUSE_NETWORK_OUT_OF_ORDER; } if (FTDM_SUCCESS != ftdm_channel_command(chan, FTDM_COMMAND_GET_INTERVAL, &interval)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to retrieve channel interval.\n"); return SWITCH_CAUSE_NETWORK_OUT_OF_ORDER; } if (FTDM_SUCCESS != ftdm_channel_command(chan, FTDM_COMMAND_SET_PRE_BUFFER_SIZE, &tech_pvt->prebuffer_len)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to set channel pre buffer size.\n"); return SWITCH_CAUSE_NETWORK_OUT_OF_ORDER; } if (FTDM_SUCCESS != ftdm_channel_command(tech_pvt->ftdm_channel, FTDM_COMMAND_ENABLE_ECHOCANCEL, NULL)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to set enable echo cancellation.\n"); } switch(codec) { case FTDM_CODEC_ULAW: { dname = "PCMU"; } break; case FTDM_CODEC_ALAW: { dname = "PCMA"; } break; case FTDM_CODEC_SLIN: { dname = "L16"; } break; default: { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid codec value retrieved from channel, codec value: %d\n", codec); goto fail; } } if (switch_core_codec_init(&tech_pvt->read_codec, dname, NULL, NULL, 8000, 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"); goto fail; } else { if (switch_core_codec_init(&tech_pvt->write_codec, dname, NULL, NULL, 8000, 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); goto fail; } } if (switch_core_session_set_read_codec(*new_session, &tech_pvt->read_codec) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't set read codec?\n"); goto fail; } if (switch_core_session_set_write_codec(*new_session, &tech_pvt->write_codec) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't set write codec?\n"); } if (switch_core_session_thread_launch(*new_session) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't start session thread.\n"); goto fail; } switch_channel_mark_answered(channel); return SWITCH_CAUSE_SUCCESS; fail: if (tech_pvt) { if (tech_pvt->ftdm_channel) { ftdm_channel_close(&tech_pvt->ftdm_channel); } 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); } } if (*new_session) { switch_core_session_destroy(new_session); } return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; } static switch_status_t channel_on_init(switch_core_session_t *session) { switch_channel_t *channel = switch_core_session_get_channel(session); switch_channel_set_state(channel, CS_CONSUME_MEDIA); return SWITCH_STATUS_SUCCESS; } static switch_status_t channel_on_destroy(switch_core_session_t *session) { ctdm_private_t *tech_pvt = switch_core_session_get_private(session); if ((tech_pvt = switch_core_session_get_private(session))) { if (FTDM_SUCCESS != ftdm_channel_command(tech_pvt->ftdm_channel, FTDM_COMMAND_ENABLE_ECHOCANCEL, NULL)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to enable echo cancellation.\n"); } 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_unset_read_codec(session); switch_core_session_unset_write_codec(session); ftdm_channel_close(&tech_pvt->ftdm_channel); } 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) { ftdm_wait_flag_t wflags = FTDM_READ; ftdm_status_t status; ctdm_private_t *tech_pvt; const char *name; switch_channel_t *channel; int chunk; uint32_t span_id, chan_id; ftdm_size_t len; char dtmf[128] = ""; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); name = switch_channel_get_name(channel); top: wflags = FTDM_READ; chunk = ftdm_channel_get_io_interval(tech_pvt->ftdm_channel) * 2; status = ftdm_channel_wait(tech_pvt->ftdm_channel, &wflags, chunk); span_id = ftdm_channel_get_span_id(tech_pvt->ftdm_channel); chan_id = ftdm_channel_get_id(tech_pvt->ftdm_channel); if (status == FTDM_FAIL) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to read from channel %s device %d:%d!\n", name, span_id, chan_id); goto fail; } if (status == FTDM_TIMEOUT) { goto top; } if (!(wflags & FTDM_READ)) { goto top; } len = tech_pvt->read_frame.buflen; if (ftdm_channel_read(tech_pvt->ftdm_channel, tech_pvt->read_frame.data, &len) != FTDM_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to read from channel %s device %d:%d!\n", name, span_id, chan_id); } *frame = &tech_pvt->read_frame; tech_pvt->read_frame.datalen = (uint32_t)len; tech_pvt->read_frame.samples = tech_pvt->read_frame.datalen; tech_pvt->read_frame.codec = &tech_pvt->read_codec; if (ftdm_channel_get_codec(tech_pvt->ftdm_channel) == FTDM_CODEC_SLIN) { tech_pvt->read_frame.samples /= 2; } while (ftdm_channel_dequeue_dtmf(tech_pvt->ftdm_channel, dtmf, sizeof(dtmf))) { switch_dtmf_t _dtmf = { 0, switch_core_default_dtmf_duration(0) }; char *p; for (p = dtmf; p && *p; p++) { if (is_dtmf(*p)) { _dtmf.digit = *p; ftdm_log(FTDM_LOG_DEBUG, "Queuing DTMF [%c] in channel %s device %d:%d\n", *p, name, span_id, chan_id); switch_channel_queue_dtmf(channel, &_dtmf); } } } return SWITCH_STATUS_SUCCESS; fail: 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) { ftdm_wait_flag_t wflags = FTDM_WRITE; ctdm_private_t *tech_pvt; const char *name; switch_channel_t *channel; uint32_t span_id, chan_id; ftdm_size_t len; unsigned char data[SWITCH_RECOMMENDED_BUFFER_SIZE] = {0}; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); span_id = ftdm_channel_get_span_id(tech_pvt->ftdm_channel); chan_id = ftdm_channel_get_id(tech_pvt->ftdm_channel); name = switch_channel_get_name(channel); if (switch_test_flag(frame, SFF_CNG)) { frame->data = data; frame->buflen = sizeof(data); if ((frame->datalen = tech_pvt->write_codec.implementation->encoded_bytes_per_packet) > frame->buflen) { goto fail; } memset(data, 255, frame->datalen); } wflags = FTDM_WRITE; ftdm_channel_wait(tech_pvt->ftdm_channel, &wflags, ftdm_channel_get_io_interval(tech_pvt->ftdm_channel) * 10); if (!(wflags & FTDM_WRITE)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Dropping frame! (write not ready) in channel %s device %d:%d!\n", name, span_id, chan_id); return SWITCH_STATUS_SUCCESS; } len = frame->datalen; if (ftdm_channel_write(tech_pvt->ftdm_channel, frame->data, frame->buflen, &len) != FTDM_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Failed to write to channel %s device %d:%d!\n", name, span_id, chan_id); } return SWITCH_STATUS_SUCCESS; fail: return SWITCH_STATUS_GENERR; } static switch_status_t channel_send_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf) { ctdm_private_t *tech_pvt = NULL; char tmp[2] = ""; tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); tmp[0] = dtmf->digit; ftdm_channel_command(tech_pvt->ftdm_channel, FTDM_COMMAND_SEND_DTMF, tmp); return SWITCH_STATUS_SUCCESS; } static switch_status_t channel_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg) { return SWITCH_STATUS_SUCCESS; } static switch_status_t channel_receive_event(switch_core_session_t *session, switch_event_t *event) { const char *command = switch_event_get_header(event, "command"); ctdm_private_t *tech_pvt = switch_core_session_get_private(session); if (!zstr(command)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "FreeTDM received %s command \n",command); if (!strcasecmp(command, kPREBUFFER_LEN)) { const char *szval = switch_event_get_header(event, kPREBUFFER_LEN); int val = !zstr(szval) ? atoi(szval) : 0; if (tech_pvt->prebuffer_len == val) { tech_pvt->prebuffer_len = val; if (FTDM_SUCCESS != ftdm_channel_command(tech_pvt->ftdm_channel, FTDM_COMMAND_SET_PRE_BUFFER_SIZE, &tech_pvt->prebuffer_len)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to set channel pre buffer size.\n"); return SWITCH_STATUS_GENERR; } } } else if (!strcasecmp(command, kECHOCANCEL)) { const char *szval = switch_event_get_header(event, kECHOCANCEL); int enabled = !!switch_true(szval); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "FreeTDM sending echo cancel [%s] command \n",enabled ? "enable" : "disable"); if (FTDM_SUCCESS != ftdm_channel_command(tech_pvt->ftdm_channel, enabled ? FTDM_COMMAND_ENABLE_ECHOCANCEL : FTDM_COMMAND_DISABLE_ECHOCANCEL, NULL)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to %s echo cancellation.\n", enabled ? "enable" : "disable"); } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "FreeTDM received unknown command [%s] \n",command); } } return SWITCH_STATUS_SUCCESS; }