aboutsummaryrefslogtreecommitdiffstats
path: root/channels
diff options
context:
space:
mode:
authorrmudgett <rmudgett@f38db490-d61c-443f-a65b-d21fe96a405b>2010-10-22 15:47:08 +0000
committerrmudgett <rmudgett@f38db490-d61c-443f-a65b-d21fe96a405b>2010-10-22 15:47:08 +0000
commitad8c494ae75dc63817b90b89e964a21b80df7329 (patch)
treee75ffecb515606ac3aef8133c4e91cc37557e6a0 /channels
parent528c9579e5a42b9559cd9db8e06588405f4723fc (diff)
Connected line is not updated when chan_dahdi/sig_pri or chan_misdn transfers a call.
When a call is transfered by ECT or implicitly by disconnect in sig_pri or implicitly by disconnect in chan_misdn, the connected line information is not exchanged. The connected line interception macros also need to be executed if defined. The CALLER interception macro is executed for the held call. The CALLEE interception macro is executed for the active/ringing call. JIRA ABE-2589 JIRA SWP-2296 Patches: abe_2589_c3bier.patch uploaded by rmudgett (license 664) abe_2589_v1.8_v2.patch uploaded by rmudgett (license 664) Review: https://reviewboard.asterisk.org/r/958/ git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.8@292704 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'channels')
-rw-r--r--channels/chan_misdn.c152
-rw-r--r--channels/sig_pri.c383
2 files changed, 455 insertions, 80 deletions
diff --git a/channels/chan_misdn.c b/channels/chan_misdn.c
index a6d623e7a..35ac69d06 100644
--- a/channels/chan_misdn.c
+++ b/channels/chan_misdn.c
@@ -8497,6 +8497,40 @@ static void release_chan_early(struct chan_list *ch)
/*!
* \internal
+ * \brief Copy the source connected line information to the destination for a transfer.
+ * \since 1.8
+ *
+ * \param dest Destination connected line
+ * \param src Source connected line
+ *
+ * \return Nothing
+ */
+static void misdn_connected_line_copy_transfer(struct ast_party_connected_line *dest, struct ast_party_connected_line *src)
+{
+ struct ast_party_connected_line connected;
+
+ connected = *src;
+ connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
+
+ /* Make sure empty strings will be erased. */
+ if (!connected.id.name.str) {
+ connected.id.name.str = "";
+ }
+ if (!connected.id.number.str) {
+ connected.id.number.str = "";
+ }
+ if (!connected.id.subaddress.str) {
+ connected.id.subaddress.str = "";
+ }
+ if (!connected.id.tag) {
+ connected.id.tag = "";
+ }
+
+ ast_party_connected_line_copy(dest, &connected);
+}
+
+/*!
+ * \internal
* \brief Attempt to transfer the active channel party to the held channel party.
*
* \param active_ch Channel currently connected.
@@ -8508,7 +8542,10 @@ 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 *bridged;
+ struct ast_channel *target;
+ struct ast_channel *transferee;
+ struct ast_party_connected_line target_colp;
+ struct ast_party_connected_line transferee_colp;
switch (active_ch->state) {
case MISDN_PROCEEDING:
@@ -8520,23 +8557,116 @@ static int misdn_attempt_transfer(struct chan_list *active_ch, struct chan_list
return -1;
}
- bridged = ast_bridged_channel(held_ch->ast);
- if (bridged) {
- ast_queue_control(held_ch->ast, AST_CONTROL_UNHOLD);
- held_ch->hold.state = MISDN_HOLD_TRANSFER;
+ ast_channel_lock(held_ch->ast);
+ while (ast_channel_trylock(active_ch->ast)) {
+ CHANNEL_DEADLOCK_AVOIDANCE(held_ch->ast);
+ }
- chan_misdn_log(1, held_ch->hold.port, "TRANSFERRING %s to %s\n",
- held_ch->ast->name, active_ch->ast->name);
- retval = ast_channel_masquerade(active_ch->ast, bridged);
- } else {
+ 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.
*/
- retval = -1;
+ ast_channel_unlock(held_ch->ast);
+ ast_channel_unlock(active_ch->ast);
+ return -1;
}
- return retval;
+ target = active_ch->ast;
+ chan_misdn_log(1, held_ch->hold.port, "TRANSFERRING %s to %s\n",
+ held_ch->ast->name, target->name);
+
+ ast_party_connected_line_init(&target_colp);
+ misdn_connected_line_copy_transfer(&target_colp, &target->connected);
+ ast_party_connected_line_init(&transferee_colp);
+ misdn_connected_line_copy_transfer(&transferee_colp, &held_ch->ast->connected);
+ held_ch->hold.state = MISDN_HOLD_TRANSFER;
+
+ /*
+ * Before starting a masquerade, all channel and pvt locks must
+ * be unlocked. Any recursive channel locks held before
+ * ast_channel_masquerade() invalidates deadlock avoidance. Any
+ * recursive channel locks held before ast_do_masquerade()
+ * invalidates channel container locking order. 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);
+
+ /* Release hold on the transferee channel. */
+ ast_indicate(transferee, AST_CONTROL_UNHOLD);
+
+ /* Setup transfer masquerade. */
+ retval = ast_channel_masquerade(target, transferee);
+ if (retval) {
+ /* Masquerade setup failed. */
+ ast_party_connected_line_free(&target_colp);
+ ast_party_connected_line_free(&transferee_colp);
+ ao2_ref(target, -1);
+ ao2_ref(transferee, -1);
+ return -1;
+ }
+ ao2_ref(transferee, -1);
+
+ /*
+ * Make sure masquerade is complete.
+ *
+ * After the masquerade, the "target" channel pointer actually
+ * points to the new transferee channel and the bridged channel
+ * is still the intended target of the transfer.
+ *
+ * By manually completing the masquerade, we can send connected
+ * line updates where they need to go.
+ */
+ ast_do_masquerade(target);
+
+ /* Transfer COLP between target and transferee channels. */
+ {
+ /*
+ * Since "target" may not actually be bridged to another
+ * channel, there is no way for us to queue a frame so that its
+ * connected line status will be updated. Instead, we use the
+ * somewhat hackish approach of using a special control frame
+ * type that instructs ast_read() to perform a specific action.
+ * In this case, the frame we queue tells ast_read() to call the
+ * connected line interception macro configured for "target".
+ */
+ struct ast_control_read_action_payload *frame_payload;
+ int payload_size;
+ int frame_size;
+ unsigned char connected_line_data[1024];
+
+ payload_size = ast_connected_line_build_data(connected_line_data,
+ sizeof(connected_line_data), &target_colp, NULL);
+ if (payload_size != -1) {
+ frame_size = payload_size + sizeof(*frame_payload);
+ frame_payload = alloca(frame_size);
+ frame_payload->action = AST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO;
+ frame_payload->payload_size = payload_size;
+ memcpy(frame_payload->payload, connected_line_data, payload_size);
+ ast_queue_control_data(target, AST_CONTROL_READ_ACTION, frame_payload,
+ frame_size);
+ }
+ /*
+ * In addition to queueing the read action frame so that the
+ * connected line info on "target" will be updated, we also
+ * are going to queue a plain old connected line update on
+ * "target" to update the target channel.
+ */
+ ast_channel_queue_connected_line_update(target, &transferee_colp, NULL);
+ }
+
+ ast_party_connected_line_free(&target_colp);
+ ast_party_connected_line_free(&transferee_colp);
+
+ ao2_ref(target, -1);
+ return 0;
}
diff --git a/channels/sig_pri.c b/channels/sig_pri.c
index bab7ae4d2..d0cfb46ce 100644
--- a/channels/sig_pri.c
+++ b/channels/sig_pri.c
@@ -1885,100 +1885,340 @@ static void sig_pri_mcid_event(struct sig_pri_span *pri, const struct pri_subcmd
#if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER)
/*!
* \internal
+ * \brief Copy the source connected line information to the destination for a transfer.
+ * \since 1.8
+ *
+ * \param dest Destination connected line
+ * \param src Source connected line
+ *
+ * \return Nothing
+ */
+static void sig_pri_connected_line_copy_transfer(struct ast_party_connected_line *dest, struct ast_party_connected_line *src)
+{
+ struct ast_party_connected_line connected;
+
+ connected = *src;
+ connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
+
+ /* Make sure empty strings will be erased. */
+ if (!connected.id.name.str) {
+ connected.id.name.str = "";
+ }
+ if (!connected.id.number.str) {
+ connected.id.number.str = "";
+ }
+ if (!connected.id.subaddress.str) {
+ connected.id.subaddress.str = "";
+ }
+ if (!connected.id.tag) {
+ connected.id.tag = "";
+ }
+
+ ast_party_connected_line_copy(dest, &connected);
+}
+#endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
+
+#if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER)
+struct xfer_rsp_data {
+ struct sig_pri_span *pri;
+ /*! Call to send transfer success/fail response over. */
+ q931_call *call;
+ /*! Invocation ID to use when sending a reply to the transfer request. */
+ int invoke_id;
+};
+#endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
+
+#if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER)
+/*!
+ * \internal
+ * \brief Send the transfer success/fail response message.
+ * \since 1.8
+ *
+ * \param data Callback user data pointer
+ * \param is_successful TRUE if the transfer was successful.
+ *
+ * \return Nothing
+ */
+static void sig_pri_transfer_rsp(void *data, int is_successful)
+{
+ struct xfer_rsp_data *rsp = data;
+
+ pri_transfer_rsp(rsp->pri->pri, rsp->call, rsp->invoke_id, is_successful);
+}
+#endif /* defined(HAVE_PRI_CALL_HOLD) || 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
* \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.
+ * \param call_1_pri First call involved in the transfer. (transferee; usually 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_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
*
* \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, int call_1_held, q931_call *call_2, int call_2_held)
+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)
{
+ struct attempt_xfer_call {
+ q931_call *pri;
+ struct ast_channel *ast;
+ int held;
+ int chanpos;
+ };
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) {
+ int transferee_is_held;
+ struct ast_channel *transferee;
+ struct attempt_xfer_call *call_1;
+ struct attempt_xfer_call *call_2;
+ struct attempt_xfer_call *target;
+ struct attempt_xfer_call *swap_call;
+ struct attempt_xfer_call c1;
+ struct attempt_xfer_call c2;
+ struct ast_party_connected_line target_colp;
+ struct ast_party_connected_line transferee_colp;
+
+ c1.pri = call_1_pri;
+ c1.held = call_1_held;
+ call_1 = &c1;
+
+ c2.pri = call_2_pri;
+ c2.held = call_2_held;
+ call_2 = &c2;
+
+ call_1->chanpos = pri_find_pri_call(pri, call_1->pri);
+ call_2->chanpos = pri_find_pri_call(pri, call_2->pri);
+ if (call_1->chanpos < 0 || call_2->chanpos < 0) {
+ /* Calls not found in span control. */
+ if (rsp_callback) {
+ /* Transfer failed. */
+ rsp_callback(data, 0);
+ }
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. */
- 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);
+ 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) {
+ /* At least one owner is not present. */
+ if (call_1->ast) {
+ ast_channel_unlock(call_1->ast);
+ }
+ if (call_2->ast) {
+ ast_channel_unlock(call_2->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]);
+ if (rsp_callback) {
+ /* Transfer failed. */
+ rsp_callback(data, 0);
}
- 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);
+ for (;;) {
+ transferee = ast_bridged_channel(call_1->ast);
+ if (transferee) {
+ break;
}
- 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);
- }
+ swap_call = call_1;
+ call_1 = call_2;
+ call_2 = swap_call;
- 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;
+ 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);
+ sig_pri_unlock_private(pri->pvts[call_1->chanpos]);
+ sig_pri_unlock_private(pri->pvts[call_2->chanpos]);
+
+ if (rsp_callback) {
+ /* Transfer failed. */
+ rsp_callback(data, 0);
+ }
+ return -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);
+
+ target = call_2;
+ ast_verb(3, "TRANSFERRING %s to %s\n", call_1->ast->name, target->ast->name);
+
+ ast_party_connected_line_init(&target_colp);
+ sig_pri_connected_line_copy_transfer(&target_colp, &target->ast->connected);
+ ast_party_connected_line_init(&transferee_colp);
+ sig_pri_connected_line_copy_transfer(&transferee_colp, &call_1->ast->connected);
+ transferee_is_held = call_1->held;
+
+ /*
+ * Setup transfer masquerade.
+ *
+ * Note: There is an extremely nasty deadlock avoidance issue
+ * with ast_channel_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_masquerade(target->ast, transferee);
+ if (retval) {
+ /* Masquerade setup failed. */
+ ast_party_connected_line_free(&target_colp);
+ ast_party_connected_line_free(&transferee_colp);
+
+ ast_mutex_lock(&pri->lock);
+
+ 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 (rsp_callback) {
+ /* Transfer failed. */
+ rsp_callback(data, 0);
}
- if (call_2_held) {
- ast_queue_control(call_2_ast, AST_CONTROL_HOLD);
+ return -1;
+ }
+
+ /*
+ * Release any hold on the transferee channel before allowing
+ * the masquerade to happen.
+ */
+ if (transferee_is_held) {
+ ast_indicate(transferee, AST_CONTROL_UNHOLD);
+ }
+ ast_mutex_lock(&pri->lock);
+
+ /*
+ * Before manually completing a masquerade, all channel and pvt
+ * locks must be unlocked. Any recursive channel locks held
+ * before ast_do_masquerade() invalidates channel container
+ * locking order. Since we are unlocking both the pvt and its
+ * owner channel it is possible for "target" to be destroyed
+ * in the pbx thread. To prevent this we must give "target"
+ * a reference before any unlocking takes place.
+ */
+ ao2_ref(target->ast, +1);
+ 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 (rsp_callback) {
+ /*
+ * Transfer successful.
+ *
+ * Must do the callback before releasing the pri->lock to ensure
+ * that the protocol message goes out before the call leg is
+ * disconnected.
+ */
+ rsp_callback(data, 1);
+ }
+
+ /*
+ * Make sure masquerade is complete.
+ *
+ * After the masquerade, the "target" channel pointer actually
+ * points to the new transferee channel and the bridged channel
+ * is still the intended target of the transfer.
+ *
+ * By manually completing the masquerade, we can send the unhold
+ * and connected line updates where they need to go.
+ */
+ ast_mutex_unlock(&pri->lock);
+ ast_do_masquerade(target->ast);
+
+ /* Release any hold on the target. */
+ if (target->held) {
+ ast_queue_control(target->ast, AST_CONTROL_UNHOLD);
+ }
+
+ /* Transfer COLP between target and transferee channels. */
+ {
+ /*
+ * Since "target" may not actually be bridged to another
+ * channel, there is no way for us to queue a frame so that its
+ * connected line status will be updated. Instead, we use the
+ * somewhat hackish approach of using a special control frame
+ * type that instructs ast_read() to perform a specific action.
+ * In this case, the frame we queue tells ast_read() to call the
+ * connected line interception macro configured for "target".
+ */
+ struct ast_control_read_action_payload *frame_payload;
+ int payload_size;
+ int frame_size;
+ unsigned char connected_line_data[1024];
+
+ payload_size = ast_connected_line_build_data(connected_line_data,
+ sizeof(connected_line_data), &target_colp, NULL);
+ if (payload_size != -1) {
+ frame_size = payload_size + sizeof(*frame_payload);
+ frame_payload = alloca(frame_size);
+ frame_payload->action = AST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO;
+ frame_payload->payload_size = payload_size;
+ memcpy(frame_payload->payload, connected_line_data, payload_size);
+ ast_queue_control_data(target->ast, AST_CONTROL_READ_ACTION, frame_payload,
+ frame_size);
}
+ /*
+ * In addition to queueing the read action frame so that the
+ * connected line info on "target" will be updated, we also
+ * are going to queue a plain old connected line update on
+ * "target" to update the target channel.
+ */
+ ast_channel_queue_connected_line_update(target->ast, &transferee_colp, NULL);
}
- 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]);
+ ast_party_connected_line_free(&target_colp);
+ ast_party_connected_line_free(&transferee_colp);
- return retval;
+ ao2_ref(target->ast, -1);
+ ast_mutex_lock(&pri->lock);
+ return 0;
}
#endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
@@ -3604,6 +3844,9 @@ static void sig_pri_handle_subcmds(struct sig_pri_span *pri, int chanpos, int ev
int index;
struct ast_channel *owner;
struct ast_party_redirecting ast_redirecting;
+#if defined(HAVE_PRI_TRANSFER)
+ struct xfer_rsp_data xfer_rsp;
+#endif /* defined(HAVE_PRI_TRANSFER) */
if (!subcmds) {
return;
@@ -3822,11 +4065,13 @@ static void sig_pri_handle_subcmds(struct sig_pri_span *pri, int chanpos, int ev
}
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);
+ xfer_rsp.pri = pri;
+ xfer_rsp.call = call_rsp;
+ xfer_rsp.invoke_id = 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,
+ sig_pri_transfer_rsp, &xfer_rsp);
sig_pri_lock_private(pri->pvts[chanpos]);
break;
#endif /* defined(HAVE_PRI_TRANSFER) */
@@ -5355,8 +5600,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, 0,
- e->hangup.call_held, 1)) {
+ if (!sig_pri_attempt_transfer(pri, e->hangup.call_held, 1,
+ e->hangup.call_active, 0, NULL, NULL)) {
break;
}
sig_pri_lock_private(pri->pvts[chanpos]);