/*!
* \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.
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:
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;
}
#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) */
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;
}
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) */
&& 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]);
}
ast_indicate(chan, -1);
- chan->visible_indication = 0;
return res;
}
case AST_CONTROL_RINGING:
case AST_CONTROL_RING:
case AST_CONTROL_HOLD:
- case AST_CONTROL_UNHOLD:
+ /* You can hear these */
return 1;
+
+ case AST_CONTROL_UNHOLD:
+ /* This is a special case. You stop hearing this. */
+ break;
}
return 0;
/* Don't bother if the channel is about to go away, anyway. */
if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
- ast_channel_unlock(chan);
res = -1;
goto indicate_cleanup;
}
/* who knows what we will get back! the anticipation is killing me. */
if (!(awesome_frame = ast_framehook_list_read_event(chan->framehooks, &frame))) {
- ast_channel_unlock(chan);
res = 0;
goto indicate_cleanup;
}
break;
}
+ if (is_visible_indication(condition)) {
+ /* A new visible indication is requested. */
+ chan->visible_indication = condition;
+ } else if (condition == AST_CONTROL_UNHOLD || _condition < 0) {
+ /* Visible indication is cleared/stopped. */
+ chan->visible_indication = 0;
+ }
+
if (chan->tech->indicate) {
/* See if the channel driver can handle this condition. */
res = chan->tech->indicate(chan, condition, data, datalen);
res = -1;
}
- ast_channel_unlock(chan);
-
if (!res) {
/* The channel driver successfully handled this indication */
- if (is_visible_indication(condition)) {
- chan->visible_indication = condition;
- }
res = 0;
goto indicate_cleanup;
}
ast_playtones_start(chan, 0, ts->data, 1);
ts = ast_tone_zone_sound_unref(ts);
res = 0;
- chan->visible_indication = condition;
}
if (res) {
}
indicate_cleanup:
+ ast_channel_unlock(chan);
if (awesome_frame) {
ast_frfree(awesome_frame);
}
int i;
int res=0;
int origstate;
+ int visible_indication;
struct ast_frame *current;
const struct ast_channel_tech *t;
void *t_pvt;
ast_debug(4, "Actually Masquerading %s(%d) into the structure of %s(%d)\n",
clonechan->name, clonechan->_state, original->name, original->_state);
+ /*
+ * Stop any visible indiction on the original channel so we can
+ * transfer it to the clonechan taking the original's place.
+ */
+ visible_indication = original->visible_indication;
+ ast_indicate(original, -1);
+
chans[0] = clonechan;
chans[1] = original;
- ast_manager_event_multichan(EVENT_FLAG_CALL, "Masquerade", 2, chans, "Clone: %s\r\nCloneState: %s\r\nOriginal: %s\r\nOriginalState: %s\r\n",
- clonechan->name, ast_state2str(clonechan->_state), original->name, ast_state2str(original->_state));
+ ast_manager_event_multichan(EVENT_FLAG_CALL, "Masquerade", 2, chans,
+ "Clone: %s\r\n"
+ "CloneState: %s\r\n"
+ "Original: %s\r\n"
+ "OriginalState: %s\r\n",
+ clonechan->name, ast_state2str(clonechan->_state), original->name, ast_state2str(original->_state));
/* Having remembered the original read/write formats, we turn off any translation on either
one */
* of this channel, and the new channel private data needs to be made
* aware of the current visible indication (RINGING, CONGESTION, etc.)
*/
- if (original->visible_indication) {
- ast_indicate(original, original->visible_indication);
+ if (visible_indication) {
+ ast_indicate(original, visible_indication);
}
/* Now, at this point, the "clone" channel is totally F'd up. We mark it as