aboutsummaryrefslogtreecommitdiffstats
path: root/channels
diff options
context:
space:
mode:
authorrmudgett <rmudgett@f38db490-d61c-443f-a65b-d21fe96a405b>2010-06-02 16:14:12 +0000
committerrmudgett <rmudgett@f38db490-d61c-443f-a65b-d21fe96a405b>2010-06-02 16:14:12 +0000
commitaa138b6e3f15826d199ecac0160d6ba2f21cec84 (patch)
tree35a07219840cd89e9f223a408a5bd77e900e18d0 /channels
parentb988f37ebc56d5a8dc6f95b7477da4e2efc3a3b0 (diff)
Add ETSI Explicit Call Transfer (ECT) support.
Added ability to send and receive ETSI Explicit Call Transfer (ECT) messages to eliminate tromboned calls. Note: Asterisk already supported initiating the transfer of calls to eliminate tromboned calls to libpri so there was nothing to do for the asterisk portion. Review: https://reviewboard.asterisk.org/r/520/ git-svn-id: http://svn.digium.com/svn/asterisk/trunk@266926 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'channels')
-rw-r--r--channels/chan_dahdi.c3
-rw-r--r--channels/sig_pri.c197
-rw-r--r--channels/sig_pri.h5
3 files changed, 130 insertions, 75 deletions
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index d8e7496ce..2d28c6fb0 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -6904,7 +6904,7 @@ static enum ast_bridge_result dahdi_bridge(struct ast_channel *c0, struct ast_ch
#ifdef PRI_2BCT
if (!triedtopribridge) {
triedtopribridge = 1;
- if (p0->pri && p0->pri == p1->pri && p0->transfer && p1->transfer) {
+ if (p0->pri && p0->pri == p1->pri && p0->pri->transfer) {
ast_mutex_lock(&p0->pri->lock);
switch (p0->sig) {
case SIG_PRI_LIB_HANDLE_CASES:
@@ -11777,6 +11777,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
pris[span].pri.cc_qsig_signaling_link_rsp =
conf->pri.pri.cc_qsig_signaling_link_rsp;
#endif /* defined(HAVE_PRI_CCSS) */
+ pris[span].pri.transfer = conf->chan.transfer;
pris[span].pri.facilityenable = conf->pri.pri.facilityenable;
ast_copy_string(pris[span].pri.msn_list, conf->pri.pri.msn_list, sizeof(pris[span].pri.msn_list));
ast_copy_string(pris[span].pri.idledial, conf->pri.pri.idledial, sizeof(pris[span].pri.idledial));
diff --git a/channels/sig_pri.c b/channels/sig_pri.c
index ae8f0ea32..846f8b055 100644
--- a/channels/sig_pri.c
+++ b/channels/sig_pri.c
@@ -1569,6 +1569,106 @@ static int sig_pri_msn_match(const char *msn_patterns, const char *exten)
return 0;
}
+#if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER)
+/*!
+ * \internal
+ * \brief Attempt to transfer the two calls to each other.
+ * \since 1.8
+ *
+ * \param pri sig_pri PRI control structure.
+ * \param call_1 First call involved in the transfer.
+ * \param call_1_held TRUE if call_1 is on hold.
+ * \param call_2 Second call involved in the transfer.
+ * \param call_2_held TRUE if call_2 is on hold.
+ *
+ * \note Assumes the pri->lock is already obtained.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int sig_pri_attempt_transfer(struct sig_pri_pri *pri, q931_call *call_1, int call_1_held, q931_call *call_2, int call_2_held)
+{
+ int retval;
+ int call_1_chanpos;
+ int call_2_chanpos;
+ struct ast_channel *call_1_ast;
+ struct ast_channel *call_2_ast;
+ struct ast_channel *bridged;
+
+ call_1_chanpos = pri_find_pri_call(pri, call_1);
+ call_2_chanpos = pri_find_pri_call(pri, call_2);
+ if (call_1_chanpos < 0 || call_2_chanpos < 0) {
+ return -1;
+ }
+
+ /* Deadlock avoidance is attempted. */
+ sig_pri_lock_private(pri->pvts[call_1_chanpos]);
+ sig_pri_lock_owner(pri, call_1_chanpos);
+ sig_pri_lock_private(pri->pvts[call_2_chanpos]);
+ sig_pri_lock_owner(pri, call_2_chanpos);
+
+ call_1_ast = pri->pvts[call_1_chanpos]->owner;
+ call_2_ast = pri->pvts[call_2_chanpos]->owner;
+ if (!call_1_ast || !call_2_ast) {
+ if (call_1_ast) {
+ ast_channel_unlock(call_1_ast);
+ }
+ if (call_2_ast) {
+ ast_channel_unlock(call_2_ast);
+ }
+ sig_pri_unlock_private(pri->pvts[call_1_chanpos]);
+ sig_pri_unlock_private(pri->pvts[call_2_chanpos]);
+ return -1;
+ }
+
+ bridged = ast_bridged_channel(call_2_ast);
+ if (bridged) {
+ if (call_1_held) {
+ ast_queue_control(call_1_ast, AST_CONTROL_UNHOLD);
+ }
+ if (call_2_held) {
+ ast_queue_control(call_2_ast, AST_CONTROL_UNHOLD);
+ }
+
+ ast_verb(3, "TRANSFERRING %s to %s\n", call_2_ast->name, call_1_ast->name);
+ retval = ast_channel_masquerade(call_1_ast, bridged);
+ } else {
+ /* Try masquerading the other way. */
+ bridged = ast_bridged_channel(call_1_ast);
+ if (bridged) {
+ if (call_1_held) {
+ ast_queue_control(call_1_ast, AST_CONTROL_UNHOLD);
+ }
+ if (call_2_held) {
+ ast_queue_control(call_2_ast, AST_CONTROL_UNHOLD);
+ }
+
+ ast_verb(3, "TRANSFERRING %s to %s\n", call_1_ast->name, call_2_ast->name);
+ retval = ast_channel_masquerade(call_2_ast, bridged);
+ } else {
+ /* Could not transfer. */
+ retval = -1;
+ }
+ }
+ if (bridged && retval) {
+ /* Restore HOLD on held calls because masquerade failed. */
+ if (call_1_held) {
+ ast_queue_control(call_1_ast, AST_CONTROL_HOLD);
+ }
+ if (call_2_held) {
+ ast_queue_control(call_2_ast, AST_CONTROL_HOLD);
+ }
+ }
+
+ ast_channel_unlock(call_1_ast);
+ ast_channel_unlock(call_2_ast);
+ sig_pri_unlock_private(pri->pvts[call_1_chanpos]);
+ sig_pri_unlock_private(pri->pvts[call_2_chanpos]);
+
+ return retval;
+}
+#endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
+
#if defined(HAVE_PRI_CCSS)
/*!
* \internal
@@ -2406,6 +2506,24 @@ static void sig_pri_handle_subcmds(struct sig_pri_pri *pri, int chanpos, int eve
subcmd->u.cc_cancel.is_agent);
break;
#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_TRANSFER)
+ case PRI_SUBCMD_TRANSFER_CALL:
+ if (!call_rsp) {
+ /* Should never happen. */
+ ast_log(LOG_ERROR,
+ "Call transfer subcommand without call to send response!\n");
+ break;
+ }
+
+ sig_pri_unlock_private(pri->pvts[chanpos]);
+ pri_transfer_rsp(pri->pri, call_rsp, subcmd->u.transfer.invoke_id,
+ sig_pri_attempt_transfer(pri,
+ subcmd->u.transfer.call_1, subcmd->u.transfer.is_call_1_held,
+ subcmd->u.transfer.call_2, subcmd->u.transfer.is_call_2_held)
+ ? 0 : 1);
+ sig_pri_lock_private(pri->pvts[chanpos]);
+ break;
+#endif /* defined(HAVE_PRI_TRANSFER) */
default:
ast_debug(2,
"Unknown call subcommand(%d) in %s event on channel %d/%d on span %d.\n",
@@ -2419,78 +2537,6 @@ static void sig_pri_handle_subcmds(struct sig_pri_pri *pri, int chanpos, int eve
#if defined(HAVE_PRI_CALL_HOLD)
/*!
* \internal
- * \brief Attempt to transfer the active call to the held call.
- * \since 1.8
- *
- * \param pri sig_pri PRI control structure.
- * \param active_call Active call to transfer.
- * \param held_call Held call to transfer.
- *
- * \note Assumes the pri->lock is already obtained.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-static int sig_pri_attempt_transfer(struct sig_pri_pri *pri, q931_call *active_call, q931_call *held_call)
-{
- int retval;
- int active_chanpos;
- int held_chanpos;
- struct ast_channel *active_ast;
- struct ast_channel *held_ast;
- struct ast_channel *bridged;
-
- active_chanpos = pri_find_pri_call(pri, active_call);
- held_chanpos = pri_find_pri_call(pri, held_call);
- if (active_chanpos < 0 || held_chanpos < 0) {
- return -1;
- }
-
- sig_pri_lock_private(pri->pvts[active_chanpos]);
- sig_pri_lock_private(pri->pvts[held_chanpos]);
- sig_pri_lock_owner(pri, active_chanpos);
- sig_pri_lock_owner(pri, held_chanpos);
-
- active_ast = pri->pvts[active_chanpos]->owner;
- held_ast = pri->pvts[held_chanpos]->owner;
- if (!active_ast || !held_ast) {
- if (active_ast) {
- ast_channel_unlock(active_ast);
- }
- if (held_ast) {
- ast_channel_unlock(held_ast);
- }
- sig_pri_unlock_private(pri->pvts[active_chanpos]);
- sig_pri_unlock_private(pri->pvts[held_chanpos]);
- return -1;
- }
-
- bridged = ast_bridged_channel(held_ast);
- if (bridged) {
- ast_queue_control(held_ast, AST_CONTROL_UNHOLD);
-
- ast_verb(3, "TRANSFERRING %s to %s\n", held_ast->name, active_ast->name);
- retval = ast_channel_masquerade(active_ast, bridged);
- } else {
- /*
- * Could not transfer. Held channel is not bridged anymore.
- * Held party probably got tired of waiting and hung up.
- */
- retval = -1;
- }
-
- ast_channel_unlock(active_ast);
- ast_channel_unlock(held_ast);
- sig_pri_unlock_private(pri->pvts[active_chanpos]);
- sig_pri_unlock_private(pri->pvts[held_chanpos]);
-
- return retval;
-}
-#endif /* defined(HAVE_PRI_CALL_HOLD) */
-
-#if defined(HAVE_PRI_CALL_HOLD)
-/*!
- * \internal
* \brief Handle the hold event from libpri.
* \since 1.8
*
@@ -3766,8 +3812,8 @@ static void *pri_dchannel(void *vpri)
&& pri->hold_disconnect_transfer) {
/* We are to transfer the call instead of simply hanging up. */
sig_pri_unlock_private(pri->pvts[chanpos]);
- if (!sig_pri_attempt_transfer(pri, e->hangup.call_active,
- e->hangup.call_held)) {
+ if (!sig_pri_attempt_transfer(pri, e->hangup.call_active, 0,
+ e->hangup.call_held, 1)) {
break;
}
sig_pri_lock_private(pri->pvts[chanpos]);
@@ -4884,6 +4930,9 @@ int sig_pri_start_pri(struct sig_pri_pri *pri)
pri_cc_retain_signaling_req(pri->pri, pri->cc_qsig_signaling_link_req);
pri_cc_retain_signaling_rsp(pri->pri, pri->cc_qsig_signaling_link_rsp);
#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_TRANSFER)
+ pri_transfer_enable(pri->pri, 1);
+#endif /* defined(HAVE_PRI_TRANSFER) */
pri->resetpos = -1;
if (ast_pthread_create_background(&pri->master, NULL, pri_dchannel, pri)) {
diff --git a/channels/sig_pri.h b/channels/sig_pri.h
index ecdc0b70f..efd3523e5 100644
--- a/channels/sig_pri.h
+++ b/channels/sig_pri.h
@@ -253,6 +253,11 @@ struct sig_pri_pri {
/*! \brief TRUE if held calls are transferred on disconnect. */
unsigned int hold_disconnect_transfer:1;
#endif /* defined(HAVE_PRI_CALL_HOLD) */
+ /*!
+ * \brief TRUE if call transfer is enabled for the span.
+ * \note Support switch-side transfer (called 2BCT, RLT or other names)
+ */
+ unsigned int transfer:1;
int dialplan; /*!< Dialing plan */
int localdialplan; /*!< Local dialing plan */
char internationalprefix[10]; /*!< country access code ('00' for european dialplans) */