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
changes/78/78/1
Richard Mudgett 13 years ago
parent b8b7e8ab45
commit 6114166237

@ -131,6 +131,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/ccss.h" #include "asterisk/ccss.h"
#include "asterisk/data.h" #include "asterisk/data.h"
#include "asterisk/features_config.h" #include "asterisk/features_config.h"
#include "asterisk/bridging.h"
/*** DOCUMENTATION /*** DOCUMENTATION
<application name="DAHDISendKeypadFacility" language="en_US"> <application name="DAHDISendKeypadFacility" language="en_US">
@ -7841,55 +7842,49 @@ static int dahdi_ring_phone(struct dahdi_pvt *p)
static void *analog_ss_thread(void *data); 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) static int attempt_transfer(struct dahdi_pvt *p)
{ {
/* In order to transfer, we need at least one of the channels to struct ast_channel *owner_real;
actually be in a call bridge. We can't conference two applications struct ast_channel *owner_3way;
together (but then, why would we want to?) */ enum ast_transfer_result xfer_res;
if (ast_bridged_channel(p->subs[SUB_REAL].owner)) { int res = 0;
/* The three-way person we're about to transfer to could still be in MOH, so
stop it now */ owner_real = ast_channel_ref(p->subs[SUB_REAL].owner);
ast_queue_unhold(p->subs[SUB_THREEWAY].owner); owner_3way = ast_channel_ref(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); ast_verb(3, "TRANSFERRING %s to %s\n",
} ast_channel_name(owner_3way), ast_channel_name(owner_real));
if (ast_channel_state(p->subs[SUB_THREEWAY].owner) == AST_STATE_RING) {
tone_zone_play_tone(p->subs[SUB_THREEWAY].dfd, DAHDI_TONE_RINGTONE); ast_channel_unlock(owner_real);
} ast_channel_unlock(owner_3way);
if (ast_channel_masquerade(p->subs[SUB_THREEWAY].owner, ast_bridged_channel(p->subs[SUB_REAL].owner))) { ast_mutex_unlock(&p->lock);
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)); xfer_res = ast_bridge_transfer_attended(owner_3way, owner_real);
return -1; if (xfer_res != AST_BRIDGE_TRANSFER_SUCCESS) {
} ast_softhangup(owner_3way, AST_SOFTHANGUP_DEV);
/* Orphan the channel after releasing the lock */ res = -1;
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;
} }
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) 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; p->owner = NULL;
/* Ring the phone */ /* Ring the phone */
dahdi_ring_phone(p); dahdi_ring_phone(p);
} else { } else if (!attempt_transfer(p)) {
if ((res = attempt_transfer(p)) < 0) { /*
ast_channel_softhangup_internal_flag_add(p->subs[SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV); * Transfer successful. Don't actually hang up at this point.
if (p->subs[SUB_THREEWAY].owner) * Let our channel legs of the calls die off as the transfer
ast_channel_unlock(p->subs[SUB_THREEWAY].owner); * percolates through the core.
} else if (res) { */
/* Don't actually hang up at this point */ break;
if (p->subs[SUB_THREEWAY].owner)
ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
break;
}
} }
} else { } else {
ast_channel_softhangup_internal_flag_add(p->subs[SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV); ast_channel_softhangup_internal_flag_add(p->subs[SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);

@ -103,6 +103,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/format.h" #include "asterisk/format.h"
#include "asterisk/format_cap.h" #include "asterisk/format_cap.h"
#include "asterisk/features_config.h" #include "asterisk/features_config.h"
#include "asterisk/bridging.h"
#include "chan_misdn_config.h" #include "chan_misdn_config.h"
#include "isdn_lib.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) static int misdn_attempt_transfer(struct chan_list *active_ch, struct chan_list *held_ch)
{ {
int retval; int retval;
struct ast_channel *target; enum ast_transfer_result xfer_res;
struct ast_channel *transferee; struct ast_channel *to_target;
struct ast_party_connected_line target_colp; struct ast_channel *to_transferee;
struct ast_party_connected_line transferee_colp;
switch (active_ch->state) { switch (active_ch->state) {
case MISDN_PROCEEDING: 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); ast_channel_lock_both(held_ch->ast, active_ch->ast);
to_target = active_ch->ast;
transferee = ast_bridged_channel(held_ch->ast); to_transferee = 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;
chan_misdn_log(1, held_ch->hold.port, "TRANSFERRING %s to %s\n", chan_misdn_log(1, held_ch->hold.port, "TRANSFERRING %s to %s\n",
ast_channel_name(held_ch->ast), ast_channel_name(target)); ast_channel_name(to_transferee), ast_channel_name(to_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);
held_ch->hold.state = MISDN_HOLD_TRANSFER; 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);
/* retval = 0;
* Before starting a masquerade, all channel and pvt locks must xfer_res = ast_bridge_transfer_attended(to_transferee, to_target);
* be unlocked. Any recursive channel locks held before if (xfer_res != AST_BRIDGE_TRANSFER_SUCCESS) {
* ast_channel_transfer_masquerade() invalidates deadlock retval = -1;
* 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 ast_channel_unref(to_target);
* "target" and "transferee" a reference before any unlocking ast_channel_unref(to_transferee);
* 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);
return retval; return retval;
} }

@ -44,6 +44,7 @@
#include "asterisk/cel.h" #include "asterisk/cel.h"
#include "asterisk/causes.h" #include "asterisk/causes.h"
#include "asterisk/features_config.h" #include "asterisk/features_config.h"
#include "asterisk/bridging.h"
#include "sig_analog.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. * \brief Attempt to transfer 3-way call.
* *
* \param p Analog private structure. * \param p Analog private structure.
* \param inthreeway TRUE if the 3-way call is conferenced.
* *
* \note * \note On entry these locks are held: real-call, private, 3-way call.
* 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. * \retval 0 on success.
* Swapped real and 3-way subchannel. * \retval -1 on error.
* \retval 0 Transfer successful. 3-way call is unlocked and subchannel is unalloced.
* \retval -1 on error. Caller must unlock 3-way call.
*/ */
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_real;
struct ast_channel *owner_3way; struct ast_channel *owner_3way;
struct ast_channel *bridge_real; enum ast_transfer_result xfer_res;
struct ast_channel *bridge_3way; int res = 0;
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);
/* owner_real = ast_channel_ref(p->subs[ANALOG_SUB_REAL].owner);
* The three-way party we're about to transfer is on hold if he owner_3way = ast_channel_ref(p->subs[ANALOG_SUB_THREEWAY].owner);
* 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;
}
/* Three-way is now the REAL */ ast_verb(3, "TRANSFERRING %s to %s\n",
analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL); ast_channel_name(owner_3way), ast_channel_name(owner_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_channel_unlock(owner_real);
* The three-way party we're about to transfer is on hold if he ast_channel_unlock(owner_3way);
* is not in a three way conference. analog_unlock_private(p);
*/
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;
}
/* Orphan the channel after releasing the lock */ xfer_res = ast_bridge_transfer_attended(owner_3way, owner_real);
ast_channel_unlock(owner_3way); if (xfer_res != AST_BRIDGE_TRANSFER_SUCCESS) {
analog_unalloc_sub(p, ANALOG_SUB_THREEWAY); ast_softhangup(owner_3way, AST_SOFTHANGUP_DEV);
return 0; res = -1;
} 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;
} }
/* 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) 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); ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
} else if ((ast_channel_pbx(ast)) || (ast_channel_state(ast) == AST_STATE_UP)) { } else if ((ast_channel_pbx(ast)) || (ast_channel_state(ast) == AST_STATE_UP)) {
if (p->transfer) { if (p->transfer) {
int inthreeway;
inthreeway = p->subs[ANALOG_SUB_THREEWAY].inthreeway;
/* In any case this isn't a threeway call anymore */ /* In any case this isn't a threeway call anymore */
analog_set_inthreeway(p, ANALOG_SUB_REAL, 0); analog_set_inthreeway(p, ANALOG_SUB_REAL, 0);
analog_set_inthreeway(p, ANALOG_SUB_THREEWAY, 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); analog_set_new_owner(p, NULL);
/* Ring the phone */ /* Ring the phone */
analog_ring(p); analog_ring(p);
} else { } else if (!analog_attempt_transfer(p)) {
res = analog_attempt_transfer(p, inthreeway); /*
if (res < 0) { * Transfer successful. Don't actually hang up at this point.
/* Transfer attempt failed. */ * Let our channel legs of the calls die off as the transfer
ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV); * percolates through the core.
ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); */
} else if (res) { break;
/* Don't actually hang up at this point */
break;
}
} }
} else { } else {
ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV); ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);

