diff --git a/conf/vars.xml b/conf/vars.xml index fced2ef139..f17540c93a 100644 --- a/conf/vars.xml +++ b/conf/vars.xml @@ -204,6 +204,8 @@ + + + - + - - + + + + + + + - - + + + + + + + + + + + + + + + + - diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.2010.vcxproj b/libs/freetdm/mod_freetdm/mod_freetdm.2010.vcxproj index 9f01a3b1a1..ec69333410 100644 --- a/libs/freetdm/mod_freetdm/mod_freetdm.2010.vcxproj +++ b/libs/freetdm/mod_freetdm/mod_freetdm.2010.vcxproj @@ -103,7 +103,7 @@ ProgramDatabase - FreeSwitch.lib;%(AdditionalDependencies) + FreeSwitchCore.lib;%(AdditionalDependencies) ../../../$(PlatformName)\$(Configuration);%(AdditionalLibraryDirectories) true $(OutDir)$(TargetName).pdb @@ -127,7 +127,7 @@ ProgramDatabase - FreeSwitch.lib;%(AdditionalDependencies) + FreeSwitchCore.lib;%(AdditionalDependencies) ../../../$(PlatformName)\$(Configuration);%(AdditionalLibraryDirectories) true $(OutDir)$(TargetName).pdb diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c index 66bc532cbc..d9c26db4fe 100755 --- a/libs/freetdm/mod_freetdm/mod_freetdm.c +++ b/libs/freetdm/mod_freetdm/mod_freetdm.c @@ -2717,6 +2717,9 @@ static switch_status_t load_config(void) char *hold_music = NULL; char *fail_dial_regex = NULL; const char *enable_callerid = "true"; + const char *answer_polarity = "false"; + const char *hangup_polarity = "false"; + int polarity_delay = 600; int callwaiting = 1; uint32_t span_id = 0, to = 0, max = 0; @@ -2788,6 +2791,12 @@ static switch_status_t load_config(void) dial_regex = val; } else if (!strcasecmp(var, "enable-callerid")) { enable_callerid = val; + } else if (!strcasecmp(var, "answer-polarity-reverse")) { + answer_polarity = val; + } else if (!strcasecmp(var, "hangup-polarity-reverse")) { + hangup_polarity = val; + } else if (!strcasecmp(var, "polarity-delay")) { + polarity_delay = atoi(val); } else if (!strcasecmp(var, "fail-dial-regex")) { fail_dial_regex = val; } else if (!strcasecmp(var, "hold-music")) { @@ -2848,6 +2857,9 @@ static switch_status_t load_config(void) "max_dialstr", &max, "hotline", hotline ? hotline : "", "enable_callerid", enable_callerid, + "answer_polarity_reverse", answer_polarity, + "hangup_polarity_reverse", hangup_polarity, + "polarity_delay", &polarity_delay, "callwaiting", &callwaiting, FTDM_TAG_END) != FTDM_SUCCESS) { ftdm_log(FTDM_LOG_ERROR, "Error configuring FreeTDM analog span %s\n", ftdm_span_get_name(span)); diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index 458025e5de..451441ec97 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -63,7 +63,7 @@ ftdm_time_t time_last_throttle_log = 0; ftdm_time_t time_current_throttle_log = 0; static ftdm_iterator_t *get_iterator(ftdm_iterator_type_t type, ftdm_iterator_t *iter); -static ftdm_status_t ftdm_call_set_call_id(ftdm_caller_data_t *caller_data); +static ftdm_status_t ftdm_call_set_call_id(ftdm_channel_t *fchan, ftdm_caller_data_t *caller_data); static ftdm_status_t ftdm_call_clear_call_id(ftdm_caller_data_t *caller_data); static ftdm_status_t ftdm_channel_clear_vars(ftdm_channel_t *ftdmchan); static ftdm_status_t ftdm_channel_done(ftdm_channel_t *ftdmchan); @@ -1546,6 +1546,7 @@ end: ftdm_mutex_unlock(ftdmchan->span->mutex); } else { ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "VETO state change from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); + goto done; } /* there is an inherent race here between set and check of the change flag but we do not care because @@ -1575,7 +1576,7 @@ end: ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not processed after aprox %dms\n", ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(state), DEFAULT_WAIT_TIME); } - +done: return ok ? FTDM_SUCCESS : FTDM_FAIL; } @@ -2294,8 +2295,15 @@ static ftdm_status_t call_hangup(ftdm_channel_t *chan, const char *file, const c ftdm_channel_set_state(file, func, line, chan, FTDM_CHANNEL_STATE_HANGUP, 1); } else { /* the signaling stack did not touch the state, - * core is responsible from clearing flags and stuff */ - ftdm_channel_close(&chan); + * core is responsible from clearing flags and stuff, however, because ftmod_analog + * is a bitch in a serious need of refactoring, we also check whether the channel is open + * to avoid an spurious warning about the channel not being open. This is because ftmod_analog + * does not follow our convention of sending SIGEVENT_STOP and waiting for the user to move + * to HANGUP (implicitly through ftdm_channel_call_hangup(), as soon as ftmod_analog is fixed + * this check can be removed */ + if (ftdm_test_flag(chan, FTDM_CHANNEL_OPEN)) { + ftdm_channel_close(&chan); + } } return FTDM_SUCCESS; } @@ -2519,7 +2527,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_place(const char *file, const char if (status == FTDM_SUCCESS) { ftdm_set_flag(ftdmchan, FTDM_CHANNEL_CALL_STARTED); - ftdm_call_set_call_id(&ftdmchan->caller_data); + ftdm_call_set_call_id(ftdmchan, &ftdmchan->caller_data); ftdm_wait_for_flag_cleared(ftdmchan, FTDM_CHANNEL_STATE_CHANGE, 100); } @@ -2603,10 +2611,11 @@ FT_DECLARE(ftdm_status_t) ftdm_span_get_sig_status(ftdm_span_t *span, ftdm_signa } } +/* this function must be called with the channel lock */ static ftdm_status_t ftdm_channel_done(ftdm_channel_t *ftdmchan) { ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "Null channel can't be done!\n"); - ftdm_mutex_lock(ftdmchan->mutex); + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_OPEN); ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_DTMF_DETECT); ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_SUPRESS_DTMF); @@ -2691,7 +2700,6 @@ static ftdm_status_t ftdm_channel_done(ftdm_channel_t *ftdmchan) ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_TRANSCODE); } ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "channel done\n"); - ftdm_mutex_unlock(ftdmchan->mutex); return FTDM_SUCCESS; } @@ -3938,7 +3946,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_write(ftdm_channel_t *ftdmchan, void *dat if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OPEN)) { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "cannot write in channel not open\n"); + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "cannot write in channel not open\n"); snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "channel not open"); status = FTDM_FAIL; goto done; @@ -4394,6 +4402,16 @@ static void print_channels_by_state(ftdm_stream_handle_t *stream, ftdm_channel_s ftdm_mutex_unlock(globals.mutex); } +static void print_core_usage(ftdm_stream_handle_t *stream) +{ + stream->write_function(stream, + "--------------------------------------------------------------------------------\n" + "ftdm core state [!] - List all channels in or not in the given state\n" + "ftdm core flag - List all channels with the fiven flag value set\n" + "ftdm core calls - List all known calls to the FreeTDM core\n" + "--------------------------------------------------------------------------------\n"); +} + static char *handle_core_command(const char *cmd) { char *mycmd = NULL; @@ -4404,22 +4422,31 @@ static char *handle_core_command(const char *cmd) char *state = NULL; char *flag = NULL; uint32_t flagval = 0; + uint32_t current_call_id = 0; + ftdm_caller_data_t *calldata = NULL; + ftdm_channel_t *fchan = NULL; ftdm_channel_state_t i = FTDM_CHANNEL_STATE_INVALID; ftdm_stream_handle_t stream = { 0 }; FTDM_STANDARD_STREAM(stream); - if (cmd) { + if (cmd && strlen(cmd)) { mycmd = ftdm_strdup(cmd); argc = ftdm_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); } else { - stream.write_function(&stream, "invalid core command\n"); + print_core_usage(&stream); + goto done; + } + + if (!argc) { + print_core_usage(&stream); goto done; } if (!strcasecmp(argv[0], "state")) { if (argc < 2) { stream.write_function(&stream, "core state command requires an argument\n"); + print_core_usage(&stream); goto done; } state = argv[1]; @@ -4440,7 +4467,8 @@ static char *handle_core_command(const char *cmd) stream.write_function(&stream, "\nTotal channels %s %s: %d\n", not ? "not in state" : "in state", ftdm_channel_state2str(i), count); } else if (!strcasecmp(argv[0], "flag")) { if (argc < 2) { - stream.write_function(&stream, "core state command requires an argument\n"); + stream.write_function(&stream, "core flag command requires an argument\n"); + print_core_usage(&stream); goto done; } flag = argv[1]; @@ -4451,8 +4479,28 @@ static char *handle_core_command(const char *cmd) flagval = atoi(flag); print_channels_by_flag(&stream, flagval, not, &count); stream.write_function(&stream, "\nTotal channels %s %d: %d\n", not ? "without flag" : "with flag", flagval, count); + } else if (!strcasecmp(argv[0], "calls")) { + ftdm_mutex_lock(globals.call_id_mutex); + current_call_id = globals.last_call_id; + for (current_call_id = 0; current_call_id <= MAX_CALLIDS; current_call_id++) { + if (!globals.call_ids[current_call_id]) { + continue; + } + calldata = globals.call_ids[current_call_id]; + fchan = calldata->fchan; + if (fchan) { + stream.write_function(&stream, "Call %d on channel %d:%d\n", current_call_id, + fchan->span_id, fchan->chan_id); + } else { + stream.write_function(&stream, "Call %d without a channel?\n", current_call_id); + } + count++; + } + ftdm_mutex_unlock(globals.call_id_mutex); + stream.write_function(&stream, "\nTotal calls: %d\n", count); } else { stream.write_function(&stream, "invalid core command %s\n", argv[0]); + print_core_usage(&stream); } done: @@ -4472,6 +4520,8 @@ FT_DECLARE(char *) ftdm_api_execute(const char *cmd) if ((p = strchr(dup, ' '))) { *p++ = '\0'; cmd = p; + } else { + cmd = ""; } type = dup; @@ -5444,7 +5494,7 @@ FT_DECLARE(ftdm_status_t) ftdm_span_send_signal(ftdm_span_t *span, ftdm_sigmsg_t case FTDM_SIGEVENT_START: { ftdm_set_flag(sigmsg->channel, FTDM_CHANNEL_CALL_STARTED); - ftdm_call_set_call_id(&sigmsg->channel->caller_data); + ftdm_call_set_call_id(sigmsg->channel, &sigmsg->channel->caller_data); ftdm_set_echocancel_call_begin(sigmsg->channel); if (sigmsg->channel->dtmfdbg.requested) { ftdm_channel_command(sigmsg->channel, FTDM_COMMAND_ENABLE_DEBUG_DTMF, NULL); @@ -6086,27 +6136,35 @@ FT_DECLARE(char *) ftdm_channel_get_history_str(const ftdm_channel_t *fchan) return stream.data; } -static ftdm_status_t ftdm_call_set_call_id(ftdm_caller_data_t *caller_data) +static ftdm_status_t ftdm_call_set_call_id(ftdm_channel_t *fchan, ftdm_caller_data_t *caller_data) { uint32_t current_call_id; - ftdm_assert_return(!caller_data->call_id, FTDM_FAIL, "Overwriting non-cleared call-id"); + + ftdm_assert_return(!caller_data->call_id, FTDM_FAIL, "Overwriting non-cleared call-id\n"); ftdm_mutex_lock(globals.call_id_mutex); + current_call_id = globals.last_call_id; - do { - if (++current_call_id > MAX_CALLIDS) { + for (current_call_id = globals.last_call_id + 1; + current_call_id != globals.last_call_id; + current_call_id++ ) { + if (current_call_id > MAX_CALLIDS) { current_call_id = 1; } - if (globals.call_ids[current_call_id] != NULL) { - continue; + if (globals.call_ids[current_call_id] == NULL) { + break; } - } while (0); + } + + ftdm_assert_return(globals.call_ids[current_call_id] == NULL, FTDM_FAIL, "We ran out of call ids\n"); globals.last_call_id = current_call_id; caller_data->call_id = current_call_id; globals.call_ids[current_call_id] = caller_data; + caller_data->fchan = fchan; + ftdm_mutex_unlock(globals.call_id_mutex); return FTDM_SUCCESS; } @@ -6123,8 +6181,8 @@ static ftdm_status_t ftdm_call_clear_call_id(ftdm_caller_data_t *caller_data) ftdm_mutex_lock(globals.call_id_mutex); if (globals.call_ids[caller_data->call_id]) { ftdm_log(FTDM_LOG_DEBUG, "Cleared call with id %u\n", caller_data->call_id); - caller_data->call_id = 0; globals.call_ids[caller_data->call_id] = NULL; + caller_data->call_id = 0; } else { ftdm_log(FTDM_LOG_CRIT, "call-id did not exist %u\n", caller_data->call_id); } diff --git a/libs/freetdm/src/ftmod/ftmod_analog/ftdm_analog.h b/libs/freetdm/src/ftmod/ftmod_analog/ftdm_analog.h index 2c79822b29..39cbc5ff2b 100644 --- a/libs/freetdm/src/ftmod/ftmod_analog/ftdm_analog.h +++ b/libs/freetdm/src/ftmod/ftmod_analog/ftdm_analog.h @@ -37,7 +37,9 @@ typedef enum { FTDM_ANALOG_RUNNING = (1 << 0), - FTDM_ANALOG_CALLERID = (1 << 1) + FTDM_ANALOG_CALLERID = (1 << 1), + FTDM_ANALOG_ANSWER_POLARITY_REVERSE = (1 << 2), + FTDM_ANALOG_HANGUP_POLARITY_REVERSE = (1 << 3) } ftdm_analog_flag_t; #define FTDM_MAX_HOTLINE_STR 20 @@ -47,11 +49,13 @@ struct ftdm_analog_data { uint32_t flags; uint32_t max_dialstr; uint32_t wait_dialtone_timeout; + uint32_t polarity_delay; uint32_t digit_timeout; char hotline[FTDM_MAX_HOTLINE_STR]; }; - +/* Analog flags to be set in the sflags (signaling flags) channel memeber */ +#define AF_POLARITY_REVERSE (1 << 0) static void *ftdm_analog_run(ftdm_thread_t *me, void *obj); typedef struct ftdm_analog_data ftdm_analog_data_t; diff --git a/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c b/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c index 0acf70b18d..981df5dcc9 100644 --- a/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c +++ b/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c @@ -184,6 +184,7 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_configure_span) uint32_t digit_timeout = 10; uint32_t wait_dialtone_timeout = 30000; uint32_t max_dialstr = MAX_DTMF; + uint32_t polarity_delay = 600; const char *var, *val; int *intval; uint32_t flags = FTDM_ANALOG_CALLERID; @@ -236,6 +237,29 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_configure_span) } else { flags &= ~FTDM_ANALOG_CALLERID; } + } else if (!strcasecmp(var, "answer_polarity_reverse")) { + if (!(val = va_arg(ap, char *))) { + break; + } + if (ftdm_true(val)) { + flags |= FTDM_ANALOG_ANSWER_POLARITY_REVERSE; + } else { + flags &= ~FTDM_ANALOG_ANSWER_POLARITY_REVERSE; + } + } else if (!strcasecmp(var, "hangup_polarity_reverse")) { + if (!(val = va_arg(ap, char *))) { + break; + } + if (ftdm_true(val)) { + flags |= FTDM_ANALOG_HANGUP_POLARITY_REVERSE; + } else { + flags &= ~FTDM_ANALOG_HANGUP_POLARITY_REVERSE; + } + } else if (!strcasecmp(var, "polarity_delay")) { + if (!(intval = va_arg(ap, int *))) { + break; + } + polarity_delay = *intval; } else if (!strcasecmp(var, "callwaiting")) { if (!(intval = va_arg(ap, int *))) { break; @@ -276,6 +300,7 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_configure_span) analog_data->flags = flags; analog_data->digit_timeout = digit_timeout; analog_data->wait_dialtone_timeout = wait_dialtone_timeout; + analog_data->polarity_delay = polarity_delay; analog_data->max_dialstr = max_dialstr; span->signal_cb = sig_cb; strncpy(analog_data->hotline, hotline, sizeof(analog_data->hotline)); @@ -399,6 +424,7 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj) ftdm_analog_data_t *analog_data = ftdmchan->span->signal_data; ftdm_channel_t *closed_chan; uint32_t state_counter = 0, elapsed = 0, collecting = 0, interval = 0, last_digit = 0, indicate = 0, dial_timeout = analog_data->wait_dialtone_timeout; + uint32_t answer_on_polarity_counter = 0; ftdm_sigmsg_t sig; ftdm_status_t status; @@ -470,7 +496,12 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj) if (ftdmchan->needed_tones[FTDM_TONEMAP_DIAL]) { ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_BUSY); } else { - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP); + /* do not go up if we're waiting for polarity reversal */ + if (ftdm_test_flag(analog_data, FTDM_ANALOG_ANSWER_POLARITY_REVERSE)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA); + } else { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP); + } } } } @@ -561,8 +592,30 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj) } case FTDM_CHANNEL_STATE_UP: case FTDM_CHANNEL_STATE_RING: + case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: { - ftdm_sleep(interval); + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) && + ftdmchan->state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA && + ftdm_test_sflag(ftdmchan, AF_POLARITY_REVERSE)) { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_NOTICE, "Answering on polarity reverse\n"); + ftdm_clear_sflag(ftdmchan, AF_POLARITY_REVERSE); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP); + answer_on_polarity_counter = state_counter; + } else if (ftdmchan->state == FTDM_CHANNEL_STATE_UP + && ftdm_test_sflag(ftdmchan, AF_POLARITY_REVERSE)){ + /* if this polarity reverse is close to the answer polarity reverse, ignore it */ + if (answer_on_polarity_counter + && (state_counter - answer_on_polarity_counter) > analog_data->polarity_delay) { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_NOTICE, "Hanging up on polarity reverse\n"); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } else { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, + "Not hanging up on polarity reverse, too close to Answer reverse\n"); + } + ftdm_clear_sflag(ftdmchan, AF_POLARITY_REVERSE); + } else { + ftdm_sleep(interval); + } continue; } break; @@ -615,6 +668,18 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj) sig.event_id = FTDM_SIGEVENT_UP; } + if (ftdmchan->type == FTDM_CHAN_TYPE_FXS && + !ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) && + ftdm_test_flag(analog_data, FTDM_ANALOG_ANSWER_POLARITY_REVERSE)) { + ftdm_polarity_t polarity = FTDM_POLARITY_REVERSE; + if (ftdmchan->polarity != FTDM_POLARITY_FORWARD) { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Polarity is already reversed on answer??\n"); + } else { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Reversing polarity on answer\n"); + ftdm_channel_command(ftdmchan, FTDM_COMMAND_SET_POLARITY, &polarity); + } + } + ftdm_span_send_signal(ftdmchan->span, &sig); continue; } @@ -639,6 +704,22 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj) continue; } break; + + case FTDM_CHANNEL_STATE_HANGUP: + /* this state is only used when the user hangup, if the device hang up (onhook) we currently + * go straight to DOWN. If we ever change this (as other signaling modules do) by using this + * state for both user and device hangup, we should check here for the type of hangup since + * some actions (polarity reverse) do not make sense if the device hung up */ + if (ftdmchan->type == FTDM_CHAN_TYPE_FXS && + ftdmchan->last_state == FTDM_CHANNEL_STATE_UP && + ftdm_test_flag(analog_data, FTDM_ANALOG_HANGUP_POLARITY_REVERSE)) { + ftdm_polarity_t polarity = ftdmchan->polarity == FTDM_POLARITY_REVERSE + ? FTDM_POLARITY_FORWARD : FTDM_POLARITY_REVERSE; + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Reversing polarity on hangup\n"); + ftdm_channel_command(ftdmchan, FTDM_COMMAND_SET_POLARITY, &polarity); + } + break; + case FTDM_CHANNEL_STATE_DOWN: { sig.event_id = FTDM_SIGEVENT_STOP; @@ -847,6 +928,9 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj) done: + closed_chan = ftdmchan; + + ftdm_channel_lock(closed_chan); if (ftdmchan->type == FTDM_CHAN_TYPE_FXO && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OFFHOOK)) { ftdm_channel_command(ftdmchan, FTDM_COMMAND_ONHOOK, NULL); @@ -857,7 +941,8 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj) } - closed_chan = ftdmchan; + ftdm_clear_sflag(ftdmchan, AF_POLARITY_REVERSE); + ftdm_channel_close(&ftdmchan); ftdm_channel_command(closed_chan, FTDM_COMMAND_SET_NATIVE_CODEC, NULL); @@ -875,8 +960,11 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj) } ftdm_log_chan(closed_chan, FTDM_LOG_DEBUG, "ANALOG CHANNEL %d:%d thread ended.\n", closed_chan->span_id, closed_chan->chan_id); + ftdm_clear_flag(closed_chan, FTDM_CHANNEL_INTHREAD); + ftdm_channel_unlock(closed_chan); + return NULL; } @@ -903,6 +991,19 @@ static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *e ftdm_mutex_lock(event->channel->mutex); locked++; + /* MAINTENANCE WARNING: + * 1. Be aware you are working on the locked channel + * 2. We should not be calling ftdm_span_send_signal or ftdm_set_state when there is already a channel thread running + * however, since this is old code I am not changing it now, but new code should adhere to that convention + * otherwise, we have possible races where we compete with the user for state changes, ie, the user requests + * a state change and then we process an event, the state change from the user is pending so our ftdm_set_state + * operation will fail. In cases where we win the race, our state change will be accepted but if a user requests + * a state change before the state change we requested here is processed by the channel thread, we'll end up + * rejecting the user request. + * + * See docs/locking.txt for further information about what guarantees should signaling modules provide when + * locking/unlocking a channel + * */ switch(event->enum_id) { case FTDM_OOB_RING_START: { @@ -940,7 +1041,11 @@ static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *e } ftdm_set_state(event->channel, FTDM_CHANNEL_STATE_DOWN); } - + if (event->channel->type == FTDM_CHAN_TYPE_FXS) { + /* we always return to forward when the device goes onhook */ + ftdm_polarity_t forward_polarity = FTDM_POLARITY_FORWARD; + ftdm_channel_command(event->channel, FTDM_COMMAND_SET_POLARITY, &forward_polarity); + } } break; case FTDM_OOB_FLASH: @@ -1004,6 +1109,35 @@ static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *e ftdm_span_send_signal(span, &sig); } break; + case FTDM_OOB_POLARITY_REVERSE: + { + if (event->channel->type != FTDM_CHAN_TYPE_FXO) { + ftdm_log_chan_msg(event->channel, FTDM_LOG_WARNING, + "Ignoring polarity reversal, this should not happen in non-FXO channels!\n"); + break; + } + if (!ftdm_test_flag(event->channel, FTDM_CHANNEL_INTHREAD) && + ftdm_test_flag(event->channel, FTDM_CHANNEL_OFFHOOK)) { + ftdm_log_chan_msg(event->channel, FTDM_LOG_WARNING, + "Forcing onhook in channel not in thread after polarity reversal\n"); + ftdm_channel_command(event->channel, FTDM_COMMAND_ONHOOK, NULL); + break; + } + if (!ftdm_test_flag(analog_data, FTDM_ANALOG_ANSWER_POLARITY_REVERSE) + && !ftdm_test_flag(analog_data, FTDM_ANALOG_HANGUP_POLARITY_REVERSE)) { + ftdm_log_chan_msg(event->channel, FTDM_LOG_DEBUG, + "Ignoring polarity reversal because this channel is not configured for it\n"); + break; + } + if (event->channel->state == FTDM_CHANNEL_STATE_DOWN) { + ftdm_log_chan_msg(event->channel, FTDM_LOG_DEBUG, + "Ignoring polarity reversal because this channel is down\n"); + break; + } + /* we have a good channel, set the polarity flag and let the channel thread deal with it */ + ftdm_set_sflag(event->channel, AF_POLARITY_REVERSE); + } + break; default: { ftdm_log_chan(event->channel, FTDM_LOG_DEBUG, "Ignoring event [%s] in state [%s]\n", ftdm_oob_event2str(event->enum_id), ftdm_channel_state2str(event->channel->state)); diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.2008.vcproj b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.2008.vcproj index e3930d8188..2e7fb82041 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.2008.vcproj +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.2008.vcproj @@ -40,11 +40,12 @@ Disabled - C:\Program Files\libsng_isdn\include;C:\Program Files\libsng_isdn\include\sng_isdn;../../include;C:\Program Files\Sangoma\include;%(AdditionalIncludeDirectories) + C:\Program Files\sangoma\sng_isdn\include;../../include;C:\Program Files\Sangoma\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true @@ -95,10 +95,11 @@ Level3 EditAndContinue + MultiThreadedDebugDLL freetdm.lib;libsng_isdn.lib;%(AdditionalDependencies) - $(OutDir);C:\Program Files\libsng_isdn\lib;C:\Program Files\Sangoma\api\lib\x86;%(AdditionalLibraryDirectories) + $(OutDir);C:\Program Files\sangoma\sng_isdn\lib;C:\Program Files\Sangoma\api\lib\x86;%(AdditionalLibraryDirectories) true Console false @@ -110,7 +111,7 @@ Disabled - C:\Program Files\libsng_isdn\include;C:\Program Files\libsng_isdn\include\sng_isdn;../../include;C:\Program Files\Sangoma\include;%(AdditionalIncludeDirectories) + C:\Program Files\sangoma\sng_isdn\include;../../include;C:\Program Files\Sangoma\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) @@ -119,6 +120,7 @@ Level3 ProgramDatabase + MultiThreadedDebugDLL freetdm.lib;libsng_isdn.lib;%(AdditionalDependencies) @@ -135,11 +137,13 @@ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true EnableFastChecks - MultiThreadedDebugDLL + MultiThreadedDLL Level3 ProgramDatabase + C:\Program Files\sangoma\sng_isdn\include;../../include;C:\Program Files\Sangoma\include;%(AdditionalIncludeDirectories) + Disabled true @@ -147,23 +151,27 @@ true true MachineX86 + freetdm.lib;libsng_isdn.lib;%(AdditionalDependencies) + $(OutDir);C:\Program Files\sangoma\sng_isdn\lib;C:\Program Files\Sangoma\api\lib\x86;%(AdditionalLibraryDirectories) WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) EnableFastChecks - MultiThreadedDebugDLL + MultiThreadedDLL Level3 ProgramDatabase + C:\Program Files\sangoma\sng_isdn\include;../../include;C:\Program Files\Sangoma\include;%(AdditionalIncludeDirectories) true Windows true true + freetdm.lib;libsng_isdn.lib;%(AdditionalDependencies) diff --git a/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.2010.vcxproj b/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.2010.vcxproj index b165ac82d3..40d0a73a5b 100644 --- a/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.2010.vcxproj +++ b/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.2010.vcxproj @@ -100,7 +100,7 @@ EditAndContinue - freetdm.lib;libsangoma.lib;%(AdditionalDependencies) + libsangoma.lib;%(AdditionalDependencies) $(OutDir);C:\Program Files\Sangoma\api\lib\x86;%(AdditionalLibraryDirectories) true Console @@ -127,7 +127,7 @@ ProgramDatabase - freetdm.lib;libsangoma.lib;%(AdditionalDependencies) + libsangoma.lib;%(AdditionalDependencies) $(OutDir);C:\Program Files\Sangoma\api\lib\x64;%(AdditionalLibraryDirectories) true Console @@ -148,7 +148,7 @@ ProgramDatabase - freetdm.lib;libsangoma.lib;%(AdditionalDependencies) + libsangoma.lib;%(AdditionalDependencies) $(OutDir);C:\Program Files\Sangoma\api\lib\x86;%(AdditionalLibraryDirectories) true Console @@ -174,7 +174,7 @@ ProgramDatabase - freetdm.lib;libsangoma.lib;%(AdditionalDependencies) + libsangoma.lib;%(AdditionalDependencies) $(OutDir);C:\Program Files\Sangoma\api\lib\x64;%(AdditionalLibraryDirectories) true Console diff --git a/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c b/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c index 2db4326847..5e9318f7e5 100644 --- a/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c +++ b/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c @@ -784,17 +784,27 @@ static FIO_COMMAND_FUNCTION(wanpipe_command) err = sangoma_set_tx_queue_sz(ftdmchan->sockfd, &tdm_api, queue_size); } break; + case FTDM_COMMAND_SET_POLARITY: + { + ftdm_polarity_t polarity = FTDM_COMMAND_OBJ_INT; + err = sangoma_tdm_set_polarity(ftdmchan->sockfd, &tdm_api, polarity); + if (!err) { + ftdmchan->polarity = polarity; + } + } + break; default: err = FTDM_NOTIMPL; break; }; if (err) { - snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", strerror(errno)); + int myerrno = errno; + ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Wanpipe failed to execute command %d: %s\n", command, strerror(myerrno)); + errno = myerrno; return err; } - return FTDM_SUCCESS; } @@ -1237,8 +1247,9 @@ FIO_CHANNEL_NEXT_EVENT_FUNCTION(wanpipe_channel_next_event) wanpipe_tdm_api_t tdm_api; ftdm_span_t *span = ftdmchan->span; - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_EVENT)) + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_EVENT)) { ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_EVENT); + } memset(&tdm_api, 0, sizeof(tdm_api)); status = sangoma_tdm_read_event(ftdmchan->sockfd, &tdm_api); @@ -1251,7 +1262,7 @@ FIO_CHANNEL_NEXT_EVENT_FUNCTION(wanpipe_channel_next_event) ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "read wanpipe event %d\n", tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type); switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type) { - case WP_TDMAPI_EVENT_LINK_STATUS: + case WP_API_EVENT_LINK_STATUS: { switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_link_status) { case WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED: @@ -1264,7 +1275,7 @@ FIO_CHANNEL_NEXT_EVENT_FUNCTION(wanpipe_channel_next_event) } break; - case WP_TDMAPI_EVENT_RXHOOK: + case WP_API_EVENT_RXHOOK: { if (ftdmchan->type == FTDM_CHAN_TYPE_FXS) { event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_hook_state & WP_TDMAPI_EVENT_RXHOOK_OFF ? FTDM_OOB_OFFHOOK : FTDM_OOB_ONHOOK; @@ -1300,26 +1311,26 @@ FIO_CHANNEL_NEXT_EVENT_FUNCTION(wanpipe_channel_next_event) } } break; - case WP_TDMAPI_EVENT_RING_DETECT: + case WP_API_EVENT_RING_DETECT: { event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state == WP_TDMAPI_EVENT_RING_PRESENT ? FTDM_OOB_RING_START : FTDM_OOB_RING_STOP; } break; /* disabled this ones when configuring, we don't need them, do we? - case WP_TDMAPI_EVENT_RING_TRIP_DETECT: + case WP_API_EVENT_RING_TRIP_DETECT: { event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state == WP_TDMAPI_EVENT_RING_PRESENT ? FTDM_OOB_ONHOOK : FTDM_OOB_OFFHOOK; } break; */ - case WP_TDMAPI_EVENT_RBS: + case WP_API_EVENT_RBS: { event_id = FTDM_OOB_CAS_BITS_CHANGE; ftdmchan->rx_cas_bits = wanpipe_swap_bits(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_rbs_bits); } break; - case WP_TDMAPI_EVENT_DTMF: + case WP_API_EVENT_DTMF: { char tmp_dtmf[2] = { tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_dtmf_digit, 0 }; event_id = FTDM_OOB_NOOP; @@ -1342,12 +1353,18 @@ FIO_CHANNEL_NEXT_EVENT_FUNCTION(wanpipe_channel_next_event) } } break; - case WP_TDMAPI_EVENT_ALARM: + case WP_API_EVENT_ALARM: { ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Got wanpipe alarms %d\n", tdm_api.wp_tdm_cmd.event.wp_api_event_alarm); event_id = FTDM_OOB_ALARM_TRAP; } break; + case WP_API_EVENT_POLARITY_REVERSE: + { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Got polarity reverse\n"); + event_id = FTDM_OOB_POLARITY_REVERSE; + } + break; default: { ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Unhandled wanpipe event %d\n", tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type); @@ -1423,7 +1440,7 @@ FIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_span_next_event) ftdm_log_chan(span->channels[i], FTDM_LOG_DEBUG, "read wanpipe event %d\n", tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type); switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type) { - case WP_TDMAPI_EVENT_LINK_STATUS: + case WP_API_EVENT_LINK_STATUS: { switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_link_status) { case WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED: @@ -1436,7 +1453,7 @@ FIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_span_next_event) } break; - case WP_TDMAPI_EVENT_RXHOOK: + case WP_API_EVENT_RXHOOK: { if (span->channels[i]->type == FTDM_CHAN_TYPE_FXS) { event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_hook_state & WP_TDMAPI_EVENT_RXHOOK_OFF ? FTDM_OOB_OFFHOOK : FTDM_OOB_ONHOOK; @@ -1472,26 +1489,26 @@ FIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_span_next_event) } } break; - case WP_TDMAPI_EVENT_RING_DETECT: + case WP_API_EVENT_RING_DETECT: { event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state == WP_TDMAPI_EVENT_RING_PRESENT ? FTDM_OOB_RING_START : FTDM_OOB_RING_STOP; } break; /* disabled this ones when configuring, we don't need them, do we? - case WP_TDMAPI_EVENT_RING_TRIP_DETECT: + case WP_API_EVENT_RING_TRIP_DETECT: { event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state == WP_TDMAPI_EVENT_RING_PRESENT ? FTDM_OOB_ONHOOK : FTDM_OOB_OFFHOOK; } break; */ - case WP_TDMAPI_EVENT_RBS: + case WP_API_EVENT_RBS: { event_id = FTDM_OOB_CAS_BITS_CHANGE; span->channels[i]->rx_cas_bits = wanpipe_swap_bits(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_rbs_bits); } break; - case WP_TDMAPI_EVENT_DTMF: + case WP_API_EVENT_DTMF: { char tmp_dtmf[2] = { tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_dtmf_digit, 0 }; event_id = FTDM_OOB_NOOP; @@ -1514,12 +1531,18 @@ FIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_span_next_event) } } break; - case WP_TDMAPI_EVENT_ALARM: + case WP_API_EVENT_ALARM: { ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Got wanpipe alarms %d\n", tdm_api.wp_tdm_cmd.event.wp_api_event_alarm); event_id = FTDM_OOB_ALARM_TRAP; } break; + case WP_API_EVENT_POLARITY_REVERSE: + { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Got polarity reverse\n"); + event_id = FTDM_OOB_POLARITY_REVERSE; + } + break; default: { ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Unhandled wanpipe event %d\n", tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type); diff --git a/libs/freetdm/src/include/freetdm.h b/libs/freetdm/src/include/freetdm.h index 79e873390e..d27353ddb0 100644 --- a/libs/freetdm/src/include/freetdm.h +++ b/libs/freetdm/src/include/freetdm.h @@ -320,6 +320,7 @@ typedef struct ftdm_caller_data { * that the user can use caller_data.call_id to obtain the call_id. The user * should use the call_id from sigmsg otherwise */ uint32_t call_id; /*!< Unique call ID for this call */ + ftdm_channel_t *fchan; /*!< FreeTDM channel associated (can be NULL) */ } ftdm_caller_data_t; /*! \brief Tone type */ @@ -557,8 +558,14 @@ typedef enum { FTDM_COMMAND_COUNT, FTDM_COMMAND_SET_RX_QUEUE_SIZE, FTDM_COMMAND_SET_TX_QUEUE_SIZE, + FTDM_COMMAND_SET_POLARITY, } ftdm_command_t; +typedef enum { + FTDM_POLARITY_FORWARD = 0, + FTDM_POLARITY_REVERSE = 1 +} ftdm_polarity_t; + /*! \brief Custom memory handler hooks. Not recommended to use unless you need memory allocation customizations */ typedef void *(*ftdm_malloc_func_t)(void *pool, ftdm_size_t len); typedef void *(*ftdm_calloc_func_t)(void *pool, ftdm_size_t elements, ftdm_size_t len); diff --git a/libs/freetdm/src/include/private/ftdm_core.h b/libs/freetdm/src/include/private/ftdm_core.h index 606a89fc77..b90e98736a 100644 --- a/libs/freetdm/src/include/private/ftdm_core.h +++ b/libs/freetdm/src/include/private/ftdm_core.h @@ -143,7 +143,9 @@ extern "C" { \return true value if the object has the flags defined */ #define ftdm_test_flag(obj, flag) ((obj)->flags & flag) +/*!< Physical (IO) module specific flags */ #define ftdm_test_pflag(obj, flag) ((obj)->pflags & flag) +/*!< signaling module specific flags */ #define ftdm_test_sflag(obj, flag) ((obj)->sflags & flag) #define ftdm_set_alarm_flag(obj, flag) (obj)->alarm_flags |= (flag) @@ -456,6 +458,7 @@ struct ftdm_channel { ftdm_fsk_data_state_t fsk; uint8_t fsk_buf[80]; uint32_t ring_count; + ftdm_polarity_t polarity; /* Private I/O data. Do not touch unless you are an I/O module */ void *io_data; /* Private signaling data. Do not touch unless you are a signaling module */ diff --git a/libs/freetdm/src/include/private/ftdm_types.h b/libs/freetdm/src/include/private/ftdm_types.h index b7c6d4a6f3..5b9d05e6c6 100644 --- a/libs/freetdm/src/include/private/ftdm_types.h +++ b/libs/freetdm/src/include/private/ftdm_types.h @@ -123,6 +123,7 @@ typedef enum { FTDM_STR2ENUM_P(ftdm_str2ftdm_analog_start_type, ftdm_analog_start_type2str, ftdm_analog_start_type_t) typedef enum { + FTDM_OOB_NOOP, FTDM_OOB_ONHOOK, FTDM_OOB_OFFHOOK, FTDM_OOB_WINK, @@ -131,11 +132,11 @@ typedef enum { FTDM_OOB_RING_STOP, FTDM_OOB_ALARM_TRAP, FTDM_OOB_ALARM_CLEAR, - FTDM_OOB_NOOP, FTDM_OOB_CAS_BITS_CHANGE, + FTDM_OOB_POLARITY_REVERSE, FTDM_OOB_INVALID } ftdm_oob_event_t; -#define OOB_STRINGS "ONHOOK", "OFFHOOK", "WINK", "FLASH", "RING_START", "RING_STOP", "ALARM_TRAP", "ALARM_CLEAR", "NOOP", "CAS_BITS_CHANGE", "INVALID" +#define OOB_STRINGS "NOOP", "ONHOOK", "OFFHOOK", "WINK", "FLASH", "RING_START", "RING_STOP", "ALARM_TRAP", "ALARM_CLEAR", "CAS_BITS_CHANGE", "POLARITY_REVERSE", "INVALID" FTDM_STR2ENUM_P(ftdm_str2ftdm_oob_event, ftdm_oob_event2str, ftdm_oob_event_t) /*! \brief Event types */ diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 53ce82b4cb..cf8ae96da0 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -191,7 +191,8 @@ typedef enum { EFLAG_TRANSFER = (1 << 23), EFLAG_BGDIAL_RESULT = (1 << 24), EFLAG_FLOOR_CHANGE = (1 << 25), - EFLAG_MUTE_DETECT = (1 << 26) + EFLAG_MUTE_DETECT = (1 << 26), + EFLAG_RECORD = (1 << 27) } event_type_t; typedef struct conference_file_node { @@ -270,6 +271,7 @@ typedef struct conference_obj { uint32_t not_talking_buf_len; int comfort_noise_level; int is_recording; + int record_count; int video_running; uint32_t eflags; uint32_t verbose_events; @@ -283,6 +285,7 @@ typedef struct conference_obj { uint32_t avg_score; uint32_t avg_itt; uint32_t avg_tally; + switch_time_t run_time; } conference_obj_t; /* Relationship with another member */ @@ -468,6 +471,7 @@ static switch_status_t conference_add_event_member_data(conference_member_t *mem switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Hear", "%s", switch_test_flag(member, MFLAG_CAN_HEAR) ? "true" : "false" ); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Speak", "%s", switch_test_flag(member, MFLAG_CAN_SPEAK) ? "true" : "false" ); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Talking", "%s", switch_test_flag(member, MFLAG_TALKING) ? "true" : "false" ); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Mute-Detect", "%s", switch_test_flag(member, MFLAG_MUTE_DETECT) ? "true" : "false" ); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", member->id); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-Type", "%s", switch_test_flag(member, MFLAG_MOD) ? "moderator" : "member"); @@ -563,6 +567,7 @@ static switch_status_t conference_record_stop(conference_obj_t *conference, char return count; } + /* Add a custom relationship to a member */ static conference_relationship_t *member_add_relationship(conference_member_t *member, uint32_t id) { @@ -1035,6 +1040,7 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v switch_mutex_unlock(globals.hash_mutex); conference->is_recording = 0; + conference->record_count = 0; while (globals.running && !switch_test_flag(conference, CFLAG_DESTRUCT)) { switch_size_t file_sample_len = samples; @@ -1099,6 +1105,7 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v /* Start recording if there's more than one participant. */ if (conference->auto_record && !conference->is_recording && conference->count > 1) { conference->is_recording = 1; + conference->record_count++; imember = conference->members; if (imember) { switch_channel_t *channel = switch_core_session_get_channel(imember->session); @@ -2623,6 +2630,7 @@ static void *SWITCH_THREAD_FUNC conference_record_thread_run(switch_thread_t *th switch_timer_t timer = { 0 }; uint32_t rlen; switch_size_t data_buf_len; + switch_event_t *event; data_buf_len = samples * sizeof(int16_t); @@ -2701,6 +2709,15 @@ static void *SWITCH_THREAD_FUNC conference_record_thread_run(switch_thread_t *th switch_core_file_set_string(&fh, SWITCH_AUDIO_COL_STR_ARTIST, "FreeSWITCH mod_conference Software Conference Module"); + if (test_eflag(conference, EFLAG_RECORD) && + switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) { + conference_add_event_data(conference, event); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "start-recording"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Path", rec->path); + switch_event_fire(&event); + } + + while (switch_test_flag(member, MFLAG_RUNNING) && switch_test_flag(conference, CFLAG_RUNNING) && conference->count) { switch_size_t len = 0; mux_used = (uint32_t) switch_buffer_inuse(member->mux_buffer); @@ -3759,6 +3776,13 @@ static void conference_xlist(conference_obj_t *conference, switch_xml_t x_confer switch_xml_set_attr_d(x_conference, "dynamic", "true"); } + if (conference->record_count > 0) { + switch_xml_set_attr_d(x_conference, "recording", "true"); + } + + switch_snprintf(i, sizeof(i), "%d", switch_epoch_time_now(NULL) - conference->run_time); + switch_xml_set_attr_d(x_conference, "run_time", ival); + if (conference->agc_level) { char tmp[30] = ""; switch_snprintf(tmp, sizeof(tmp), "%d", conference->agc_level); @@ -3825,6 +3849,9 @@ static void conference_xlist(conference_obj_t *conference, switch_xml_t x_confer x_tag = switch_xml_add_child_d(x_flags, "can_speak", count++); switch_xml_set_txt_d(x_tag, switch_test_flag(member, MFLAG_CAN_SPEAK) ? "true" : "false"); + x_tag = switch_xml_add_child_d(x_flags, "mute_detect", count++); + switch_xml_set_txt_d(x_tag, switch_test_flag(member, MFLAG_MUTE_DETECT) ? "true" : "false"); + x_tag = switch_xml_add_child_d(x_flags, "talking", count++); switch_xml_set_txt_d(x_tag, switch_test_flag(member, MFLAG_TALKING) ? "true" : "false"); @@ -4414,6 +4441,7 @@ static switch_status_t conf_api_sub_record(conference_obj_t *conference, switch_ return SWITCH_STATUS_GENERR; stream->write_function(stream, "Record file %s\n", argv[2]); + conference->record_count++; launch_conference_record_thread(conference, argv[2]); return SWITCH_STATUS_SUCCESS; } @@ -4421,6 +4449,7 @@ static switch_status_t conf_api_sub_record(conference_obj_t *conference, switch_ static switch_status_t conf_api_sub_norecord(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv) { int all; + switch_event_t *event; switch_assert(conference != NULL); switch_assert(stream != NULL); @@ -4432,6 +4461,20 @@ static switch_status_t conf_api_sub_norecord(conference_obj_t *conference, switc stream->write_function(stream, "Stop recording file %s\n", argv[2]); if (!conference_record_stop(conference, all ? NULL : argv[2]) && !all) { stream->write_function(stream, "non-existant recording '%s'\n", argv[2]); + } else { + if (all) { + conference->record_count = 0; + } else { + conference->record_count--; + } + if (test_eflag(conference, EFLAG_RECORD) && + switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) { + conference_add_event_data(conference, event); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "stop-recording"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Path", all ? "all" : argv[2]); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Other-Recordings", conference->record_count ? "true" : "false"); + switch_event_fire(&event); + } } return SWITCH_STATUS_SUCCESS; @@ -5138,6 +5181,8 @@ static void clear_eflags(char *events, uint32_t *f) *f &= ~EFLAG_BGDIAL_RESULT; } else if (!strcmp(event, "floor-change")) { *f &= ~EFLAG_FLOOR_CHANGE; + } else if (!strcmp(event, "record")) { + *f &= ~EFLAG_RECORD; } event = next; @@ -6121,6 +6166,7 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_m conference->caller_id_name = switch_core_strdup(conference->pool, caller_id_name); conference->caller_id_number = switch_core_strdup(conference->pool, caller_id_number); conference->caller_controls = switch_core_strdup(conference->pool, caller_controls); + conference->run_time = switch_epoch_time_now(NULL); if (!zstr(perpetual_sound)) { diff --git a/src/mod/applications/mod_fifo/mod_fifo.c b/src/mod/applications/mod_fifo/mod_fifo.c index 9d9896956e..e9d7cfb0e6 100644 --- a/src/mod/applications/mod_fifo/mod_fifo.c +++ b/src/mod/applications/mod_fifo/mod_fifo.c @@ -1761,27 +1761,28 @@ static void *SWITCH_THREAD_FUNC node_thread_run(switch_thread_t *thread, void *o for (hi = switch_hash_first(NULL, globals.fifo_hash); hi; hi = switch_hash_next(hi)) { switch_hash_this(hi, &var, NULL, &val); if ((node = (fifo_node_t *) val)) { + int x = 0; + switch_event_t *pop; if (node->ready == FIFO_DELAY_DESTROY) { - int doit = 0; - - switch_mutex_lock(node->update_mutex); - doit = node->consumer_count == 0 && node_caller_count(node) == 0; - switch_mutex_unlock(node->update_mutex); - - if (doit) { + if (switch_thread_rwlock_trywrlock(node->rwlock) == SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "%s removed.\n", node->name); switch_core_hash_delete(globals.fifo_hash, node->name); + + for (x = 0; x < MAX_PRI; x++) { + while (fifo_queue_pop(node->fifo_list[x], &pop, 2) == SWITCH_STATUS_SUCCESS) { + switch_event_destroy(&pop); + } + } node->ready = 0; - switch_mutex_lock(node->mutex); switch_core_hash_destroy(&node->consumer_hash); switch_mutex_unlock(node->mutex); switch_mutex_unlock(node->update_mutex); + switch_thread_rwlock_unlock(node->rwlock); switch_core_destroy_memory_pool(&node->pool); goto restart; } - } @@ -2172,7 +2173,7 @@ SWITCH_STANDARD_APP(fifo_function) char *mydata = NULL, *argv[5] = { 0 }; fifo_node_t *node = NULL, *node_list[MAX_NODES_PER_CONSUMER + 1] = { 0 }; switch_channel_t *channel = switch_core_session_get_channel(session); - int do_wait = 1, node_count = 0, i = 0; + int do_destroy = 0, do_wait = 1, node_count = 0, i = 0; const char *moh = NULL; const char *announce = NULL; switch_event_t *event = NULL; @@ -2247,13 +2248,10 @@ SWITCH_STANDARD_APP(fifo_function) if (!(node = switch_core_hash_find(globals.fifo_hash, nlist[i]))) { node = create_node(nlist[i], importance, globals.sql_mutex); node->ready = 1; - switch_thread_rwlock_rdlock(node->rwlock); } - node_list[node_count++] = node; - } - if (switch_true(switch_channel_get_variable(channel, "fifo_destroy_after_use")) && node->ready == 1) { - node->ready = FIFO_DELAY_DESTROY; + switch_thread_rwlock_rdlock(node->rwlock); + node_list[node_count++] = node; } switch_mutex_unlock(globals.mutex); @@ -3103,10 +3101,22 @@ SWITCH_STANDARD_APP(fifo_function) done: - if (node) { - switch_thread_rwlock_unlock(node->rwlock); + if (switch_true(switch_channel_get_variable(channel, "fifo_destroy_after_use"))) { + do_destroy = 1; } + switch_mutex_lock(globals.mutex); + for (i = 0; i < node_count; i++) { + if (!(node = node_list[i])) { + continue; + } + switch_thread_rwlock_unlock(node->rwlock); + if (node->ready == 1 && do_destroy) { + node->ready = FIFO_DELAY_DESTROY; + } + } + switch_mutex_unlock(globals.mutex); + switch_channel_clear_app_flag_key(FIFO_APP_KEY, channel, FIFO_APP_BRIDGE_TAG); switch_core_media_bug_resume(session); diff --git a/src/mod/endpoints/mod_sofia/sofia_presence.c b/src/mod/endpoints/mod_sofia/sofia_presence.c index f0cade3e61..4b93492eae 100644 --- a/src/mod/endpoints/mod_sofia/sofia_presence.c +++ b/src/mod/endpoints/mod_sofia/sofia_presence.c @@ -55,6 +55,7 @@ static void sync_sla(sofia_profile_t *profile, const char *to_user, const char * struct dialog_helper { char status[512]; char rpid[512]; + char presence_id[1024]; }; struct resub_helper { @@ -479,9 +480,10 @@ static int sofia_presence_dialog_callback(void *pArg, int argc, char **argv, cha { struct dialog_helper *helper = (struct dialog_helper *) pArg; - if (argc == 2) { + if (argc == 3) { switch_set_string(helper->status, argv[0]); switch_set_string(helper->rpid, argv[1]); + switch_set_string(helper->presence_id, argv[2]); } return -1; @@ -638,7 +640,8 @@ static void actual_sofia_presence_event_handler(switch_event_t *event) } if (probe_euser && probe_host && (profile = sofia_glue_find_profile(probe_host))) { - sql = switch_mprintf("select status,rpid from sip_dialogs where ((sip_from_user='%q' and sip_from_host='%q') or presence_id='%q@%q')", + sql = switch_mprintf("select status,rpid,presence_id from sip_dialogs " + "where ((sip_from_user='%q' and sip_from_host='%q') or presence_id='%q@%q')", probe_euser, probe_host, probe_euser, probe_host); sofia_glue_execute_sql_callback(profile, profile->ireg_mutex, sql, sofia_presence_dialog_callback, &dh); @@ -656,16 +659,16 @@ static void actual_sofia_presence_event_handler(switch_event_t *event) "sip_dialogs.direction, " "sip_dialogs.sip_to_user, " "sip_dialogs.sip_to_host, " + "sip_presence.status," "sip_presence.rpid," "sip_dialogs.presence_id, " "sip_presence.open_closed," "'%q','%q' " + "from sip_registrations " - "from sip_registrations left join sip_dialogs on " + "left join sip_dialogs on " "sip_dialogs.presence_id = sip_registrations.sip_user || '@' || sip_registrations.sip_host " - - "or (sip_dialogs.sip_from_user = sip_registrations.sip_user " "and sip_dialogs.sip_from_host = sip_registrations.sip_host) " @@ -813,7 +816,7 @@ static void actual_sofia_presence_event_handler(switch_event_t *event) sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE); } - sql = switch_mprintf("select status,rpid from sip_dialogs where ((sip_from_user='%q' and sip_from_host='%q') or presence_id='%q@%q')", + sql = switch_mprintf("select status,rpid,presence_id from sip_dialogs where ((sip_from_user='%q' and sip_from_host='%q') or presence_id='%q@%q')", euser, host, euser, host); sofia_glue_execute_sql_callback(profile, profile->ireg_mutex, sql, sofia_presence_dialog_callback, &dh); switch_safe_free(sql); @@ -824,25 +827,19 @@ static void actual_sofia_presence_event_handler(switch_event_t *event) "sip_subscriptions.full_via,sip_subscriptions.expires,sip_subscriptions.user_agent," "sip_subscriptions.accept,sip_subscriptions.profile_name" ",'%q','%q','%q',sip_presence.status,sip_presence.rpid,sip_presence.open_closed,'%q','%q'," - "sip_subscriptions.version, sip_dialogs.presence_id " + "sip_subscriptions.version, '%q' " "from sip_subscriptions " "left join sip_presence on " "(sip_subscriptions.sub_to_user=sip_presence.sip_user and sip_subscriptions.sub_to_host=sip_presence.sip_host and " "sip_subscriptions.profile_name=sip_presence.profile_name) " - "left join sip_dialogs on " - - "sip_dialogs.presence_id = sip_subscriptions.sub_to_user || '@' || sip_subscriptions.sub_to_host or " - - - "(sip_dialogs.sip_from_user = sip_subscriptions.sub_to_user " - "and sip_dialogs.sip_from_host = sip_subscriptions.sub_to_host) " "where sip_subscriptions.expires > -1 and " "(event='%q' or event='%q') and sub_to_user='%q' " "and (sub_to_host='%q' or presence_hosts like '%%%q%%') " - "and (sip_subscriptions.profile_name = '%q' or sip_subscriptions.presence_hosts != sip_subscriptions.sub_to_host)", - switch_str_nil(status), switch_str_nil(rpid), host, - dh.status,dh.rpid, + "and (sip_subscriptions.profile_name = '%q' or sip_subscriptions.presence_hosts != sip_subscriptions.sub_to_host) ", + + switch_str_nil(status), switch_str_nil(rpid), host, + dh.status,dh.rpid,dh.presence_id, event_type, alt_event_type, euser, host, host, profile->name))) { struct presence_helper helper = { 0 }; diff --git a/src/switch_ivr_bridge.c b/src/switch_ivr_bridge.c index 2f2dcdbc95..aa5ddb9232 100644 --- a/src/switch_ivr_bridge.c +++ b/src/switch_ivr_bridge.c @@ -1052,8 +1052,6 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_signal_bridge(switch_core_session_t * switch_channel_set_variable(caller_channel, "signal_bridge", "true"); switch_channel_set_variable(peer_channel, "signal_bridge", "true"); - switch_channel_sort_cid(peer_channel, SWITCH_FALSE); - /* fire events that will change the data table from "show channels" */ if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_EXECUTE) == SWITCH_STATUS_SUCCESS) { switch_channel_event_set_data(caller_channel, event); @@ -1119,8 +1117,6 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_multi_threaded_bridge(switch_core_ses switch_channel_set_flag_recursive(caller_channel, CF_BRIDGE_ORIGINATOR); switch_channel_clear_flag(peer_channel, CF_BRIDGE_ORIGINATOR); - switch_channel_sort_cid(peer_channel, SWITCH_FALSE); - b_leg->session = peer_session; switch_copy_string(b_leg->b_uuid, switch_core_session_get_uuid(session), sizeof(b_leg->b_uuid)); b_leg->stream_id = stream_id; diff --git a/src/switch_ivr_originate.c b/src/switch_ivr_originate.c index 87a47e65aa..51032dec60 100644 --- a/src/switch_ivr_originate.c +++ b/src/switch_ivr_originate.c @@ -2410,6 +2410,9 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess } } + new_profile->callee_id_name = switch_core_strdup(new_profile->pool, "Outbound Call"); + new_profile->callee_id_number = switch_core_strdup(new_profile->pool, new_profile->destination_number); + originate_status[i].caller_profile = NULL; originate_status[i].peer_channel = NULL; originate_status[i].peer_session = NULL; diff --git a/w32/Library/FreeSwitchCore.2010.vcxproj b/w32/Library/FreeSwitchCore.2010.vcxproj index e218445a02..c74ad944b1 100644 --- a/w32/Library/FreeSwitchCore.2010.vcxproj +++ b/w32/Library/FreeSwitchCore.2010.vcxproj @@ -120,8 +120,7 @@ false - - + $(OutDir)FreeSwitchCore.lib MachineX86