From f5894db211d69cc2dafff85c163e2e78c351a4bd Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Tue, 22 Jul 2014 20:53:28 -0400 Subject: [PATCH] OPENZAP-238: [freetdm] Several core and gsm improvements * Add error handing in mod_freetdm for ftdm trace failures * Allow freetdm signaling modules to specify a destroy function * Added conditional forwarding to the freetdm gsm module Just specify the conditional-forwarding-number gsm parameter in freetdm.conf.xml * Added new 'gsm call' freetdm command for raw GSM calls which can be used to enable/disabling network features (e.g call *93) without having to resort to use a full originate that requires routing the call somewhere when answered * Miscelaneous cleanup of piggy coding style left over by one of the previous authors -_- --- libs/freetdm/mod_freetdm/mod_freetdm.c | 31 +++- libs/freetdm/src/ftdm_io.c | 20 ++- libs/freetdm/src/ftmod/ftmod_gsm/ftmod_gsm.c | 166 ++++++++++++++---- libs/freetdm/src/include/private/ftdm_core.h | 1 + libs/freetdm/src/include/private/ftdm_types.h | 1 + 5 files changed, 172 insertions(+), 47 deletions(-) diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c index 0686524a51..f564586fb2 100644 --- a/libs/freetdm/mod_freetdm/mod_freetdm.c +++ b/libs/freetdm/mod_freetdm/mod_freetdm.c @@ -4936,6 +4936,7 @@ FTDM_CLI_DECLARE(ftdm_cmd_trace) uint32_t chan_id = 0; uint32_t span_id = 0; uint32_t chan_count = 0; + ftdm_status_t status; ftdm_span_t *span = NULL; ftdm_channel_t *chan = NULL; @@ -4964,17 +4965,39 @@ FTDM_CLI_DECLARE(ftdm_cmd_trace) if (chan_id) { chan = ftdm_span_get_channel(span, chan_id); + snprintf(tracepath, sizeof(tracepath), "%s-in-s%dc%d", argv[1], span_id, chan_id); - ftdm_channel_command(chan, FTDM_COMMAND_TRACE_INPUT, tracepath); + status = ftdm_channel_command(chan, FTDM_COMMAND_TRACE_INPUT, tracepath); + if (status != FTDM_SUCCESS) { + stream->write_function(stream, "-ERR failed to enable input trace at path %s\n", tracepath); + goto end; + } + snprintf(tracepath, sizeof(tracepath), "%s-out-s%dc%d", argv[1], span_id, chan_id); - ftdm_channel_command(chan, FTDM_COMMAND_TRACE_OUTPUT, tracepath); + status = ftdm_channel_command(chan, FTDM_COMMAND_TRACE_OUTPUT, tracepath); + if (status != FTDM_SUCCESS) { + stream->write_function(stream, "-ERR failed to enable output trace at path %s\n", tracepath); + ftdm_channel_command(chan, FTDM_COMMAND_TRACE_END_ALL, NULL); + goto end; + } } else { for (i = 1; i <= chan_count; i++) { chan = ftdm_span_get_channel(span, i); + snprintf(tracepath, sizeof(tracepath), "%s-in-s%dc%d", argv[1], span_id, i); - ftdm_channel_command(chan, FTDM_COMMAND_TRACE_INPUT, tracepath); + status = ftdm_channel_command(chan, FTDM_COMMAND_TRACE_INPUT, tracepath); + if (status != FTDM_SUCCESS) { + stream->write_function(stream, "-ERR failed to enable input trace at path %s\n", tracepath); + goto end; + } + snprintf(tracepath, sizeof(tracepath), "%s-out-s%dc%d", argv[1], span_id, i); - ftdm_channel_command(chan, FTDM_COMMAND_TRACE_OUTPUT, tracepath); + status = ftdm_channel_command(chan, FTDM_COMMAND_TRACE_OUTPUT, tracepath); + if (status != FTDM_SUCCESS) { + stream->write_function(stream, "-ERR failed to enable output trace at path %s\n", tracepath); + ftdm_channel_command(chan, FTDM_COMMAND_TRACE_END_ALL, NULL); + goto end; + } } } stream->write_function(stream, "+OK trace enabled with prefix path %s\n", argv[1]); diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index fd411b55d6..2312216d1c 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -728,7 +728,14 @@ static ftdm_status_t ftdm_span_destroy(ftdm_span_t *span) } ftdm_mutex_unlock(span->mutex); ftdm_mutex_destroy(&span->mutex); - ftdm_safe_free(span->signal_data); + + /* Give the span a chance to destroy its own signaling data */ + if (span->destroy) { + span->destroy(span); + } else if (span->signal_data) { + /* We take care of their dirty business ... */ + ftdm_free(span->signal_data); + } return status; } @@ -5520,7 +5527,8 @@ FT_DECLARE(ftdm_io_interface_t *) ftdm_global_get_io_interface(const char *iotyp FT_DECLARE(int) ftdm_load_module(const char *name) { ftdm_dso_lib_t lib; - int count = 0, x = 0; + int count = 0; + ftdm_bool_t load_proceed = FTDM_TRUE; char path[512] = ""; char *err; ftdm_module_t *mod; @@ -5544,11 +5552,11 @@ FT_DECLARE(int) ftdm_load_module(const char *name) if (mod->io_load(&interface1) != FTDM_SUCCESS || !interface1 || !interface1->name) { ftdm_log(FTDM_LOG_ERROR, "Error loading %s\n", path); + load_proceed = FTDM_FALSE; } else { ftdm_log(FTDM_LOG_INFO, "Loading IO from %s [%s]\n", path, interface1->name); if (ftdm_global_add_io_interface(interface1) == FTDM_SUCCESS) { process_module_config(interface1); - x++; } } } @@ -5556,13 +5564,13 @@ FT_DECLARE(int) ftdm_load_module(const char *name) if (mod->sig_load) { if (mod->sig_load() != FTDM_SUCCESS) { ftdm_log(FTDM_LOG_ERROR, "Error loading %s\n", path); + load_proceed = FTDM_FALSE; } else { ftdm_log(FTDM_LOG_INFO, "Loading SIG from %s\n", path); - x++; } } - if (x) { + if (load_proceed) { char *p; mod->lib = lib; ftdm_set_string(mod->path, path); @@ -5583,7 +5591,7 @@ FT_DECLARE(int) ftdm_load_module(const char *name) } ftdm_mutex_unlock(globals.mutex); } else { - ftdm_log(FTDM_LOG_ERROR, "Unloading %s\n", path); + ftdm_log(FTDM_LOG_ERROR, "Errors during module load. Unloading %s\n", path); ftdm_dso_destroy(&lib); } diff --git a/libs/freetdm/src/ftmod/ftmod_gsm/ftmod_gsm.c b/libs/freetdm/src/ftmod/ftmod_gsm/ftmod_gsm.c index 86ea231708..e0a1143cb2 100755 --- a/libs/freetdm/src/ftmod/ftmod_gsm/ftmod_gsm.c +++ b/libs/freetdm/src/ftmod/ftmod_gsm/ftmod_gsm.c @@ -82,6 +82,7 @@ "ftdm gsm status \n" \ "ftdm gsm sms \n" \ "ftdm gsm exec \n" \ +"ftdm gsm call [number]\n" \ "--------------------------------------------------------------------------------\n" // Used to declare command handler @@ -105,6 +106,9 @@ typedef struct ftdm_gsm_span_data_s { ftdm_channel_t *bchan; int32_t call_id; uint32_t sms_id; + char conditional_forward_number[255]; + ftdm_sched_t *sched; + ftdm_timer_id_t conditional_forwarding_timer; } ftdm_gsm_span_data_t; // command handler function type. @@ -200,8 +204,46 @@ int on_wat_span_write(unsigned char span_id, void *buffer, unsigned len) return len; } +static void ftdm_gsm_make_raw_call(ftdm_gsm_span_data_t *gsm_data, const char *number) +{ + wat_con_event_t con_event; + + ftdm_channel_lock(gsm_data->bchan); + + if (ftdm_test_flag(gsm_data->bchan, FTDM_CHANNEL_INUSE)) { + ftdm_log_chan(gsm_data->bchan, FTDM_LOG_ERROR, "Failed to place raw call to %s: channel busy\n", number); + goto done; + } + + ftdm_log_chan(gsm_data->bchan, FTDM_LOG_INFO, "Placing raw call to %s\n", number); + ftdm_set_flag(gsm_data->bchan, FTDM_CHANNEL_INUSE); + + gsm_data->call_id = g_outbound_call_id++; + memset(&con_event, 0, sizeof(con_event)); + ftdm_set_string(con_event.called_num.digits, number); + wat_con_req(gsm_data->span->span_id, gsm_data->call_id , &con_event); + +done: + ftdm_channel_unlock(gsm_data->bchan); +} + +static void ftdm_gsm_enable_conditional_forwarding(void *data) +{ + ftdm_gsm_span_data_t *gsm_data = data; + ftdm_log_chan(gsm_data->bchan, FTDM_LOG_NOTICE, "Enabling conditional forwarding to %s\n", gsm_data->conditional_forward_number); + ftdm_gsm_make_raw_call(data, gsm_data->conditional_forward_number); +} + static void on_wat_span_status(unsigned char span_id, wat_span_status_t *status) { + ftdm_span_t *span = NULL; + ftdm_gsm_span_data_t *gsm_data = NULL; + if (!(span = GetSpanByID(span_id, &gsm_data))) { + ftdm_log(FTDM_LOG_ERROR, "Failed to get span from id %d\n", span_id); + return; + } + gsm_data = span->signal_data; + switch (status->type) { case WAT_SPAN_STS_READY: { @@ -211,9 +253,15 @@ static void on_wat_span_status(unsigned char span_id, wat_span_status_t *status) case WAT_SPAN_STS_SIGSTATUS: { if (status->sts.sigstatus == WAT_SIGSTATUS_UP) { - ftdm_log(FTDM_LOG_INFO, "span %d: Signaling is now up\n", span_id); + ftdm_log_chan_msg(gsm_data->bchan, FTDM_LOG_INFO, "Signaling is now up\n"); } else { - ftdm_log(FTDM_LOG_INFO, "span %d: Signaling is now down\n", span_id); + ftdm_log_chan_msg(gsm_data->bchan, FTDM_LOG_INFO, "Signaling is now down\n"); + } + if (!ftdm_strlen_zero_buf(gsm_data->conditional_forward_number)) { + ftdm_sched_timer(gsm_data->sched, "conditional_forwarding_delay", 500, + ftdm_gsm_enable_conditional_forwarding, + gsm_data, + &gsm_data->conditional_forwarding_timer); } } break; @@ -309,33 +357,34 @@ void on_wat_con_sts(unsigned char span_id, uint8_t call_id, wat_con_status_t *st { ftdm_span_t *span = NULL; + ftdm_channel_state_t state = FTDM_CHANNEL_STATE_END; //ftdm_status_t ftdm_status = FTDM_FAIL; ftdm_gsm_span_data_t *gsm_data = NULL; - if(!(span = GetSpanByID(span_id, &gsm_data))) { + if (!(span = GetSpanByID(span_id, &gsm_data))) { return; } - - - switch(status->type) { - + switch (status->type) { case WAT_CON_STATUS_TYPE_RINGING: - ftdm_log(FTDM_LOG_INFO, "on_wat_con_sts - WAT_CON_STATUS_TYPE_RINGING\r\n"); - ftdm_set_state(gsm_data->bchan, FTDM_CHANNEL_STATE_RINGING); + ftdm_log_chan_msg(gsm_data->bchan, FTDM_LOG_INFO, "Received ringing indication\n"); + state = FTDM_CHANNEL_STATE_RINGING; break; case WAT_CON_STATUS_TYPE_ANSWER: - ftdm_log(FTDM_LOG_INFO, "on_wat_con_sts - WAT_CON_STATUS_TYPE_ANSWER\r\n"); - ftdm_set_state(gsm_data->bchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA); + ftdm_log_chan_msg(gsm_data->bchan, FTDM_LOG_INFO, "Received answer indication\n"); + state = FTDM_CHANNEL_STATE_PROGRESS_MEDIA; break; + default: - ftdm_log(FTDM_LOG_INFO, "on_wat_con_sts - Unhandled state %d\n", span_id); + ftdm_log_chan(gsm_data->bchan, FTDM_LOG_WARNING, "Unhandled indication status %d\n", status->type); + break; }; - - - return; + + if (state != FTDM_CHANNEL_STATE_END && gsm_data->bchan->state != FTDM_CHANNEL_STATE_DOWN) { + ftdm_set_state(gsm_data->bchan, state); + } } void on_wat_rel_ind(unsigned char span_id, uint8_t call_id, wat_rel_event_t *rel_event) @@ -352,6 +401,8 @@ void on_wat_rel_ind(unsigned char span_id, uint8_t call_id, wat_rel_event_t *rel if (gsm_data->bchan->state == FTDM_CHANNEL_STATE_HANGUP || gsm_data->bchan->state == FTDM_CHANNEL_STATE_DOWN) { + /* this might be due to a call to enable call forwarding, + * which does not run the state machine */ return; } @@ -366,7 +417,13 @@ void on_wat_rel_cfm(unsigned char span_id, uint8_t call_id) ftdm_log(FTDM_LOG_INFO, "s%d: Call hangup complete (id:%d)\n", span_id, call_id); - if(!(span = GetSpanByID(span_id, &gsm_data))) { + if (!(span = GetSpanByID(span_id, &gsm_data))) { + return; + } + + if (gsm_data->bchan->state == FTDM_CHANNEL_STATE_DOWN) { + /* this is most likely a call forwarding enable call, which do not run the state machine */ + ftdm_clear_flag(gsm_data->bchan, FTDM_CHANNEL_INUSE); return; } @@ -378,7 +435,6 @@ void on_wat_rel_cfm(unsigned char span_id, uint8_t call_id) ftdm_set_state(gsm_data->bchan, FTDM_CHANNEL_STATE_DOWN); break; } - } void on_wat_sms_ind(unsigned char span_id, wat_sms_event_t *sms_event) @@ -528,7 +584,6 @@ static ftdm_status_t ftdm_gsm_start(ftdm_span_t *span) ftdm_log(FTDM_LOG_ERROR, "Failed to start span %s!\n", span->name); return FTDM_FAIL; } - return ftdm_thread_create_detached(ftdm_gsm_run, span); } @@ -541,6 +596,17 @@ static ftdm_status_t ftdm_gsm_stop(ftdm_span_t *span) return FTDM_SUCCESS; } +static ftdm_status_t ftdm_gsm_destroy(ftdm_span_t *span) +{ + ftdm_gsm_span_data_t *gsm_data = span->signal_data; + ftdm_assert_return(gsm_data != NULL, FTDM_FAIL, "Span does not have GSM data!\n"); + if (gsm_data->sched) { + ftdm_sched_destroy(&gsm_data->sched); + } + ftdm_free(gsm_data); + return FTDM_SUCCESS; +} + static FIO_CHANNEL_GET_SIG_STATUS_FUNCTION(ftdm_gsm_get_channel_sig_status) { if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_SIG_UP)) { @@ -898,6 +964,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_gsm_configure_span_signaling) unsigned paramindex = 0; const char *var = NULL; const char *val = NULL; + char schedname[255]; int codec = FTDM_CODEC_SLIN; int interval = 20; @@ -993,6 +1060,8 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_gsm_configure_span_signaling) span_config.hardware_dtmf = WAT_FALSE; } ftdm_log(FTDM_LOG_DEBUG, "Configuring GSM span %s with hardware dtmf %s\n", span->name, val); + } else if (!strcasecmp(var, "conditional-forwarding-number")) { + ftdm_set_string(gsm_data->conditional_forward_number, val); } else { ftdm_log(FTDM_LOG_ERROR, "Ignoring unknown GSM parameter '%s'", var); } @@ -1001,6 +1070,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_gsm_configure_span_signaling) /* Bind function pointers for control operations */ span->start = ftdm_gsm_start; span->stop = ftdm_gsm_stop; + span->destroy = ftdm_gsm_destroy; span->sig_read = NULL; span->sig_write = NULL; if (hwdtmf_detect || hwdtmf_generate) { @@ -1033,8 +1103,18 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_gsm_configure_span_signaling) gsm_data->span = span; + /* Setup the scheduler */ + snprintf(schedname, sizeof(schedname), "ftmod_gsm_%s", span->name); + if (ftdm_sched_create(&gsm_data->sched, schedname) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Failed to setup scheduler for span %s!\n", span->name); + ftdm_gsm_destroy(span); + return FTDM_FAIL; + } + + /* Start the signaling stack */ if (wat_span_config(span->span_id, &span_config)) { ftdm_log(FTDM_LOG_ERROR, "Failed to configure span %s for GSM signaling!!\n", span->name); + ftdm_gsm_destroy(span); return FTDM_FAIL; } @@ -1057,11 +1137,10 @@ static void *ftdm_gsm_run(ftdm_thread_t *me, void *obj) ftdm_status_t status = FTDM_SUCCESS; char buffer[1025] = { 0 }; int waitms = 0; + ftdm_wait_flag_t flags = 0; + ftdm_status_t status; - ftdm_log(FTDM_LOG_INFO,"ftdm_gsm_run\r\n"); - gsm_data = span->signal_data; - ftdm_assert_return(gsm_data != NULL, NULL, "No gsm data attached to span\n"); ftdm_log(FTDM_LOG_DEBUG, "GSM monitor thread for span %s started\n", span->name); @@ -1073,6 +1152,7 @@ static void *ftdm_gsm_run(ftdm_thread_t *me, void *obj) while (ftdm_running()) { wat_span_run(span->span_id); + ftdm_sched_run(gsm_data->sched); waitms = wat_span_schedule_next(span->span_id); if (waitms > GSM_POLL_INTERVAL_MS) { @@ -1123,25 +1203,14 @@ static FIO_IO_LOAD_FUNCTION(ftdm_gsm_io_init) return (FTDM_SUCCESS); } -static FIO_SIG_LOAD_FUNCTION(ftdm_gsm_init) -{ - /* this is called on module load */ - return FTDM_SUCCESS; -} - -static FIO_SIG_UNLOAD_FUNCTION(ftdm_gsm_destroy) -{ - /* this is called on module unload */ - return FTDM_SUCCESS; -} EX_DECLARE_DATA ftdm_module_t ftdm_module = { /* .name */ "gsm", /* .io_load */ ftdm_gsm_io_init, /* .io_unload */ NULL, - /* .sig_load */ ftdm_gsm_init, + /* .sig_load */ NULL, /* .sig_configure */ NULL, - /* .sig_unload */ ftdm_gsm_destroy, + /* .sig_unload */ NULL, /* .configure_span_signaling */ ftdm_gsm_configure_span_signaling }; @@ -1317,12 +1386,12 @@ COMMAND_HANDLER(exec) span_id = atoi(argv[0]); if (ftdm_span_find_by_name(argv[0], &span) != FTDM_SUCCESS && ftdm_span_find(span_id, &span) != FTDM_SUCCESS) { - stream->write_function(stream, "-ERR Failed to find GSM span '%s'\n", argv[1]); + stream->write_function(stream, "-ERR Failed to find GSM span '%s'\n", argv[0]); return FTDM_FAIL; } if (!span || !span->signal_data || (span->start != ftdm_gsm_start)) { - stream->write_function(stream, "-ERR '%s' is not a valid GSM span\n", argv[1]); + stream->write_function(stream, "-ERR '%s' is not a valid GSM span\n", argv[0]); return FTDM_FAIL; } @@ -1345,6 +1414,28 @@ COMMAND_HANDLER(exec) return FTDM_SUCCESS; } +// AT Command Handler +COMMAND_HANDLER(call) +{ + int span_id = 0; + ftdm_span_t *span = NULL; + + span_id = atoi(argv[0]); + if (ftdm_span_find_by_name(argv[0], &span) != FTDM_SUCCESS && ftdm_span_find(span_id, &span) != FTDM_SUCCESS) { + stream->write_function(stream, "-ERR Failed to find GSM span '%s'\n", argv[0]); + return FTDM_FAIL; + } + + if (!span || !span->signal_data || (span->start != ftdm_gsm_start)) { + stream->write_function(stream, "-ERR '%s' is not a valid GSM span\n", argv[0]); + return FTDM_FAIL; + } + + ftdm_gsm_make_raw_call(span->signal_data, argv[1]); + stream->write_function(stream, "+OK\n"); + return FTDM_SUCCESS; +} + // command map struct { const char*CMD; // command @@ -1355,7 +1446,8 @@ struct { COMMAND(version, 0), COMMAND(status, 1), COMMAND(sms, 3), - COMMAND(exec, 2) + COMMAND(exec, 2), + COMMAND(call, 2), }; // Commnand API entry point diff --git a/libs/freetdm/src/include/private/ftdm_core.h b/libs/freetdm/src/include/private/ftdm_core.h index 609198e731..935cd50078 100644 --- a/libs/freetdm/src/include/private/ftdm_core.h +++ b/libs/freetdm/src/include/private/ftdm_core.h @@ -512,6 +512,7 @@ struct ftdm_span { fio_channel_request_t channel_request; ftdm_span_start_t start; ftdm_span_stop_t stop; + ftdm_span_destroy_t destroy; ftdm_channel_sig_read_t sig_read; ftdm_channel_sig_write_t sig_write; ftdm_channel_sig_dtmf_t sig_queue_dtmf; diff --git a/libs/freetdm/src/include/private/ftdm_types.h b/libs/freetdm/src/include/private/ftdm_types.h index 6cd4e191e2..a498489af1 100755 --- a/libs/freetdm/src/include/private/ftdm_types.h +++ b/libs/freetdm/src/include/private/ftdm_types.h @@ -348,6 +348,7 @@ typedef struct ftdm_bitstream ftdm_bitstream_t; typedef struct ftdm_fsk_modulator ftdm_fsk_modulator_t; typedef ftdm_status_t (*ftdm_span_start_t)(ftdm_span_t *span); typedef ftdm_status_t (*ftdm_span_stop_t)(ftdm_span_t *span); +typedef ftdm_status_t (*ftdm_span_destroy_t)(ftdm_span_t *span); typedef ftdm_status_t (*ftdm_channel_sig_read_t)(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t size); typedef ftdm_status_t (*ftdm_channel_sig_write_t)(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t size); typedef ftdm_status_t (*ftdm_channel_sig_dtmf_t)(ftdm_channel_t *ftdmchan, const char *dtmf);