From 611416623748504be81c58b455205a4bc7fff414 Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Fri, 7 Jun 2013 01:06:49 +0000 Subject: Refactor chan_dahdi/sig_analog/sig_pri and chan_misdn to use the common transfer functions. (closes issue ASTERISK-21523) Reported by: Matt Jordan (closes issue ASTERISK-21524) Reported by: Matt Jordan Review: https://reviewboard.asterisk.org/r/2600/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@390804 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- channels/chan_dahdi.c | 105 ++++++++++++-------------- channels/chan_misdn.c | 73 +++++------------- channels/sig_analog.c | 119 +++++++++-------------------- channels/sig_pri.c | 203 +++++++++++++++++++++++++------------------------- channels/sig_pri.h | 5 ++ 5 files changed, 209 insertions(+), 296 deletions(-) (limited to 'channels') diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index 92de02391..109fac06d 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -131,6 +131,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/ccss.h" #include "asterisk/data.h" #include "asterisk/features_config.h" +#include "asterisk/bridging.h" /*** DOCUMENTATION @@ -7841,55 +7842,49 @@ static int dahdi_ring_phone(struct dahdi_pvt *p) static void *analog_ss_thread(void *data); +/*! + * \internal + * \brief Attempt to transfer 3-way call. + * + * \param p DAHDI private structure. + * + * \note On entry these locks are held: real-call, private, 3-way call. + * \note On exit these locks are held: real-call, private. + * + * \retval 0 on success. + * \retval -1 on error. + */ static int attempt_transfer(struct dahdi_pvt *p) { - /* In order to transfer, we need at least one of the channels to - actually be in a call bridge. We can't conference two applications - together (but then, why would we want to?) */ - if (ast_bridged_channel(p->subs[SUB_REAL].owner)) { - /* The three-way person we're about to transfer to could still be in MOH, so - stop it now */ - ast_queue_unhold(p->subs[SUB_THREEWAY].owner); - if (ast_channel_state(p->subs[SUB_REAL].owner) == AST_STATE_RINGING) { - ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_RINGING); - } - if (ast_channel_state(p->subs[SUB_THREEWAY].owner) == AST_STATE_RING) { - tone_zone_play_tone(p->subs[SUB_THREEWAY].dfd, DAHDI_TONE_RINGTONE); - } - if (ast_channel_masquerade(p->subs[SUB_THREEWAY].owner, ast_bridged_channel(p->subs[SUB_REAL].owner))) { - ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n", - ast_channel_name(ast_bridged_channel(p->subs[SUB_REAL].owner)), ast_channel_name(p->subs[SUB_THREEWAY].owner)); - return -1; - } - /* Orphan the channel after releasing the lock */ - ast_channel_unlock(p->subs[SUB_THREEWAY].owner); - unalloc_sub(p, SUB_THREEWAY); - } else if (ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) { - ast_queue_unhold(p->subs[SUB_REAL].owner); - if (ast_channel_state(p->subs[SUB_THREEWAY].owner) == AST_STATE_RINGING) { - ast_queue_control(p->subs[SUB_THREEWAY].owner, AST_CONTROL_RINGING); - } - if (ast_channel_state(p->subs[SUB_REAL].owner) == AST_STATE_RING) { - tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE); - } - if (ast_channel_masquerade(p->subs[SUB_REAL].owner, ast_bridged_channel(p->subs[SUB_THREEWAY].owner))) { - ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n", - ast_channel_name(ast_bridged_channel(p->subs[SUB_THREEWAY].owner)), ast_channel_name(p->subs[SUB_REAL].owner)); - return -1; - } - /* Three-way is now the REAL */ - swap_subs(p, SUB_THREEWAY, SUB_REAL); - ast_channel_unlock(p->subs[SUB_REAL].owner); - unalloc_sub(p, SUB_THREEWAY); - /* Tell the caller not to hangup */ - return 1; - } else { - ast_debug(1, "Neither %s nor %s are in a bridge, nothing to transfer\n", - ast_channel_name(p->subs[SUB_REAL].owner), ast_channel_name(p->subs[SUB_THREEWAY].owner)); - ast_channel_softhangup_internal_flag_add(p->subs[SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV); - return -1; + struct ast_channel *owner_real; + struct ast_channel *owner_3way; + enum ast_transfer_result xfer_res; + int res = 0; + + owner_real = ast_channel_ref(p->subs[SUB_REAL].owner); + owner_3way = ast_channel_ref(p->subs[SUB_THREEWAY].owner); + + ast_verb(3, "TRANSFERRING %s to %s\n", + ast_channel_name(owner_3way), ast_channel_name(owner_real)); + + ast_channel_unlock(owner_real); + ast_channel_unlock(owner_3way); + ast_mutex_unlock(&p->lock); + + xfer_res = ast_bridge_transfer_attended(owner_3way, owner_real); + if (xfer_res != AST_BRIDGE_TRANSFER_SUCCESS) { + ast_softhangup(owner_3way, AST_SOFTHANGUP_DEV); + res = -1; } - return 0; + + /* Must leave with these locked. */ + ast_channel_lock(owner_real); + ast_mutex_lock(&p->lock); + + ast_channel_unref(owner_real); + ast_channel_unref(owner_3way); + + return res; } static int check_for_conference(struct dahdi_pvt *p) @@ -8399,17 +8394,13 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast) p->owner = NULL; /* Ring the phone */ dahdi_ring_phone(p); - } else { - if ((res = attempt_transfer(p)) < 0) { - ast_channel_softhangup_internal_flag_add(p->subs[SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV); - if (p->subs[SUB_THREEWAY].owner) - ast_channel_unlock(p->subs[SUB_THREEWAY].owner); - } else if (res) { - /* Don't actually hang up at this point */ - if (p->subs[SUB_THREEWAY].owner) - ast_channel_unlock(p->subs[SUB_THREEWAY].owner); - break; - } + } else if (!attempt_transfer(p)) { + /* + * Transfer successful. Don't actually hang up at this point. + * Let our channel legs of the calls die off as the transfer + * percolates through the core. + */ + break; } } else { ast_channel_softhangup_internal_flag_add(p->subs[SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV); diff --git a/channels/chan_misdn.c b/channels/chan_misdn.c index 1a8f0d980..38387d373 100644 --- a/channels/chan_misdn.c +++ b/channels/chan_misdn.c @@ -103,6 +103,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/format.h" #include "asterisk/format_cap.h" #include "asterisk/features_config.h" +#include "asterisk/bridging.h" #include "chan_misdn_config.h" #include "isdn_lib.h" @@ -8592,10 +8593,9 @@ static void release_chan_early(struct chan_list *ch) static int misdn_attempt_transfer(struct chan_list *active_ch, struct chan_list *held_ch) { int retval; - struct ast_channel *target; - struct ast_channel *transferee; - struct ast_party_connected_line target_colp; - struct ast_party_connected_line transferee_colp; + enum ast_transfer_result xfer_res; + struct ast_channel *to_target; + struct ast_channel *to_transferee; switch (active_ch->state) { case MISDN_PROCEEDING: @@ -8608,59 +8608,24 @@ static int misdn_attempt_transfer(struct chan_list *active_ch, struct chan_list } ast_channel_lock_both(held_ch->ast, active_ch->ast); - - transferee = ast_bridged_channel(held_ch->ast); - if (!transferee) { - /* - * Could not transfer. Held channel is not bridged anymore. - * Held party probably got tired of waiting and hung up. - */ - ast_channel_unlock(held_ch->ast); - ast_channel_unlock(active_ch->ast); - return -1; - } - - target = active_ch->ast; + to_target = active_ch->ast; + to_transferee = held_ch->ast; chan_misdn_log(1, held_ch->hold.port, "TRANSFERRING %s to %s\n", - ast_channel_name(held_ch->ast), ast_channel_name(target)); - - ast_party_connected_line_init(&target_colp); - ast_party_connected_line_copy(&target_colp, ast_channel_connected(target)); - - /* Reset any earlier private connected id representation */ - ast_party_id_reset(&target_colp.priv); - - ast_party_connected_line_init(&transferee_colp); - ast_party_connected_line_copy(&transferee_colp, ast_channel_connected(held_ch->ast)); - - /* Reset any earlier private connected id representation*/ - ast_party_id_reset(&transferee_colp.priv); - + ast_channel_name(to_transferee), ast_channel_name(to_target)); held_ch->hold.state = MISDN_HOLD_TRANSFER; + ast_channel_ref(to_target); + ast_channel_ref(to_transferee); + ast_channel_unlock(to_target); + ast_channel_unlock(to_transferee); - /* - * Before starting a masquerade, all channel and pvt locks must - * be unlocked. Any recursive channel locks held before - * ast_channel_transfer_masquerade() invalidates deadlock - * avoidance. Since we are unlocking both the pvt and its owner - * channel it is possible for "target" and "transferee" to be - * destroyed by their pbx threads. To prevent this we must give - * "target" and "transferee" a reference before any unlocking - * takes place. - */ - ao2_ref(target, +1); - ao2_ref(transferee, +1); - ast_channel_unlock(held_ch->ast); - ast_channel_unlock(active_ch->ast); - - /* Setup transfer masquerade. */ - retval = ast_channel_transfer_masquerade(target, &target_colp, 0, - transferee, &transferee_colp, 1); - - ast_party_connected_line_free(&target_colp); - ast_party_connected_line_free(&transferee_colp); - ao2_ref(target, -1); - ao2_ref(transferee, -1); + retval = 0; + xfer_res = ast_bridge_transfer_attended(to_transferee, to_target); + if (xfer_res != AST_BRIDGE_TRANSFER_SUCCESS) { + retval = -1; + } + + ast_channel_unref(to_target); + ast_channel_unref(to_transferee); return retval; } diff --git a/channels/sig_analog.c b/channels/sig_analog.c index 956c4a654..9660d3fe6 100644 --- a/channels/sig_analog.c +++ b/channels/sig_analog.c @@ -44,6 +44,7 @@ #include "asterisk/cel.h" #include "asterisk/causes.h" #include "asterisk/features_config.h" +#include "asterisk/bridging.h" #include "sig_analog.h" @@ -688,87 +689,44 @@ static int analog_is_dialing(struct analog_pvt *p, enum analog_sub index) * \brief Attempt to transfer 3-way call. * * \param p Analog private structure. - * \param inthreeway TRUE if the 3-way call is conferenced. * - * \note - * On entry these locks are held: real-call, private, 3-way call. + * \note On entry these locks are held: real-call, private, 3-way call. + * \note On exit these locks are held: real-call, private. * - * \retval 1 Transfer successful. 3-way call is unlocked and subchannel is unalloced. - * Swapped real and 3-way subchannel. - * \retval 0 Transfer successful. 3-way call is unlocked and subchannel is unalloced. - * \retval -1 on error. Caller must unlock 3-way call. + * \retval 0 on success. + * \retval -1 on error. */ -static int analog_attempt_transfer(struct analog_pvt *p, int inthreeway) +static int analog_attempt_transfer(struct analog_pvt *p) { struct ast_channel *owner_real; struct ast_channel *owner_3way; - struct ast_channel *bridge_real; - struct ast_channel *bridge_3way; - - owner_real = p->subs[ANALOG_SUB_REAL].owner; - owner_3way = p->subs[ANALOG_SUB_THREEWAY].owner; - bridge_real = ast_bridged_channel(owner_real); - bridge_3way = ast_bridged_channel(owner_3way); - - /* - * In order to transfer, we need at least one of the channels to - * actually be in a call bridge. We can't conference two - * applications together. Why would we want to? - */ - if (bridge_3way) { - ast_verb(3, "TRANSFERRING %s to %s\n", ast_channel_name(owner_3way), ast_channel_name(owner_real)); - ast_cel_report_event(owner_3way, - (ast_channel_state(owner_real) == AST_STATE_RINGING - || ast_channel_state(owner_3way) == AST_STATE_RINGING) - ? AST_CEL_BLINDTRANSFER : AST_CEL_ATTENDEDTRANSFER, - NULL, ast_channel_linkedid(owner_3way), NULL); + enum ast_transfer_result xfer_res; + int res = 0; - /* - * The three-way party we're about to transfer is on hold if he - * is not in a three way conference. - */ - if (ast_channel_transfer_masquerade(owner_real, ast_channel_connected(owner_real), 0, - bridge_3way, ast_channel_connected(owner_3way), !inthreeway)) { - ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n", - ast_channel_name(bridge_3way), ast_channel_name(owner_real)); - return -1; - } + owner_real = ast_channel_ref(p->subs[ANALOG_SUB_REAL].owner); + owner_3way = ast_channel_ref(p->subs[ANALOG_SUB_THREEWAY].owner); - /* Three-way is now the REAL */ - analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL); - ast_channel_unlock(owner_3way); - analog_unalloc_sub(p, ANALOG_SUB_THREEWAY); - /* Tell the caller not to hangup */ - return 1; - } else if (bridge_real) { - /* Try transferring the other way. */ - ast_verb(3, "TRANSFERRING %s to %s\n", ast_channel_name(owner_real), ast_channel_name(owner_3way)); - ast_cel_report_event(owner_3way, - (ast_channel_state(owner_real) == AST_STATE_RINGING - || ast_channel_state(owner_3way) == AST_STATE_RINGING) - ? AST_CEL_BLINDTRANSFER : AST_CEL_ATTENDEDTRANSFER, - NULL, ast_channel_linkedid(owner_3way), NULL); + ast_verb(3, "TRANSFERRING %s to %s\n", + ast_channel_name(owner_3way), ast_channel_name(owner_real)); - /* - * The three-way party we're about to transfer is on hold if he - * is not in a three way conference. - */ - if (ast_channel_transfer_masquerade(owner_3way, ast_channel_connected(owner_3way), - !inthreeway, bridge_real, ast_channel_connected(owner_real), 0)) { - ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n", - ast_channel_name(bridge_real), ast_channel_name(owner_3way)); - return -1; - } + ast_channel_unlock(owner_real); + ast_channel_unlock(owner_3way); + analog_unlock_private(p); - /* Orphan the channel after releasing the lock */ - ast_channel_unlock(owner_3way); - analog_unalloc_sub(p, ANALOG_SUB_THREEWAY); - return 0; - } else { - ast_debug(1, "Neither %s nor %s are in a bridge, nothing to transfer\n", - ast_channel_name(owner_real), ast_channel_name(owner_3way)); - return -1; + xfer_res = ast_bridge_transfer_attended(owner_3way, owner_real); + if (xfer_res != AST_BRIDGE_TRANSFER_SUCCESS) { + ast_softhangup(owner_3way, AST_SOFTHANGUP_DEV); + res = -1; } + + /* Must leave with these locked. */ + ast_channel_lock(owner_real); + analog_lock_private(p); + + ast_channel_unref(owner_real); + ast_channel_unref(owner_3way); + + return res; } static int analog_update_conf(struct analog_pvt *p) @@ -2892,10 +2850,6 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_ ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); } else if ((ast_channel_pbx(ast)) || (ast_channel_state(ast) == AST_STATE_UP)) { if (p->transfer) { - int inthreeway; - - inthreeway = p->subs[ANALOG_SUB_THREEWAY].inthreeway; - /* In any case this isn't a threeway call anymore */ analog_set_inthreeway(p, ANALOG_SUB_REAL, 0); analog_set_inthreeway(p, ANALOG_SUB_THREEWAY, 0); @@ -2909,16 +2863,13 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_ analog_set_new_owner(p, NULL); /* Ring the phone */ analog_ring(p); - } else { - res = analog_attempt_transfer(p, inthreeway); - if (res < 0) { - /* Transfer attempt failed. */ - ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV); - ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); - } else if (res) { - /* Don't actually hang up at this point */ - break; - } + } else if (!analog_attempt_transfer(p)) { + /* + * Transfer successful. Don't actually hang up at this point. + * Let our channel legs of the calls die off as the transfer + * percolates through the core. + */ + break; } } else { ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV); diff --git a/channels/sig_pri.c b/channels/sig_pri.c index 461ea51b2..d5d6f4976 100644 --- a/channels/sig_pri.c +++ b/channels/sig_pri.c @@ -50,6 +50,7 @@ #include "asterisk/transcap.h" #include "asterisk/features.h" #include "asterisk/aoc.h" +#include "asterisk/bridging.h" #include "sig_pri.h" #ifndef PRI_EVENT_FACILITY @@ -1587,6 +1588,10 @@ static int pri_fixup_principle(struct sig_pri_span *pri, int principle, q931_cal strcpy(new_chan->moh_suggested, old_chan->moh_suggested); new_chan->moh_state = old_chan->moh_state; old_chan->moh_state = SIG_PRI_MOH_STATE_IDLE; +#if defined(HAVE_PRI_TRANSFER) + new_chan->xfer_data = old_chan->xfer_data; + old_chan->xfer_data = NULL; +#endif /* defined(HAVE_PRI_TRANSFER) */ #if defined(HAVE_PRI_AOC_EVENTS) new_chan->aoc_s_request_invoke_id = old_chan->aoc_s_request_invoke_id; @@ -2396,6 +2401,8 @@ struct xfer_rsp_data { q931_call *call; /*! Invocation ID to use when sending a reply to the transfer request. */ int invoke_id; + /*! TRUE if the transfer response has been made. */ + int responded; }; #endif /* defined(HAVE_PRI_TRANSFER) */ @@ -2405,32 +2412,24 @@ struct xfer_rsp_data { * \brief Send the transfer success/fail response message. * \since 1.8 * - * \param data Callback user data pointer + * \param rsp Transfer response data. * \param is_successful TRUE if the transfer was successful. * + * \note Assumes the rsp->pri->lock is already obtained. + * * \return Nothing */ -static void sig_pri_transfer_rsp(void *data, int is_successful) +static void sig_pri_transfer_rsp(struct xfer_rsp_data *rsp, int is_successful) { - struct xfer_rsp_data *rsp = data; + if (rsp->responded) { + return; + } + rsp->responded = 1; pri_transfer_rsp(rsp->pri->pri, rsp->call, rsp->invoke_id, is_successful); } #endif /* defined(HAVE_PRI_TRANSFER) */ -#if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) -/*! - * \brief Protocol callback to indicate if transfer will happen. - * \since 1.8 - * - * \param data Callback user data pointer - * \param is_successful TRUE if the transfer will happen. - * - * \return Nothing - */ -typedef void (*xfer_rsp_callback)(void *data, int is_successful); -#endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */ - #if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) /*! * \internal @@ -2442,15 +2441,14 @@ typedef void (*xfer_rsp_callback)(void *data, int is_successful); * \param call_1_held TRUE if call_1_pri is on hold. * \param call_2_pri Second call involved in the transfer. (target; usually active/ringing) * \param call_2_held TRUE if call_2_pri is on hold. - * \param rsp_callback Protocol callback to indicate if transfer will happen. NULL if not used. - * \param data Callback user data pointer + * \param xfer_data Transfer response data if non-NULL. * * \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_span *pri, q931_call *call_1_pri, int call_1_held, q931_call *call_2_pri, int call_2_held, xfer_rsp_callback rsp_callback, void *data) +static int sig_pri_attempt_transfer(struct sig_pri_span *pri, q931_call *call_1_pri, int call_1_held, q931_call *call_2_pri, int call_2_held, struct xfer_rsp_data *xfer_data) { struct attempt_xfer_call { q931_call *pri; @@ -2459,10 +2457,9 @@ static int sig_pri_attempt_transfer(struct sig_pri_span *pri, q931_call *call_1_ int chanpos; }; int retval; - struct ast_channel *transferee; + enum ast_transfer_result xfer_res; struct attempt_xfer_call *call_1; struct attempt_xfer_call *call_2; - struct attempt_xfer_call *swap_call; struct attempt_xfer_call c1; struct attempt_xfer_call c2; @@ -2478,118 +2475,104 @@ static int sig_pri_attempt_transfer(struct sig_pri_span *pri, q931_call *call_1_ call_2->chanpos = pri_find_principle_by_call(pri, call_2->pri); if (call_1->chanpos < 0 || call_2->chanpos < 0) { /* Calls not found in span control. */ - if (rsp_callback) { +#if defined(HAVE_PRI_TRANSFER) + if (xfer_data) { /* Transfer failed. */ - rsp_callback(data, 0); + sig_pri_transfer_rsp(xfer_data, 0); } +#endif /* defined(HAVE_PRI_TRANSFER) */ return -1; } - /* Attempt to make transferee and target consistent. */ - if (!call_1->held && call_2->held) { - /* - * Swap call_1 and call_2 to make call_1 the transferee(held call) - * and call_2 the target(active call). - */ - swap_call = call_1; - call_1 = call_2; - call_2 = swap_call; - } - - /* Deadlock avoidance is attempted. */ + /* Get call_1 owner. */ sig_pri_lock_private(pri->pvts[call_1->chanpos]); sig_pri_lock_owner(pri, call_1->chanpos); + call_1->ast = pri->pvts[call_1->chanpos]->owner; + if (call_1->ast) { + ast_channel_ref(call_1->ast); + ast_channel_unlock(call_1->ast); + } + sig_pri_unlock_private(pri->pvts[call_1->chanpos]); + + /* Get call_2 owner. */ 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_2->ast) { + ast_channel_ref(call_2->ast); + ast_channel_unlock(call_2->ast); + } + sig_pri_unlock_private(pri->pvts[call_2->chanpos]); + if (!call_1->ast || !call_2->ast) { /* At least one owner is not present. */ if (call_1->ast) { - ast_channel_unlock(call_1->ast); + ast_channel_unref(call_1->ast); } if (call_2->ast) { - ast_channel_unlock(call_2->ast); + ast_channel_unref(call_2->ast); } - sig_pri_unlock_private(pri->pvts[call_1->chanpos]); - sig_pri_unlock_private(pri->pvts[call_2->chanpos]); - if (rsp_callback) { +#if defined(HAVE_PRI_TRANSFER) + if (xfer_data) { /* Transfer failed. */ - rsp_callback(data, 0); + sig_pri_transfer_rsp(xfer_data, 0); } +#endif /* defined(HAVE_PRI_TRANSFER) */ return -1; } - for (;;) { - transferee = ast_bridged_channel(call_1->ast); - if (transferee) { - break; - } - - /* Try masquerading the other way. */ - swap_call = call_1; - call_1 = call_2; - call_2 = swap_call; + ast_verb(3, "TRANSFERRING %s to %s\n", + ast_channel_name(call_1->ast), ast_channel_name(call_2->ast)); - transferee = ast_bridged_channel(call_1->ast); - if (transferee) { - break; - } - - /* Could not transfer. Neither call is bridged. */ - ast_channel_unlock(call_1->ast); - ast_channel_unlock(call_2->ast); +#if defined(HAVE_PRI_TRANSFER) + if (xfer_data) { + /* + * Add traps on the transferer channels in case threading causes + * them to hangup before ast_bridge_transfer_attended() returns + * and we can get the pri->lock back. + */ + sig_pri_lock_private(pri->pvts[call_1->chanpos]); + pri->pvts[call_1->chanpos]->xfer_data = xfer_data; sig_pri_unlock_private(pri->pvts[call_1->chanpos]); + sig_pri_lock_private(pri->pvts[call_2->chanpos]); + pri->pvts[call_2->chanpos]->xfer_data = xfer_data; sig_pri_unlock_private(pri->pvts[call_2->chanpos]); - - if (rsp_callback) { - /* Transfer failed. */ - rsp_callback(data, 0); - } - return -1; } +#endif /* defined(HAVE_PRI_TRANSFER) */ - ast_verb(3, "TRANSFERRING %s to %s\n", ast_channel_name(call_1->ast), ast_channel_name(call_2->ast)); - - /* - * Setup transfer masquerade. - * - * Note: There is an extremely nasty deadlock avoidance issue - * with ast_channel_transfer_masquerade(). Deadlock may be possible if - * the channels involved are proxies (chan_agent channels) and - * it is called with locks. Unfortunately, there is no simple - * or even merely difficult way to guarantee deadlock avoidance - * and still be able to send an ECT success response without the - * possibility of the bridged channel hanging up on us. - */ ast_mutex_unlock(&pri->lock); - retval = ast_channel_transfer_masquerade( - call_2->ast, - ast_channel_connected(call_2->ast), - call_2->held, - transferee, - ast_channel_connected(call_1->ast), - call_1->held); - - /* Reacquire the pri->lock to hold off completion of the transfer masquerade. */ + xfer_res = ast_bridge_transfer_attended(call_1->ast, call_2->ast); ast_mutex_lock(&pri->lock); + retval = (xfer_res != AST_BRIDGE_TRANSFER_SUCCESS) ? -1 : 0; - 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]); +#if defined(HAVE_PRI_TRANSFER) + if (xfer_data) { + int rsp_chanpos; - if (rsp_callback) { /* - * Report transfer status. + * Remove the transferrer channel traps. * - * Must do the callback before the masquerade completes to ensure - * that the protocol message goes out before the call leg is - * disconnected. + * We must refind chanpos because we released pri->lock. */ - rsp_callback(data, retval ? 0 : 1); + rsp_chanpos = pri_find_principle_by_call(pri, call_1->pri); + if (0 <= rsp_chanpos) { + sig_pri_lock_private(pri->pvts[rsp_chanpos]); + pri->pvts[rsp_chanpos]->xfer_data = NULL; + sig_pri_unlock_private(pri->pvts[rsp_chanpos]); + } + rsp_chanpos = pri_find_principle_by_call(pri, call_2->pri); + if (0 <= rsp_chanpos) { + sig_pri_lock_private(pri->pvts[rsp_chanpos]); + pri->pvts[rsp_chanpos]->xfer_data = NULL; + sig_pri_unlock_private(pri->pvts[rsp_chanpos]); + } + + /* Report transfer status. */ + sig_pri_transfer_rsp(xfer_data, retval ? 0 : 1); } +#endif /* defined(HAVE_PRI_TRANSFER) */ + ast_channel_unref(call_1->ast); + ast_channel_unref(call_2->ast); return retval; } #endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */ @@ -4457,10 +4440,11 @@ static void sig_pri_handle_subcmds(struct sig_pri_span *pri, int chanpos, int ev xfer_rsp.pri = pri; xfer_rsp.call = call_rsp; xfer_rsp.invoke_id = subcmd->u.transfer.invoke_id; + xfer_rsp.responded = 0; 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, - sig_pri_transfer_rsp, &xfer_rsp); + &xfer_rsp); sig_pri_lock_private(pri->pvts[chanpos]); break; #endif /* defined(HAVE_PRI_TRANSFER) */ @@ -7114,7 +7098,7 @@ static void *pri_dchannel(void *vpri) /* 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_held, 1, - e->hangup.call_active, 0, NULL, NULL)) { + e->hangup.call_active, 0, NULL)) { break; } sig_pri_lock_private(pri->pvts[chanpos]); @@ -7595,6 +7579,20 @@ int sig_pri_hangup(struct sig_pri_chan *p, struct ast_channel *ast) } #endif /* defined(SUPPORT_USERUSER) */ +#if defined(HAVE_PRI_TRANSFER) + if (p->xfer_data) { + /* + * The transferrer call leg is disconnecting. It must mean that + * the transfer was successful and the core is disconnecting the + * call legs involved. + * + * The transfer protocol response message must go out before the + * call leg is disconnected. + */ + sig_pri_transfer_rsp(p->xfer_data, 1); + } +#endif /* defined(HAVE_PRI_TRANSFER) */ + #if defined(HAVE_PRI_AOC_EVENTS) if (p->holding_aoce) { pri_aoc_e_send(p->pri->pri, p->call, &p->aoc_e); @@ -7623,6 +7621,9 @@ int sig_pri_hangup(struct sig_pri_chan *p, struct ast_channel *ast) pri_hangup(p->pri->pri, p->call, icause); } } +#if defined(HAVE_PRI_TRANSFER) + p->xfer_data = NULL; +#endif /* defined(HAVE_PRI_TRANSFER) */ #if defined(HAVE_PRI_AOC_EVENTS) p->aoc_s_request_invoke_id_valid = 0; p->holding_aoce = 0; diff --git a/channels/sig_pri.h b/channels/sig_pri.h index 805ed9b48..62e0dc963 100644 --- a/channels/sig_pri.h +++ b/channels/sig_pri.h @@ -172,6 +172,7 @@ enum sig_pri_reset_state { }; struct sig_pri_span; +struct xfer_rsp_data; struct sig_pri_callback { /* Unlock the private in the signalling private structure. This is used for three way calling madness. */ @@ -353,6 +354,10 @@ struct sig_pri_chan { enum sig_pri_call_level call_level; /*! \brief Channel reset/restart state. */ enum sig_pri_reset_state resetting; +#if defined(HAVE_PRI_TRANSFER) + /*! If non-NULL, send transfer disconnect successfull response to first call disconnecting. */ + struct xfer_rsp_data *xfer_data; +#endif /* defined(HAVE_PRI_TRANSFER) */ int prioffset; /*!< channel number in span */ int logicalspan; /*!< logical span number within trunk group */ int mastertrunkgroup; /*!< what trunk group is our master */ -- cgit v1.2.3