From 74a01d23bc4661e2a4dfc1eca5df62a735332072 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Mon, 10 Jan 2011 16:49:58 -0500 Subject: [PATCH] freetdm: Added FTDM_SIGEVENT_DIALING Added hunting scheme to support new ftdm_call_place API Make ftdm_channel_call_answer use ftdm_channel_call_indicate with FTDM_CHANNEL_INDICATE_ANSWER Updated doxygen docs in freetdm.h --- libs/freetdm/docs/async.txt | 14 +++ libs/freetdm/mod_freetdm/mod_freetdm.c | 151 ++++++++++++++----------- libs/freetdm/src/ftdm_io.c | 144 +++++++++++++++++------ libs/freetdm/src/ftdm_state.c | 6 + libs/freetdm/src/include/freetdm.h | 107 ++++++++++++++++-- 5 files changed, 313 insertions(+), 109 deletions(-) create mode 100644 libs/freetdm/docs/async.txt diff --git a/libs/freetdm/docs/async.txt b/libs/freetdm/docs/async.txt new file mode 100644 index 0000000000..04069ebebd --- /dev/null +++ b/libs/freetdm/docs/async.txt @@ -0,0 +1,14 @@ +APIs that result in an event when the API returns FTDM_SUCCESS + +ftdm_channel_call_answer() +ftdm_channel_call_indicate() + FTDM_SIGEVENT_INDICATION_COMPLETED + *note that FTDM_SIGEVENT_INDICATION_COMPLETED has associated data to indicate the result of the indication + *note this event is only delivered on non-blocking channels + +ftdm_call_place + FTDM_SIGEVENT_DIALING + +ftdm_channel_call_hangup + FTDM_SIGEVENT_RELEASED + diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c index f50d26c290..7ac1b281a4 100755 --- a/libs/freetdm/mod_freetdm/mod_freetdm.c +++ b/libs/freetdm/mod_freetdm/mod_freetdm.c @@ -1073,6 +1073,64 @@ static const char* channel_get_variable(switch_core_session_t *session, switch_e return NULL; } +typedef struct { + switch_event_t *var_event; + switch_core_session_t *new_session; + private_t *tech_pvt; + switch_caller_profile_t *caller_profile; +} hunt_data_t; + +static ftdm_status_t on_channel_found(ftdm_channel_t *fchan, ftdm_caller_data_t *caller_data) +{ + uint32_t span_id, chan_id; + const char *var; + char *sess_uuid; + char name[128]; + ftdm_status_t status; + hunt_data_t *hdata = caller_data->priv; + switch_channel_t *channel = switch_core_session_get_channel(hdata->new_session); + + if ((var = switch_event_get_header(hdata->var_event, "freetdm_pre_buffer_size"))) { + int tmp = atoi(var); + if (tmp > -1) { + ftdm_channel_command(fchan, FTDM_COMMAND_SET_PRE_BUFFER_SIZE, &tmp); + } + } + + span_id = ftdm_channel_get_span_id(fchan); + chan_id = ftdm_channel_get_id(fchan); + + tech_init(hdata->tech_pvt, hdata->new_session, fchan); + hdata->tech_pvt->caller_profile = hdata->caller_profile; + + snprintf(name, sizeof(name), "FreeTDM/%u:%u/%s", span_id, chan_id, caller_data->dnis.digits); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Connect outbound channel %s\n", name); + switch_channel_set_name(channel, name); + switch_channel_set_variable(channel, "freetdm_span_name", ftdm_channel_get_span_name(fchan)); + switch_channel_set_variable_printf(channel, "freetdm_span_number", "%d", span_id); + switch_channel_set_variable_printf(channel, "freetdm_chan_number", "%d", chan_id); + + switch_channel_set_state(channel, CS_INIT); + sess_uuid = switch_core_session_get_uuid(hdata->new_session); + status = ftdm_channel_add_token(fchan, sess_uuid, ftdm_channel_get_token_count(fchan)); + switch_assert(status == FTDM_SUCCESS); + + if (SPAN_CONFIG[span_id].limit_calls) { + char spanresource[512]; + snprintf(spanresource, sizeof(spanresource), "span_%s_%s", ftdm_channel_get_span_name(fchan), + caller_data->dnis.digits); + ftdm_log(FTDM_LOG_DEBUG, "Adding rate limit resource on channel %d:%d (%s/%s/%d/%d)\n", + span_id, chan_id, FREETDM_LIMIT_REALM, + spanresource, SPAN_CONFIG[span_id].limit_calls, SPAN_CONFIG[span_id].limit_seconds); + if (switch_limit_incr("hash", hdata->new_session, FREETDM_LIMIT_REALM, spanresource, + SPAN_CONFIG[span_id].limit_calls, SPAN_CONFIG[span_id].limit_seconds) != SWITCH_STATUS_SUCCESS) { + return FTDM_BREAK; + } + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Attached session %s to channel %d:%d\n", sess_uuid, span_id, chan_id); + return FTDM_SUCCESS; +} + /* Make sure when you have 2 sessions in the same scope that you pass the appropriate one to the routines that allocate memory or you will have 1 channel with memory allocated from another channel's pool! */ @@ -1081,13 +1139,11 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags, switch_call_cause_t *cancel_cause) { - + hunt_data_t hunt_data; const char *dest = NULL; char *data = NULL; int span_id = -1, group_id = -1, chan_id = 0; - ftdm_channel_t *ftdmchan = NULL; switch_call_cause_t cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; - char name[128]; ftdm_status_t status; int direction = FTDM_TOP_DOWN; ftdm_caller_data_t caller_data = {{ 0 }}; @@ -1097,6 +1153,7 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi int argc = 0; const char *var; const char *dest_num = NULL, *callerid_num = NULL; + ftdm_hunting_scheme_t hunting; if (!outbound_profile) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing caller profile\n"); @@ -1125,7 +1182,7 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi if ((argc = switch_separate_string(data, '/', argv, (sizeof(argv) / sizeof(argv[0])))) < 2) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid dial string\n"); - return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; } if (switch_is_number(argv[0])) { @@ -1309,30 +1366,21 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi ftdm_set_string(caller_data.cid_name, outbound_profile->caller_id_name); ftdm_set_string(caller_data.cid_num.digits, switch_str_nil(outbound_profile->caller_id_number)); + memset(&hunting, 0, sizeof(hunting)); + if (group_id >= 0) { - status = ftdm_channel_open_by_group(group_id, direction, &caller_data, &ftdmchan); + hunting.mode = FTDM_HUNT_GROUP; + hunting.mode_data.group.group_id = group_id; + hunting.mode_data.group.direction = direction; } else if (chan_id) { - status = ftdm_channel_open(span_id, chan_id, &ftdmchan); + hunting.mode = FTDM_HUNT_CHAN; + hunting.mode_data.chan.span_id = span_id; + hunting.mode_data.chan.chan_id = chan_id; } else { - status = ftdm_channel_open_by_span(span_id, direction, &caller_data, &ftdmchan); + hunting.mode = FTDM_HUNT_SPAN; + hunting.mode_data.span.span_id = span_id; + hunting.mode_data.span.direction = direction; } - - if (status != FTDM_SUCCESS) { - if (caller_data.hangup_cause == SWITCH_CAUSE_NONE) { - caller_data.hangup_cause = SWITCH_CAUSE_NORMAL_CIRCUIT_CONGESTION; - } - return caller_data.hangup_cause; - } - - if ((var = switch_event_get_header(var_event, "freetdm_pre_buffer_size"))) { - int tmp = atoi(var); - if (tmp > -1) { - ftdm_channel_command(ftdmchan, FTDM_COMMAND_SET_PRE_BUFFER_SIZE, &tmp); - } - } - - span_id = ftdm_channel_get_span_id(ftdmchan); - chan_id = ftdm_channel_get_id(ftdmchan); for (h = var_event->headers; h; h = h->next) { if (!strncasecmp(h->name, FREETDM_VAR_PREFIX, FREETDM_VAR_PREFIX_LEN)) { @@ -1343,57 +1391,33 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi } } } - + if ((*new_session = switch_core_session_request(freetdm_endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, flags, pool)) != 0) { private_t *tech_pvt; switch_caller_profile_t *caller_profile; switch_channel_t *channel = switch_core_session_get_channel(*new_session); switch_core_session_add_stream(*new_session, NULL); - if ((tech_pvt = (private_t *) switch_core_session_alloc(*new_session, sizeof(private_t))) != 0) { - tech_init(tech_pvt, *new_session, ftdmchan); - } else { + if (!(tech_pvt = (private_t *) switch_core_session_alloc(*new_session, sizeof(private_t)))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Hey where is my memory pool?\n"); switch_core_session_destroy(new_session); cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; goto fail; } - snprintf(name, sizeof(name), "FreeTDM/%u:%u/%s", span_id, chan_id, dest); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Connect outbound channel %s\n", name); - switch_channel_set_name(channel, name); - switch_channel_set_variable(channel, "freetdm_span_name", ftdm_channel_get_span_name(ftdmchan)); - switch_channel_set_variable_printf(channel, "freetdm_span_number", "%d", span_id); - switch_channel_set_variable_printf(channel, "freetdm_chan_number", "%d", chan_id); - ftdm_channel_set_caller_data(ftdmchan, &caller_data); caller_profile = switch_caller_profile_clone(*new_session, outbound_profile); caller_profile->destination_number = switch_core_strdup(caller_profile->pool, switch_str_nil(dest_num)); caller_profile->caller_id_number = switch_core_strdup(caller_profile->pool, switch_str_nil(callerid_num)); switch_channel_set_caller_profile(channel, caller_profile); - tech_pvt->caller_profile = caller_profile; - - - switch_channel_set_state(channel, CS_INIT); - if (ftdm_channel_add_token(ftdmchan, switch_core_session_get_uuid(*new_session), ftdm_channel_get_token_count(ftdmchan)) != FTDM_SUCCESS) { - switch_core_session_destroy(new_session); - cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; - goto fail; - } + hunting.result_cb = on_channel_found; + hunt_data.var_event = var_event; + hunt_data.new_session = *new_session; + hunt_data.caller_profile = caller_profile; + hunt_data.tech_pvt = tech_pvt; + caller_data.priv = &hunt_data; - if (SPAN_CONFIG[span_id].limit_calls) { - char spanresource[512]; - snprintf(spanresource, sizeof(spanresource), "span_%s_%s", ftdm_channel_get_span_name(ftdmchan), caller_data.dnis.digits); - ftdm_log(FTDM_LOG_DEBUG, "Adding rate limit resource on channel %d:%d (%s/%s/%d/%d)\n", span_id, chan_id, FREETDM_LIMIT_REALM, - spanresource, SPAN_CONFIG[span_id].limit_calls, SPAN_CONFIG[span_id].limit_seconds); - if (switch_limit_incr("hash", *new_session, FREETDM_LIMIT_REALM, spanresource, SPAN_CONFIG[span_id].limit_calls, SPAN_CONFIG[span_id].limit_seconds) != SWITCH_STATUS_SUCCESS) { - switch_core_session_destroy(new_session); - cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; - goto fail; - } - } - - if ((status = ftdm_channel_call_place(ftdmchan)) != FTDM_SUCCESS) { + if ((status = ftdm_call_place(&caller_data, &hunting)) != FTDM_SUCCESS) { if (tech_pvt->read_codec.implementation) { switch_core_codec_destroy(&tech_pvt->read_codec); } @@ -1402,28 +1426,22 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi switch_core_codec_destroy(&tech_pvt->write_codec); } switch_core_session_destroy(new_session); - if (status == FTDM_BREAK) { /* glare, we don't want to touch the channel since is being used for incoming call now */ + if (status == FTDM_BREAK || status == FTDM_EBUSY) { cause = SWITCH_CAUSE_NORMAL_CIRCUIT_CONGESTION; - ftdmchan = NULL; } else { cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; } - goto fail; + goto fail; } - ftdm_channel_init(ftdmchan); + ftdm_channel_init(caller_data.fchan); return SWITCH_CAUSE_SUCCESS; } fail: - if (ftdmchan) { - ftdm_channel_call_hangup_with_cause(ftdmchan, FTDM_CAUSE_NORMAL_TEMPORARY_FAILURE); - } - return cause; - } static void ftdm_enable_channel_dtmf(ftdm_channel_t *fchan, switch_channel_t *channel) @@ -1645,6 +1663,7 @@ static FIO_SIGNAL_CB_FUNCTION(on_common_signal) case FTDM_SIGEVENT_RELEASED: case FTDM_SIGEVENT_INDICATION_COMPLETED: + case FTDM_SIGEVENT_DIALING: { /* Swallow these events */ return FTDM_BREAK; diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index cce3963c3b..f111c8e754 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -1425,8 +1425,9 @@ static __inline__ int request_voice_channel(ftdm_channel_t *check, ftdm_channel_ * sometimes with the group or span lock held and were * blocking anyone hunting for channels available and * I believe teh channel_request() function may take - * a bit of time - * */ + * a bit of time. However channel_request is a callback + * used by boost and may be only a few other old sig mods + * and it should be deprecated */ ftdm_mutex_unlock(check->mutex); ftdm_set_caller_data(check->span, caller_data); status = check->span->channel_request(check->span, check->chan_id, @@ -1439,7 +1440,9 @@ static __inline__ int request_voice_channel(ftdm_channel_t *check, ftdm_channel_ if (status == FTDM_SUCCESS) { *ftdmchan = check; ftdm_set_flag(check, FTDM_CHANNEL_OUTBOUND); +#if 0 ftdm_mutex_unlock(check->mutex); +#endif return 1; } } @@ -1491,9 +1494,9 @@ static ftdm_status_t __inline__ get_best_rated(ftdm_channel_t **fchan, ftdm_chan } *fchan = best_rated; ftdm_set_flag(best_rated, FTDM_CHANNEL_OUTBOUND); - +#if 0 ftdm_mutex_unlock(best_rated->mutex); - +#endif return FTDM_SUCCESS; } @@ -1523,7 +1526,7 @@ FT_DECLARE(int) ftdm_channel_get_availability(ftdm_channel_t *ftdmchan) return availability; } -FT_DECLARE(ftdm_status_t) ftdm_channel_open_by_group(uint32_t group_id, ftdm_direction_t direction, ftdm_caller_data_t *caller_data, ftdm_channel_t **ftdmchan) +static ftdm_status_t _ftdm_channel_open_by_group(uint32_t group_id, ftdm_direction_t direction, ftdm_caller_data_t *caller_data, ftdm_channel_t **ftdmchan) { ftdm_status_t status = FTDM_FAIL; ftdm_channel_t *check = NULL; @@ -1604,6 +1607,16 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_open_by_group(uint32_t group_id, ftdm_dir return status; } +FT_DECLARE(ftdm_status_t) ftdm_channel_open_by_group(uint32_t group_id, ftdm_direction_t direction, ftdm_caller_data_t *caller_data, ftdm_channel_t **ftdmchan) +{ + ftdm_status_t status; + status = _ftdm_channel_open_by_group(group_id, direction, caller_data, ftdmchan); + if (status == FTDM_SUCCESS) { + ftdm_channel_t *fchan = *ftdmchan; + ftdm_channel_unlock(fchan); + } + return status; +} FT_DECLARE(ftdm_status_t) ftdm_span_channel_use_count(ftdm_span_t *span, uint32_t *count) { @@ -1626,7 +1639,8 @@ FT_DECLARE(ftdm_status_t) ftdm_span_channel_use_count(ftdm_span_t *span, uint32_ return FTDM_SUCCESS; } -FT_DECLARE(ftdm_status_t) ftdm_channel_open_by_span(uint32_t span_id, ftdm_direction_t direction, ftdm_caller_data_t *caller_data, ftdm_channel_t **ftdmchan) +/* Hunt a channel by span, if successful the channel is returned locked */ +static ftdm_status_t _ftdm_channel_open_by_span(uint32_t span_id, ftdm_direction_t direction, ftdm_caller_data_t *caller_data, ftdm_channel_t **ftdmchan) { ftdm_status_t status = FTDM_FAIL; ftdm_channel_t *check = NULL; @@ -1724,6 +1738,17 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_open_by_span(uint32_t span_id, ftdm_direc return status; } +FT_DECLARE(ftdm_status_t) ftdm_channel_open_by_span(uint32_t span_id, ftdm_direction_t direction, ftdm_caller_data_t *caller_data, ftdm_channel_t **ftdmchan) +{ + ftdm_status_t status; + status = _ftdm_channel_open_by_span(span_id, direction, caller_data, ftdmchan); + if (status == FTDM_SUCCESS) { + ftdm_channel_t *fchan = *ftdmchan; + ftdm_channel_unlock(fchan); + } + return status; +} + FT_DECLARE(ftdm_status_t) ftdm_channel_open_chan(ftdm_channel_t *ftdmchan) { ftdm_status_t status = FTDM_FAIL; @@ -1774,7 +1799,7 @@ done: return status; } -FT_DECLARE(ftdm_status_t) ftdm_channel_open(uint32_t span_id, uint32_t chan_id, ftdm_channel_t **ftdmchan) +static ftdm_status_t _ftdm_channel_open(uint32_t span_id, uint32_t chan_id, ftdm_channel_t **ftdmchan) { ftdm_channel_t *check = NULL; ftdm_span_t *span = NULL; @@ -1855,6 +1880,10 @@ openchan: ftdm_set_flag(check, FTDM_CHANNEL_INUSE); ftdm_set_flag(check, FTDM_CHANNEL_OUTBOUND); *ftdmchan = check; +#if 1 + /* we've got the channel, do not unlock it */ + goto done; +#endif unlockchan: ftdm_mutex_unlock(check->mutex); @@ -1868,6 +1897,17 @@ done: return status; } +FT_DECLARE(ftdm_status_t) ftdm_channel_open(uint32_t span_id, uint32_t chan_id, ftdm_channel_t **ftdmchan) +{ + ftdm_status_t status; + status = _ftdm_channel_open(span_id, chan_id, ftdmchan); + if (status == FTDM_SUCCESS) { + ftdm_channel_t *fchan = *ftdmchan; + ftdm_channel_unlock(fchan); + } + return status; +} + FT_DECLARE(uint32_t) ftdm_channel_get_id(const ftdm_channel_t *ftdmchan) { return ftdmchan->chan_id; @@ -2048,25 +2088,11 @@ FT_DECLARE(void) ftdm_ack_indication(ftdm_channel_t *fchan, ftdm_channel_indicat ftdm_span_send_signal(fchan->span, &msg); } -/*! \brief Answer call without locking the channel. The caller must have locked first - * \note This function was added because ftdm_channel_call_indicate needs to answer the call - * when its already locking the channel, ftdm_channel_set_state cannot be called with the same - * lock locked once or more (recursive lock) and wait for the result */ +/*! Answer call without locking the channel. The caller must have locked first */ static ftdm_status_t _ftdm_channel_call_answer_nl(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) { ftdm_status_t status = FTDM_SUCCESS; - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { - status = FTDM_EINVAL; - goto done; - } - - if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call is already TERMINATING\n"); - status = FTDM_ECANCELED; - goto done; - } - if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_SKIP_STATES)) { /* We will fail RFC's if we not skip states, but some modules apart from ftmod_sangoma_isdn * expect the call to always to go PROGRESS and PROGRESS MEDIA state before going to UP, so @@ -2116,13 +2142,12 @@ done: FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) { - ftdm_status_t status = FTDM_SUCCESS; + ftdm_status_t status; - ftdm_channel_lock(ftdmchan); + /* we leave the locking up to ftdm_channel_call_indicate, DO NOT lock here since ftdm_channel_call_indicate expects + * the lock recursivity to be 1 */ + status = _ftdm_channel_call_indicate(file, func, line, ftdmchan, FTDM_CHANNEL_INDICATE_ANSWER); - status = _ftdm_channel_call_answer_nl(file, func, line, ftdmchan); - - ftdm_channel_unlock(ftdmchan); return status; } @@ -2319,7 +2344,6 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const ch status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, 1); break; case FTDM_CHANNEL_INDICATE_ANSWER: - /* _ftdm_channel_call_answer takes care of the indication ack */ status = _ftdm_channel_call_answer_nl(file, func, line, ftdmchan); break; default: @@ -2365,7 +2389,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_reset(const char *file, const char *func return FTDM_SUCCESS; } -FT_DECLARE(ftdm_status_t) _ftdm_channel_call_place(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) +static ftdm_status_t _ftdm_channel_call_place_nl(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) { ftdm_status_t status = FTDM_FAIL; @@ -2374,8 +2398,6 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_place(const char *file, const char ftdm_set_echocancel_call_begin(ftdmchan); - ftdm_channel_lock(ftdmchan); - if (!ftdmchan->span->outgoing_call) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "outgoing_call method not implemented in this span!\n"); status = FTDM_ENOSYS; @@ -2427,8 +2449,6 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_place(const char *file, const char } done: - ftdm_channel_unlock(ftdmchan); - #ifdef __WINDOWS__ UNREFERENCED_PARAMETER(file); UNREFERENCED_PARAMETER(func); @@ -2437,6 +2457,64 @@ done: return status; } +FT_DECLARE(ftdm_status_t) _ftdm_channel_call_place(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) +{ + ftdm_status_t status; + ftdm_channel_lock(ftdmchan); + + status = _ftdm_channel_call_place_nl(file, func, line, ftdmchan); + + ftdm_channel_unlock(ftdmchan); + return status; +} + +FT_DECLARE(ftdm_status_t) _ftdm_call_place(const char *file, const char *func, int line, + ftdm_caller_data_t *caller_data, ftdm_hunting_scheme_t *hunting) +{ + ftdm_assert_return(caller_data, FTDM_EINVAL, "Invalid caller data\n"); + ftdm_assert_return(hunting, FTDM_EINVAL, "Invalid hunting scheme\n"); + + ftdm_status_t status = FTDM_SUCCESS; + ftdm_channel_t *fchan = NULL; + + if (hunting->mode == FTDM_HUNT_SPAN) { + status = _ftdm_channel_open_by_span(hunting->mode_data.span.span_id, + hunting->mode_data.span.direction, caller_data, &fchan); + } else if (hunting->mode == FTDM_HUNT_GROUP) { + status = _ftdm_channel_open_by_group(hunting->mode_data.group.group_id, + hunting->mode_data.group.direction, caller_data, &fchan); + } else if (hunting->mode == FTDM_HUNT_CHAN) { + status = _ftdm_channel_open(hunting->mode_data.chan.span_id, hunting->mode_data.chan.chan_id, &fchan); + } else { + ftdm_log(FTDM_LOG_ERROR, "Cannot make outbound call with invalid hunting mode %d\n", hunting->mode); + return FTDM_EINVAL; + } + + if (status != FTDM_SUCCESS) { + return FTDM_EBUSY; + } + + /* we have a locked channel and are not afraid of using it! */ + status = hunting->result_cb(fchan, caller_data); + if (status != FTDM_SUCCESS) { + status = FTDM_ECANCELED; + goto done; + } + + ftdm_channel_set_caller_data(fchan, caller_data); + + status = _ftdm_channel_call_place_nl(file, func, line, fchan); + if (status != FTDM_SUCCESS) { + goto done; + } + + caller_data->fchan = fchan; +done: + ftdm_channel_unlock(fchan); + + return status; +} + FT_DECLARE(ftdm_status_t) ftdm_channel_set_sig_status(ftdm_channel_t *ftdmchan, ftdm_signaling_status_t sigstatus) { ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "Null channel\n"); diff --git a/libs/freetdm/src/ftdm_state.c b/libs/freetdm/src/ftdm_state.c index 593dc5858b..574d85845b 100644 --- a/libs/freetdm/src/ftdm_state.c +++ b/libs/freetdm/src/ftdm_state.c @@ -77,6 +77,12 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const c } else if (state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA) { ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS); ftdm_set_flag(fchan, FTDM_CHANNEL_MEDIA); + } 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); } /* MAINTENANCE WARNING diff --git a/libs/freetdm/src/include/freetdm.h b/libs/freetdm/src/include/freetdm.h index 5f61b0e257..121aa18bf7 100644 --- a/libs/freetdm/src/include/freetdm.h +++ b/libs/freetdm/src/include/freetdm.h @@ -321,8 +321,57 @@ typedef struct ftdm_caller_data { * 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) */ + void *priv; /*!< Private data for the FreeTDM user */ } ftdm_caller_data_t; +/*! \brief Hunting mode */ +typedef enum { + FTDM_HUNT_SPAN, /*!< Hunt channels in a given span */ + FTDM_HUNT_GROUP, /*!< Hunt channels in a given group */ + FTDM_HUNT_CHAN, /*!< Hunt for a specific channel */ +} ftdm_hunt_mode_t; + +/*! \brief Structure used for FTDM_HUNT_SPAN mode */ +typedef struct { + uint32_t span_id; + ftdm_direction_t direction; +} ftdm_span_hunt_t; + +/*! \brief Structure used for FTDM_HUNT_GROUP mode */ +typedef struct { + uint32_t group_id; + ftdm_direction_t direction; +} ftdm_group_hunt_t; + +/*! \brief Structure used for FTDM_HUNT_CHAN mode */ +typedef struct { + uint32_t span_id; + uint32_t chan_id; +} ftdm_chan_hunt_t; + +/*! \brief Function called before placing the call in the hunted channel + * The user can have a last saying in whether to proceed or abort + * the call attempt. Be aware that this callback will be called with + * the channel lock and you must not do any blocking operations during + * its execution. + * \param fchan The channel that will be used to place the call + * \param caller_data The caller data provided to ftdm_call_place + * \return FTDM_SUCCESS to proceed or FTDM_BREAK to abort the hunting + */ +typedef ftdm_status_t (*ftdm_hunt_result_cb_t)(ftdm_channel_t *fchan, ftdm_caller_data_t *caller_data); + +/*! \brief Channel Hunting provided to ftdm_call_place() */ +typedef struct { + ftdm_hunt_mode_t mode; + union { + ftdm_span_hunt_t span; + ftdm_group_hunt_t group; + ftdm_chan_hunt_t chan; + } mode_data; + ftdm_hunt_result_cb_t result_cb; +} ftdm_hunting_scheme_t; + + /*! \brief Tone type */ typedef enum { FTDM_TONE_DTMF = (1 << 0) @@ -335,7 +384,7 @@ typedef enum { FTDM_SIGEVENT_RELEASED, /*!< Channel is completely released and available */ FTDM_SIGEVENT_UP, /*!< Outgoing call has been answered */ FTDM_SIGEVENT_FLASH, /*!< Flash event (typically on-hook/off-hook for analog devices) */ - FTDM_SIGEVENT_PROCEED, /*!< Outgoing call got a response */ + FTDM_SIGEVENT_PROCEED, /*!< Outgoing call got an initial positive response from the other end */ FTDM_SIGEVENT_RINGING, /*!< Remote side is in ringing state */ FTDM_SIGEVENT_PROGRESS, /*!< Outgoing call is making progress */ FTDM_SIGEVENT_PROGRESS_MEDIA, /*!< Outgoing call is making progress and there is media available */ @@ -350,12 +399,13 @@ typedef enum { FTDM_SIGEVENT_TRACE, /*!