@ -50,6 +50,7 @@
#include "asterisk/transcap.h" #include "asterisk/transcap.h"
#include "asterisk/features.h" #include "asterisk/features.h"
#include "asterisk/aoc.h" #include "asterisk/aoc.h"
#include "asterisk/bridging.h"
#include "sig_pri.h" #include "sig_pri.h"
#ifndef PRI_EVENT_FACILITY #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); strcpy(new_chan->moh_suggested, old_chan->moh_suggested);
new_chan->moh_state = old_chan->moh_state; new_chan->moh_state = old_chan->moh_state;
old_chan->moh_state = SIG_PRI_MOH_STATE_IDLE; 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) #if defined(HAVE_PRI_AOC_EVENTS)
new_chan->aoc_s_request_invoke_id = old_chan->aoc_s_request_invoke_id; 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; q931_call *call;
/*! Invocation ID to use when sending a reply to the transfer request. */ /*! Invocation ID to use when sending a reply to the transfer request. */
int invoke_id; int invoke_id;
/*! TRUE if the transfer response has been made. */
int responded;
}; };
#endif /* defined(HAVE_PRI_TRANSFER) */ #endif /* defined(HAVE_PRI_TRANSFER) */
@ -2405,32 +2412,24 @@ struct xfer_rsp_data {
* \brief Send the transfer success/fail response message. * \brief Send the transfer success/fail response message.
* \since 1.8 * \since 1.8
* *
* \param data Callback user data pointer * \param rsp Transfer response data.
* \param is_successful TRUE if the transfer was successful. * \param is_successful TRUE if the transfer was successful.
* *
* \note Assumes the rsp->pri->lock is already obtained.
*
* \return Nothing * \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); pri_transfer_rsp(rsp->pri->pri, rsp->call, rsp->invoke_id, is_successful);
} }
#endif /* defined(HAVE_PRI_TRANSFER) */ #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) #if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER)
/*! /*!
* \internal * \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_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_pri Second call involved in the transfer. (target; usually active/ringing)
* \param call_2_held TRUE if call_2_pri is on hold. * \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 xfer_data Transfer response data if non-NULL.
* \param data Callback user data pointer
* *
* \note Assumes the pri->lock is already obtained. * \note Assumes the pri->lock is already obtained.
* *
* \retval 0 on success. * \retval 0 on success.
* \retval -1 on error. * \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 { struct attempt_xfer_call {
q931_call *pri; 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 chanpos;
}; };
int retval; int retval;
struct ast_channel *transferee; enum ast_transfer_result xfer_res;
struct attempt_xfer_call *call_1; struct attempt_xfer_call *call_1;
struct attempt_xfer_call *call_2; struct attempt_xfer_call *call_2;
struct attempt_xfer_call *swap_call;
struct attempt_xfer_call c1; struct attempt_xfer_call c1;
struct attempt_xfer_call c2; 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); call_2->chanpos = pri_find_principle_by_call(pri, call_2->pri);
if (call_1->chanpos < 0 || call_2->chanpos < 0) { if (call_1->chanpos < 0 || call_2->chanpos < 0) {
/* Calls not found in span control. */ /* Calls not found in span control. */
if (rsp_callback) { #if defined(HAVE_PRI_TRANSFER)
if (xfer_data) {
/* Transfer failed. */ /* Transfer failed. */
rsp_callback(data, 0); sig_pri_transfer_rsp(xfer_data, 0);
} }
#endif /* defined(HAVE_PRI_TRANSFER) */
return -1; return -1;
} }
/* Attempt to make transferee and target consistent. */ /* Get call_1 owner. */
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. */
sig_pri_lock_private(pri->pvts[call_1->chanpos]); sig_pri_lock_private(pri->pvts[call_1->chanpos]);
sig_pri_lock_owner(pri, 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_private(pri->pvts[call_2->chanpos]);
sig_pri_lock_owner(pri, 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; 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) { if (!call_1->ast || !call_2->ast) {
/* At least one owner is not present. */ /* At least one owner is not present. */
if (call_1->ast) { if (call_1->ast) {
ast_channel_unlock(call_1->ast); ast_channel_unref(call_1->ast);
} }
if (call_2->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]); #if defined(HAVE_PRI_TRANSFER)
sig_pri_unlock_private(pri->pvts[call_2->chanpos]); if (xfer_data) {
if (rsp_callback) {
/* Transfer failed. */ /* Transfer failed. */
rsp_callback(data, 0); sig_pri_transfer_rsp(xfer_data, 0);
} }
#endif /* defined(HAVE_PRI_TRANSFER) */
return -1; return -1;
} }
for (;;) { ast_verb(3, "TRANSFERRING %s to %s\n",
transferee = ast_bridged_channel(call_1->ast); ast_channel_name(call_1->ast), ast_channel_name(call_2->ast));
if (transferee) {
break;
}
/* Try masquerading the other way. */
swap_call = call_1;
call_1 = call_2;
call_2 = swap_call;
transferee = ast_bridged_channel(call_1->ast); #if defined(HAVE_PRI_TRANSFER)
if (transferee) { if (xfer_data) {
break; /*
} * Add traps on the transferer channels in case threading causes
* them to hangup before ast_bridge_transfer_attended() returns
/* Could not transfer. Neither call is bridged. */ * and we can get the pri->lock back.
ast_channel_unlock(call_1->ast); */
ast_channel_unlock(call_2->ast); 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_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]); 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); ast_mutex_unlock(&pri->lock);
retval = ast_channel_transfer_masquerade( xfer_res = ast_bridge_transfer_attended(call_1->ast, call_2->ast);
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. */
ast_mutex_lock(&pri->lock); ast_mutex_lock(&pri->lock);
retval = (xfer_res != AST_BRIDGE_TRANSFER_SUCCESS) ? -1 : 0;
ast_channel_unlock(call_1->ast); #if defined(HAVE_PRI_TRANSFER)
ast_channel_unlock(call_2->ast); if (xfer_data) {
sig_pri_unlock_private(pri->pvts[call_1->chanpos]); int rsp_chanpos;
sig_pri_unlock_private(pri->pvts[call_2->chanpos]);
if (rsp_callback) {
/* /*
* Report transfer status. * Remove the transferrer channel traps.
* *
* Must do the callback before the masquerade completes to ensure * We must refind chanpos because we released pri->lock.
* that the protocol message goes out before the call leg is
* disconnected.
*/ */
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; return retval;
} }
#endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */ #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.pri = pri;
xfer_rsp.call = call_rsp; xfer_rsp.call = call_rsp;
xfer_rsp.invoke_id = subcmd->u.transfer.invoke_id; xfer_rsp.invoke_id = subcmd->u.transfer.invoke_id;
xfer_rsp.responded = 0;
sig_pri_attempt_transfer(pri, sig_pri_attempt_transfer(pri,
subcmd->u.transfer.call_1, subcmd->u.transfer.is_call_1_held, subcmd->u.transfer.call_1, subcmd->u.transfer.is_call_1_held,
subcmd->u.transfer.call_2, subcmd->u.transfer.is_call_2_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]); sig_pri_lock_private(pri->pvts[chanpos]);
break; break;
#endif /* defined(HAVE_PRI_TRANSFER) */ #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. */ /* We are to transfer the call instead of simply hanging up. */
sig_pri_unlock_private(pri->pvts[chanpos]); sig_pri_unlock_private(pri->pvts[chanpos]);
if (!sig_pri_attempt_transfer(pri, e->hangup.call_held, 1, 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; break;
} }
sig_pri_lock_private(pri->pvts[chanpos]); 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) */ #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 defined(HAVE_PRI_AOC_EVENTS)
if (p->holding_aoce) { if (p->holding_aoce) {
pri_aoc_e_send(p->pri->pri, p->call, &p->aoc_e); 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); 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) #if defined(HAVE_PRI_AOC_EVENTS)
p->aoc_s_request_invoke_id_valid = 0; p->aoc_s_request_invoke_id_valid = 0;
p->holding_aoce = 0; p->holding_aoce = 0;

