diff options
author | rmudgett <rmudgett@f38db490-d61c-443f-a65b-d21fe96a405b> | 2010-06-02 16:14:12 +0000 |
---|---|---|
committer | rmudgett <rmudgett@f38db490-d61c-443f-a65b-d21fe96a405b> | 2010-06-02 16:14:12 +0000 |
commit | aa138b6e3f15826d199ecac0160d6ba2f21cec84 (patch) | |
tree | 35a07219840cd89e9f223a408a5bd77e900e18d0 /channels/sig_pri.c | |
parent | b988f37ebc56d5a8dc6f95b7477da4e2efc3a3b0 (diff) |
Add ETSI Explicit Call Transfer (ECT) support.
Added ability to send and receive ETSI Explicit Call Transfer (ECT)
messages to eliminate tromboned calls.
Note: Asterisk already supported initiating the transfer of calls to
eliminate tromboned calls to libpri so there was nothing to do for the
asterisk portion.
Review: https://reviewboard.asterisk.org/r/520/
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@266926 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'channels/sig_pri.c')
-rw-r--r-- | channels/sig_pri.c | 197 |
1 files changed, 123 insertions, 74 deletions
diff --git a/channels/sig_pri.c b/channels/sig_pri.c index ae8f0ea32..846f8b055 100644 --- a/channels/sig_pri.c +++ b/channels/sig_pri.c @@ -1569,6 +1569,106 @@ static int sig_pri_msn_match(const char *msn_patterns, const char *exten) return 0; } +#if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) +/*! + * \internal + * \brief Attempt to transfer the two calls to each other. + * \since 1.8 + * + * \param pri sig_pri PRI control structure. + * \param call_1 First call involved in the transfer. + * \param call_1_held TRUE if call_1 is on hold. + * \param call_2 Second call involved in the transfer. + * \param call_2_held TRUE if call_2 is on hold. + * + * \note Assumes the pri->lock is already obtained. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int sig_pri_attempt_transfer(struct sig_pri_pri *pri, q931_call *call_1, int call_1_held, q931_call *call_2, int call_2_held) +{ + int retval; + int call_1_chanpos; + int call_2_chanpos; + struct ast_channel *call_1_ast; + struct ast_channel *call_2_ast; + struct ast_channel *bridged; + + call_1_chanpos = pri_find_pri_call(pri, call_1); + call_2_chanpos = pri_find_pri_call(pri, call_2); + if (call_1_chanpos < 0 || call_2_chanpos < 0) { + return -1; + } + + /* Deadlock avoidance is attempted. */ + sig_pri_lock_private(pri->pvts[call_1_chanpos]); + sig_pri_lock_owner(pri, call_1_chanpos); + sig_pri_lock_private(pri->pvts[call_2_chanpos]); + sig_pri_lock_owner(pri, call_2_chanpos); + + call_1_ast = pri->pvts[call_1_chanpos]->owner; + call_2_ast = pri->pvts[call_2_chanpos]->owner; + if (!call_1_ast || !call_2_ast) { + if (call_1_ast) { + ast_channel_unlock(call_1_ast); + } + if (call_2_ast) { + ast_channel_unlock(call_2_ast); + } + sig_pri_unlock_private(pri->pvts[call_1_chanpos]); + sig_pri_unlock_private(pri->pvts[call_2_chanpos]); + return -1; + } + + bridged = ast_bridged_channel(call_2_ast); + if (bridged) { + if (call_1_held) { + ast_queue_control(call_1_ast, AST_CONTROL_UNHOLD); + } + if (call_2_held) { + ast_queue_control(call_2_ast, AST_CONTROL_UNHOLD); + } + + ast_verb(3, "TRANSFERRING %s to %s\n", call_2_ast->name, call_1_ast->name); + retval = ast_channel_masquerade(call_1_ast, bridged); + } else { + /* Try masquerading the other way. */ + bridged = ast_bridged_channel(call_1_ast); + if (bridged) { + if (call_1_held) { + ast_queue_control(call_1_ast, AST_CONTROL_UNHOLD); + } + if (call_2_held) { + ast_queue_control(call_2_ast, AST_CONTROL_UNHOLD); + } + + ast_verb(3, "TRANSFERRING %s to %s\n", call_1_ast->name, call_2_ast->name); + retval = ast_channel_masquerade(call_2_ast, bridged); + } else { + /* Could not transfer. */ + retval = -1; + } + } + if (bridged && retval) { + /* Restore HOLD on held calls because masquerade failed. */ + if (call_1_held) { + ast_queue_control(call_1_ast, AST_CONTROL_HOLD); + } + if (call_2_held) { + ast_queue_control(call_2_ast, AST_CONTROL_HOLD); + } + } + + ast_channel_unlock(call_1_ast); + ast_channel_unlock(call_2_ast); + sig_pri_unlock_private(pri->pvts[call_1_chanpos]); + sig_pri_unlock_private(pri->pvts[call_2_chanpos]); + + return retval; +} +#endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */ + #if defined(HAVE_PRI_CCSS) /*! * \internal @@ -2406,6 +2506,24 @@ static void sig_pri_handle_subcmds(struct sig_pri_pri *pri, int chanpos, int eve subcmd->u.cc_cancel.is_agent); break; #endif /* defined(HAVE_PRI_CCSS) */ +#if defined(HAVE_PRI_TRANSFER) + case PRI_SUBCMD_TRANSFER_CALL: + if (!call_rsp) { + /* Should never happen. */ + ast_log(LOG_ERROR, + "Call transfer subcommand without call to send response!\n"); + break; + } + + sig_pri_unlock_private(pri->pvts[chanpos]); + pri_transfer_rsp(pri->pri, call_rsp, subcmd->u.transfer.invoke_id, + sig_pri_attempt_transfer(pri, + subcmd->u.transfer.call_1, subcmd->u.transfer.is_call_1_held, + subcmd->u.transfer.call_2, subcmd->u.transfer.is_call_2_held) + ? 0 : 1); + sig_pri_lock_private(pri->pvts[chanpos]); + break; +#endif /* defined(HAVE_PRI_TRANSFER) */ default: ast_debug(2, "Unknown call subcommand(%d) in %s event on channel %d/%d on span %d.\n", @@ -2419,78 +2537,6 @@ static void sig_pri_handle_subcmds(struct sig_pri_pri *pri, int chanpos, int eve #if defined(HAVE_PRI_CALL_HOLD) /*! * \internal - * \brief Attempt to transfer the active call to the held call. - * \since 1.8 - * - * \param pri sig_pri PRI control structure. - * \param active_call Active call to transfer. - * \param held_call Held call to transfer. - * - * \note Assumes the pri->lock is already obtained. - * - * \retval 0 on success. - * \retval -1 on error. - */ -static int sig_pri_attempt_transfer(struct sig_pri_pri *pri, q931_call *active_call, q931_call *held_call) -{ - int retval; - int active_chanpos; - int held_chanpos; - struct ast_channel *active_ast; - struct ast_channel *held_ast; - struct ast_channel *bridged; - - active_chanpos = pri_find_pri_call(pri, active_call); - held_chanpos = pri_find_pri_call(pri, held_call); - if (active_chanpos < 0 || held_chanpos < 0) { - return -1; - } - - sig_pri_lock_private(pri->pvts[active_chanpos]); - sig_pri_lock_private(pri->pvts[held_chanpos]); - sig_pri_lock_owner(pri, active_chanpos); - sig_pri_lock_owner(pri, held_chanpos); - - active_ast = pri->pvts[active_chanpos]->owner; - held_ast = pri->pvts[held_chanpos]->owner; - if (!active_ast || !held_ast) { - if (active_ast) { - ast_channel_unlock(active_ast); - } - if (held_ast) { - ast_channel_unlock(held_ast); - } - sig_pri_unlock_private(pri->pvts[active_chanpos]); - sig_pri_unlock_private(pri->pvts[held_chanpos]); - return -1; - } - - bridged = ast_bridged_channel(held_ast); - if (bridged) { - ast_queue_control(held_ast, AST_CONTROL_UNHOLD); - - ast_verb(3, "TRANSFERRING %s to %s\n", held_ast->name, active_ast->name); - retval = ast_channel_masquerade(active_ast, bridged); - } else { - /* - * Could not transfer. Held channel is not bridged anymore. - * Held party probably got tired of waiting and hung up. - */ - retval = -1; - } - - ast_channel_unlock(active_ast); - ast_channel_unlock(held_ast); - sig_pri_unlock_private(pri->pvts[active_chanpos]); - sig_pri_unlock_private(pri->pvts[held_chanpos]); - - return retval; -} -#endif /* defined(HAVE_PRI_CALL_HOLD) */ - -#if defined(HAVE_PRI_CALL_HOLD) -/*! - * \internal * \brief Handle the hold event from libpri. * \since 1.8 * @@ -3766,8 +3812,8 @@ static void *pri_dchannel(void *vpri) && pri->hold_disconnect_transfer) { /* We are to transfer the call instead of simply hanging up. */ sig_pri_unlock_private(pri->pvts[chanpos]); - if (!sig_pri_attempt_transfer(pri, e->hangup.call_active, - e->hangup.call_held)) { + if (!sig_pri_attempt_transfer(pri, e->hangup.call_active, 0, + e->hangup.call_held, 1)) { break; } sig_pri_lock_private(pri->pvts[chanpos]); @@ -4884,6 +4930,9 @@ int sig_pri_start_pri(struct sig_pri_pri *pri) pri_cc_retain_signaling_req(pri->pri, pri->cc_qsig_signaling_link_req); pri_cc_retain_signaling_rsp(pri->pri, pri->cc_qsig_signaling_link_rsp); #endif /* defined(HAVE_PRI_CCSS) */ +#if defined(HAVE_PRI_TRANSFER) + pri_transfer_enable(pri->pri, 1); +#endif /* defined(HAVE_PRI_TRANSFER) */ pri->resetpos = -1; if (ast_pthread_create_background(&pri->master, NULL, pri_dchannel, pri)) { |