FT_DECLARE(char *) ftdm_channel_get_history_str(const ftdm_channel_t *fchan) { uint8_t i = 0; ftdm_time_t currtime = 0; ftdm_time_t prevtime = 0; ftdm_stream_handle_t stream = { 0 }; FTDM_STANDARD_STREAM(stream); if (!fchan->history[0].file) { stream.write_function(&stream, "-- No state history --\n"); return stream.data; } stream.write_function(&stream, "%-30.30s %-30.30s %-30.30s %s", "-- States --", "-- Function --", "-- Location --", "-- Time Offset --\n"); for (i = fchan->hindex; i < ftdm_array_len(fchan->history); i++) { if (!fchan->history[i].file) { break; } write_history_entry(fchan, &stream, i, &prevtime); } for (i = 0; i < fchan->hindex; i++) { write_history_entry(fchan, &stream, i, &prevtime); } currtime = ftdm_current_time_in_ms(); stream.write_function(&stream, "\nTime since last state change: %lums\n", (currtime - prevtime)); return stream.data; }
/** * \brief Executes an FreeTDM command on a Wanpipe channel * \param ftdmchan Channel to execute command on * \param command FreeTDM command to execute * \param obj Object (unused) * \return Success or failure */ static FIO_COMMAND_FUNCTION(wanpipe_command) { wanpipe_tdm_api_t tdm_api; int err = 0; memset(&tdm_api, 0, sizeof(tdm_api)); switch(command) { case FTDM_COMMAND_OFFHOOK: { err=sangoma_tdm_txsig_offhook(ftdmchan->sockfd,&tdm_api); if (err) { snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "OFFHOOK Failed"); return FTDM_FAIL; } ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_OFFHOOK); } break; case FTDM_COMMAND_ONHOOK: { err=sangoma_tdm_txsig_onhook(ftdmchan->sockfd,&tdm_api); if (err) { snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "ONHOOK Failed"); return FTDM_FAIL; } ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_OFFHOOK); } break; case FTDM_COMMAND_GENERATE_RING_ON: { err=sangoma_tdm_txsig_start(ftdmchan->sockfd,&tdm_api); if (err) { snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Ring Failed"); return FTDM_FAIL; } ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_RINGING); ftdm_set_pflag_locked(ftdmchan, WP_RINGING); ftdmchan->ring_time = ftdm_current_time_in_ms() + wp_globals.ring_on_ms; } break; case FTDM_COMMAND_GENERATE_RING_OFF: { err=sangoma_tdm_txsig_offhook(ftdmchan->sockfd,&tdm_api); if (err) { snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Ring-off Failed"); return FTDM_FAIL; } ftdm_clear_pflag_locked(ftdmchan, WP_RINGING); ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_RINGING); } break; case FTDM_COMMAND_GET_INTERVAL: { err=sangoma_tdm_get_usr_period(ftdmchan->sockfd, &tdm_api); if (err > 0 ) { FTDM_COMMAND_OBJ_INT = err; err=0; } } break; case FTDM_COMMAND_ENABLE_ECHOCANCEL: { err=sangoma_tdm_enable_hwec(ftdmchan->sockfd, &tdm_api); if (err) { snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "HWEC Enable Failed"); return FTDM_FAIL; } } break; case FTDM_COMMAND_DISABLE_ECHOCANCEL: { err=sangoma_tdm_disable_hwec(ftdmchan->sockfd, &tdm_api); if (err) { snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "HWEC Disable Failed"); return FTDM_FAIL; } } break; case FTDM_COMMAND_ENABLE_DTMF_DETECT: { #ifdef WP_API_FEATURE_DTMF_EVENTS err = sangoma_tdm_enable_dtmf_events(ftdmchan->sockfd, &tdm_api); if (err) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Enabling of Sangoma HW DTMF failed\n"); snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "HW DTMF Enable Failed"); return FTDM_FAIL; } ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Enabled DTMF events\n"); #else return FTDM_NOTIMPL; #endif } break; case FTDM_COMMAND_DISABLE_DTMF_DETECT: { #ifdef WP_API_FEATURE_DTMF_EVENTS err = sangoma_tdm_disable_dtmf_events(ftdmchan->sockfd, &tdm_api); if (err) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Disabling of Sangoma HW DTMF failed\n"); snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "HW DTMF Disable Failed"); return FTDM_FAIL; } ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Disabled DTMF events\n"); #else return FTDM_NOTIMPL; #endif } break; case FTDM_COMMAND_ENABLE_LOOP: { #ifdef WP_API_FEATURE_LOOP err=sangoma_tdm_enable_loop(ftdmchan->sockfd, &tdm_api); if (err) { snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Loop Enable Failed"); return FTDM_FAIL; } #endif } break; case FTDM_COMMAND_DISABLE_LOOP: { #ifdef WP_API_FEATURE_LOOP err=sangoma_tdm_disable_loop(ftdmchan->sockfd, &tdm_api); if (err) { snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Loop Disable Failed"); return FTDM_FAIL; } #endif } break; case FTDM_COMMAND_SET_INTERVAL: { err=sangoma_tdm_set_usr_period(ftdmchan->sockfd, &tdm_api, FTDM_COMMAND_OBJ_INT); ftdmchan->packet_len = ftdmchan->native_interval * (ftdmchan->effective_codec == FTDM_CODEC_SLIN ? 16 : 8); } break; case FTDM_COMMAND_SET_CAS_BITS: { #ifdef LIBSANGOMA_VERSION err = sangoma_tdm_write_rbs(ftdmchan->sockfd,&tdm_api, ftdmchan->physical_chan_id, wanpipe_swap_bits(FTDM_COMMAND_OBJ_INT)); #else err = sangoma_tdm_write_rbs(ftdmchan->sockfd, &tdm_api, wanpipe_swap_bits(FTDM_COMMAND_OBJ_INT)); #endif } break; case FTDM_COMMAND_GET_CAS_BITS: { #ifdef LIBSANGOMA_VERSION unsigned char rbsbits; err = sangoma_tdm_read_rbs(ftdmchan->sockfd, &tdm_api, ftdmchan->physical_chan_id, &rbsbits); if (!err) { FTDM_COMMAND_OBJ_INT = wanpipe_swap_bits(rbsbits); } #else // does sangoma_tdm_read_rbs is available here? FTDM_COMMAND_OBJ_INT = ftdmchan->rx_cas_bits; #endif } break; case FTDM_COMMAND_SET_LINK_STATUS: { ftdm_channel_hw_link_status_t status = FTDM_COMMAND_OBJ_INT; char sangoma_status = status == FTDM_HW_LINK_CONNECTED ? FE_CONNECTED : FE_DISCONNECTED; err = sangoma_tdm_set_fe_status(ftdmchan->sockfd, &tdm_api, sangoma_status); } break; case FTDM_COMMAND_GET_LINK_STATUS: { unsigned char sangoma_status = 0; err = sangoma_tdm_get_fe_status(ftdmchan->sockfd, &tdm_api, &sangoma_status); if (!err) { FTDM_COMMAND_OBJ_INT = sangoma_status == FE_CONNECTED ? FTDM_HW_LINK_CONNECTED : FTDM_HW_LINK_DISCONNECTED; } } break; default: break; }; if (err) { snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", strerror(errno)); return FTDM_FAIL; } return FTDM_SUCCESS; }
static ftdm_status_t ftdm_core_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq) { ftdm_status_t status; int ok = 1; int waitms = DEFAULT_WAIT_TIME; if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_READY)) { ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, "Ignored state change request from %s to %s, the channel is not ready\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); return FTDM_FAIL; } if (ftdmchan->state_status != FTDM_STATE_STATUS_COMPLETED) { ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, "Ignored state change request from %s to %s, the previous state change has not been processed yet (status = %s)\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state), ftdm_state_status2str(ftdmchan->state_status)); return FTDM_FAIL; } if (ftdmchan->state == state) { ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "Why bother changing state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); return FTDM_FAIL; } if (!ftdmchan->state_completed_interrupt) { status = ftdm_interrupt_create(&ftdmchan->state_completed_interrupt, FTDM_INVALID_SOCKET); if (status != FTDM_SUCCESS) { ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_CRIT, "Failed to create state change interrupt when moving from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); return status; } } if (ftdmchan->span->state_map) { ok = ftdm_parse_state_map(ftdmchan, state, ftdmchan->span->state_map); goto end; } /* basic core state validation (by-passed if the signaling module provides a state_map) */ switch(ftdmchan->state) { case FTDM_CHANNEL_STATE_HANGUP: case FTDM_CHANNEL_STATE_TERMINATING: { ok = 0; switch(state) { case FTDM_CHANNEL_STATE_DOWN: case FTDM_CHANNEL_STATE_BUSY: case FTDM_CHANNEL_STATE_RESTART: ok = 1; break; default: break; } } break; case FTDM_CHANNEL_STATE_UP: { ok = 1; switch(state) { case FTDM_CHANNEL_STATE_PROGRESS: case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: case FTDM_CHANNEL_STATE_RING: ok = 0; break; default: break; } } break; case FTDM_CHANNEL_STATE_DOWN: { ok = 0; switch(state) { case FTDM_CHANNEL_STATE_DIALTONE: case FTDM_CHANNEL_STATE_COLLECT: case FTDM_CHANNEL_STATE_DIALING: case FTDM_CHANNEL_STATE_RING: case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: case FTDM_CHANNEL_STATE_PROGRESS: case FTDM_CHANNEL_STATE_IDLE: case FTDM_CHANNEL_STATE_GET_CALLERID: case FTDM_CHANNEL_STATE_GENRING: ok = 1; break; default: break; } } break; case FTDM_CHANNEL_STATE_BUSY: { switch(state) { case FTDM_CHANNEL_STATE_UP: ok = 0; break; default: break; } } break; case FTDM_CHANNEL_STATE_RING: { switch(state) { case FTDM_CHANNEL_STATE_UP: ok = 1; break; default: break; } } break; default: break; } end: if (!ok) { 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; } ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Changed state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); ftdmchan->last_state = ftdmchan->state; ftdmchan->state = state; ftdmchan->state_status = FTDM_STATE_STATUS_NEW; ftdmchan->history[ftdmchan->hindex].file = file; ftdmchan->history[ftdmchan->hindex].func = func; ftdmchan->history[ftdmchan->hindex].line = line; ftdmchan->history[ftdmchan->hindex].state = ftdmchan->state; ftdmchan->history[ftdmchan->hindex].last_state = ftdmchan->last_state; ftdmchan->history[ftdmchan->hindex].time = ftdm_current_time_in_ms(); ftdmchan->history[ftdmchan->hindex].end_time = 0; ftdmchan->hindex++; if (ftdmchan->hindex == ftdm_array_len(ftdmchan->history)) { ftdmchan->hindex = 0; } ftdm_set_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); ftdm_mutex_lock(ftdmchan->span->mutex); ftdm_set_flag(ftdmchan->span, FTDM_SPAN_STATE_CHANGE); if (ftdmchan->span->pendingchans) { ftdm_queue_enqueue(ftdmchan->span->pendingchans, ftdmchan); } ftdm_mutex_unlock(ftdmchan->span->mutex); if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NONBLOCK)) { /* the channel should not block waiting for state processing */ goto done; } if (!waitrq) { /* no waiting was requested */ goto done; } /* let's wait for the state change to be completed by the signaling stack */ ftdm_set_flag(ftdmchan, FTDM_CHANNEL_BLOCKING); ftdm_mutex_unlock(ftdmchan->mutex); status = ftdm_interrupt_wait(ftdmchan->state_completed_interrupt, waitms); ftdm_mutex_lock(ftdmchan->mutex); if (status != FTDM_SUCCESS) { ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_BLOCKING); ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not completed after aprox %dms\n", ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(state), DEFAULT_WAIT_TIME); ok = 0; goto done; } done: return ok ? FTDM_SUCCESS : FTDM_FAIL; }
FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const char *func, int line, ftdm_channel_t *fchan) { uint8_t hindex = 0; ftdm_time_t diff = 0; ftdm_channel_state_t state = fchan->state; if (fchan->state_status == FTDM_STATE_STATUS_COMPLETED) { ftdm_assert_return(!ftdm_test_flag(fchan, FTDM_CHANNEL_STATE_CHANGE), FTDM_FAIL, "State change flag set but state is not completed\n"); return FTDM_SUCCESS; } ftdm_usrmsg_free(&fchan->usrmsg); ftdm_clear_flag(fchan, FTDM_CHANNEL_STATE_CHANGE); if (state == FTDM_CHANNEL_STATE_PROGRESS) { ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS); } else if (state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA) { ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS); ftdm_test_and_set_media(fchan); } else if (state == FTDM_CHANNEL_STATE_UP) { ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS); ftdm_set_flag(fchan, FTDM_CHANNEL_ANSWERED); ftdm_test_and_set_media(fchan); } else if (state == FTDM_CHANNEL_STATE_DIALING) { ftdm_sigmsg_t msg; memset(&msg, 0, sizeof(msg)); msg.channel = fchan; msg.event_id = FTDM_SIGEVENT_DIALING; ftdm_span_send_signal(fchan->span, &msg); } else if (state == FTDM_CHANNEL_STATE_HANGUP) { ftdm_set_echocancel_call_end(fchan); } /* MAINTENANCE WARNING * we're assuming an indication performed * via state change will involve a single state change */ ftdm_ack_indication(fchan, fchan->indication, FTDM_SUCCESS); hindex = (fchan->hindex == 0) ? (ftdm_array_len(fchan->history) - 1) : (fchan->hindex - 1); ftdm_assert(!fchan->history[hindex].end_time, "End time should be zero!\n"); fchan->history[hindex].end_time = ftdm_current_time_in_ms(); fchan->state_status = FTDM_STATE_STATUS_COMPLETED; diff = fchan->history[hindex].end_time - fchan->history[hindex].time; ftdm_log_chan_ex(fchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Completed state change from %s to %s in %llums\n", ftdm_channel_state2str(fchan->last_state), ftdm_channel_state2str(state), diff); if (ftdm_test_flag(fchan, FTDM_CHANNEL_BLOCKING)) { ftdm_clear_flag(fchan, FTDM_CHANNEL_BLOCKING); ftdm_interrupt_signal(fchan->state_completed_interrupt); } return FTDM_SUCCESS; }
int main(int argc, char *argv[]) { ftdm_span_t *span; ftdm_conf_parameter_t parameters[20]; ftdm_mutex_create(&the_mutex); if (argc < 2) { printf("umm no\n"); exit(1); } ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG); if (ftdm_global_init() != FTDM_SUCCESS) { fprintf(stderr, "Error loading FreeTDM\n"); exit(1); } ftdm_global_configuration(); printf("FreeTDM loaded\n"); if (ftdm_span_find_by_name(argv[1], &span) != FTDM_SUCCESS) { fprintf(stderr, "Error finding FreeTDM span %s\n", argv[1]); goto done; } /* testing non-blocking operation */ //ftdm_span_set_blocking_mode(span, FTDM_FALSE); parameters[0].var = "variant"; parameters[0].val = "br"; parameters[1].var = "max_ani"; parameters[1].val = "4"; parameters[2].var = "max_dnis"; parameters[2].val = "4"; parameters[3].var = "logging"; parameters[3].val = "all"; parameters[4].var = NULL; parameters[4].val = NULL; if (ftdm_configure_span_signaling(span, "r2", on_r2_signal, parameters) == FTDM_SUCCESS) { ftdm_span_start(span); } else { fprintf(stderr, "Error starting R2 span\n"); goto done; } running = 1; signal(SIGINT, stop_test); while(running) { ftdm_sleep(20); if (fchan && indication != FTDM_CHANNEL_INDICATE_NONE) { ftdm_channel_t *lchan = NULL; ftdm_channel_indication_t ind = FTDM_CHANNEL_INDICATE_NONE; ftdm_time_t start, stop, diff; ftdm_mutex_lock(the_mutex); ind = indication; indication = FTDM_CHANNEL_INDICATE_NONE; lchan = fchan; ftdm_mutex_unlock(the_mutex); start = ftdm_current_time_in_ms(); ftdm_channel_call_indicate(lchan, ind); stop = ftdm_current_time_in_ms(); diff = stop - start; ftdm_log(FTDM_LOG_DEBUG, "Setting indication %s took %"FTDM_TIME_FMT" ms\n", ftdm_channel_indication2str(ind), diff); } } done: ftdm_global_destroy(); return 0; }