@ -172,6 +172,7 @@ enum sig_pri_reset_state {
}; };
struct sig_pri_span; struct sig_pri_span;
struct xfer_rsp_data;
struct sig_pri_callback { struct sig_pri_callback {
/* Unlock the private in the signalling private structure. This is used for three way calling madness. */ /* 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; enum sig_pri_call_level call_level;
/*! \brief Channel reset/restart state. */ /*! \brief Channel reset/restart state. */
enum sig_pri_reset_state resetting; 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 prioffset; /*!< channel number in span */
int logicalspan; /*!< logical span number within trunk group */ int logicalspan; /*!< logical span number within trunk group */
int mastertrunkgroup; /*!< what trunk group is our master */ int mastertrunkgroup; /*!< what trunk group is our master */

@ -6384,6 +6384,12 @@ static const struct ast_datastore_info xfer_ds_info = {
.destroy = xfer_ds_destroy, .destroy = xfer_ds_destroy,
}; };
/*
* BUGBUG ast_channel_transfer_masquerade() can be deleted when bridging COLP has been reimplemented.
*
* ast_bridge_transfer_attended() will need to do something like
* this when it has to do a masquerade into an application.
*/
int ast_channel_transfer_masquerade( int ast_channel_transfer_masquerade(
struct ast_channel *target_chan, struct ast_channel *target_chan,
const struct ast_party_connected_line *target_id, const struct ast_party_connected_line *target_id,

Loading…
Cancel
Save