[asterisk-commits] rmudgett: branch rmudgett/dahdi_facility r220222 - in /team/rmudgett/dahdi_fa...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Thu Sep 24 13:34:57 CDT 2009
Author: rmudgett
Date: Thu Sep 24 13:34:53 2009
New Revision: 220222
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=220222
Log:
Added code to transfer held calls on disconnect as a user option.
Modified:
team/rmudgett/dahdi_facility/CHANGES
team/rmudgett/dahdi_facility/channels/chan_dahdi.c
team/rmudgett/dahdi_facility/channels/sig_pri.c
team/rmudgett/dahdi_facility/channels/sig_pri.h
team/rmudgett/dahdi_facility/configs/chan_dahdi.conf.sample
Modified: team/rmudgett/dahdi_facility/CHANGES
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/dahdi_facility/CHANGES?view=diff&rev=220222&r1=220221&r2=220222
==============================================================================
--- team/rmudgett/dahdi_facility/CHANGES (original)
+++ team/rmudgett/dahdi_facility/CHANGES Thu Sep 24 13:34:53 2009
@@ -203,6 +203,9 @@
LibPRI).
* Added the ability to ignore calls that are not in a Multiple Subscriber
Number (MSN) list for PTMP CPE interfaces.
+ * Added support for BRI PTMP NT mode. (Requires latest LibPRI.)
+ * Added handling of received HOLD/RETRIEVE messages and the optional ability
+ to transfer a held call on disconnect similar to an analog phone.
Asterisk Manager Interface
--------------------------
Modified: team/rmudgett/dahdi_facility/channels/chan_dahdi.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/dahdi_facility/channels/chan_dahdi.c?view=diff&rev=220222&r1=220221&r2=220222
==============================================================================
--- team/rmudgett/dahdi_facility/channels/chan_dahdi.c (original)
+++ team/rmudgett/dahdi_facility/channels/chan_dahdi.c Thu Sep 24 13:34:53 2009
@@ -11175,6 +11175,10 @@
#ifdef HAVE_PRI_INBANDDISCONNECT
pris[span].pri.inbanddisconnect = conf->pri.pri.inbanddisconnect;
#endif
+#if defined(HAVE_PRI_CALL_HOLD)
+ pris[span].pri.hold_disconnect_transfer =
+ conf->pri.pri.hold_disconnect_transfer;
+#endif /* defined(HAVE_PRI_CALL_HOLD) */
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));
@@ -16162,6 +16166,10 @@
#endif /* PRI_GETSET_TIMERS */
} else if (!strcasecmp(v->name, "facilityenable")) {
confp->pri.pri.facilityenable = ast_true(v->value);
+#if defined(HAVE_PRI_CALL_HOLD)
+ } else if (!strcasecmp(v->name, "hold_disconnect_transfer")) {
+ confp->pri.pri.hold_disconnect_transfer = ast_true(v->value);
+#endif /* defined(HAVE_PRI_CALL_HOLD) */
#endif /* HAVE_PRI */
#ifdef HAVE_SS7
} else if (!strcasecmp(v->name, "ss7type")) {
Modified: team/rmudgett/dahdi_facility/channels/sig_pri.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/dahdi_facility/channels/sig_pri.c?view=diff&rev=220222&r1=220221&r2=220222
==============================================================================
--- team/rmudgett/dahdi_facility/channels/sig_pri.c (original)
+++ team/rmudgett/dahdi_facility/channels/sig_pri.c Thu Sep 24 13:34:53 2009
@@ -50,7 +50,6 @@
#ifndef PRI_EVENT_FACILITY
#error please update libpri
#endif
-#define HAVE_PRI_CALL_HOLD 1 /* BUGBUG remove this line and put test in configure.ac */
/* define this to send PRI user-user information elements */
#undef SUPPORT_USERUSER
@@ -960,6 +959,35 @@
}
return idx;
}
+#endif /* defined(HAVE_PRI_CALL_HOLD) */
+
+#if defined(HAVE_PRI_CALL_HOLD)
+/*!
+ * \internal
+ * \brief Find the channel associated with the libpri call.
+ * \since 1.6.3
+ *
+ * \param pri sig_pri span controller to find interface.
+ * \param call LibPRI opaque call pointer to find.
+ *
+ * \note Assumes the pri->lock is already obtained.
+ *
+ * \retval array-index into private pointer array on success.
+ * \retval -1 on error.
+ */
+static int pri_find_pri_call(struct sig_pri_pri *pri, q931_call *call)
+{
+ int idx;
+
+ for (idx = 0; idx < pri->numchans; ++idx) {
+ if (pri->pvts[idx] && pri->pvts[idx]->call == call) {
+ /* Found the channel */
+ return idx;
+ }
+ }
+ return -1;
+}
+
#endif /* defined(HAVE_PRI_CALL_HOLD) */
static void *do_idle_thread(void *vchan)
@@ -1341,6 +1369,219 @@
}
}
}
+
+#if defined(HAVE_PRI_CALL_HOLD)
+/*!
+ * \internal
+ * \brief Attempt to transfer the active call to the held call.
+ * \since 1.6.3
+ *
+ * \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.6.3
+ *
+ * \param pri sig_pri PRI control structure.
+ * \param ev Hold event received.
+ *
+ * \note Assumes the pri->lock is already obtained.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int sig_pri_handle_hold(struct sig_pri_pri *pri, pri_event *ev)
+{
+ int retval;
+ int chanpos_old;
+ int chanpos_new;
+ struct ast_channel *bridged;
+ struct ast_channel *owner;
+
+ chanpos_old = pri_find_principle(pri, ev->hold.channel, ev->hold.call);
+ if (chanpos_old < 0) {
+ ast_log(LOG_WARNING,
+ "Received HOLD on unconfigured channel %d/%d span %d\n",
+ PRI_SPAN(ev->hold.channel), PRI_CHANNEL(ev->hold.channel), pri->span);
+ return -1;
+ }
+ if (pri->pvts[chanpos_old]->no_b_channel) {
+ /* Call is already on hold or is call waiting call. */
+ return -1;
+ }
+
+ sig_pri_lock_private(pri->pvts[chanpos_old]);
+ sig_pri_lock_owner(pri, chanpos_old);
+ owner = pri->pvts[chanpos_old]->owner;
+ if (!owner) {
+ retval = -1;
+ goto done_with_private;
+ }
+ bridged = ast_bridged_channel(owner);
+ if (!bridged) {
+ /* Cannot hold a call that is not bridged. */
+ retval = -1;
+ goto done_with_owner;
+ }
+ chanpos_new = pri_find_empty_nobch(pri);
+ if (chanpos_new < 0) {
+ /* No hold channel available. */
+ retval = -1;
+ goto done_with_owner;
+ }
+ sig_pri_handle_subcmds(pri, chanpos_old, ev->e, ev->hold.channel, ev->hold.subcmds);
+ chanpos_new = pri_fixup_principle(pri, chanpos_new, ev->hold.call);
+ if (chanpos_new < 0) {
+ /* Should never happen. */
+ retval = -1;
+ } else {
+ struct ast_frame f = { AST_FRAME_CONTROL, };
+
+ f.subclass = AST_CONTROL_HOLD;
+ ast_queue_frame(owner, &f);
+ retval = 0;
+ }
+
+done_with_owner:;
+ ast_channel_unlock(owner);
+done_with_private:;
+ sig_pri_unlock_private(pri->pvts[chanpos_old]);
+
+ return retval;
+}
+#endif /* defined(HAVE_PRI_CALL_HOLD) */
+
+#if defined(HAVE_PRI_CALL_HOLD)
+/*!
+ * \internal
+ * \brief Handle the retrieve event from libpri.
+ * \since 1.6.3
+ *
+ * \param pri sig_pri PRI control structure.
+ * \param ev Retrieve event received.
+ *
+ * \note Assumes the pri->lock is already obtained.
+ *
+ * \return Nothing
+ */
+static void sig_pri_handle_retrieve(struct sig_pri_pri *pri, pri_event *ev)
+{
+ int chanpos;
+
+ if (!(ev->retrieve.channel & PRI_HELD_CALL)
+ || pri_find_principle(pri, ev->retrieve.channel, ev->retrieve.call) < 0) {
+ /* The call is not currently held. */
+ pri_retrieve_rej(pri->pri, ev->retrieve.call,
+ PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED);
+ return;
+ }
+ if (PRI_CHANNEL(ev->retrieve.channel) == 0xFF) {
+ chanpos = pri_find_empty_chan(pri, 1);
+ } else {
+ chanpos = pri_find_principle(pri,
+ ev->retrieve.channel & ~PRI_HELD_CALL, ev->retrieve.call);
+ if (ev->retrieve.flexible
+ && (chanpos < 0 || pri->pvts[chanpos]->owner)) {
+ /*
+ * Channel selection is flexible and the requested channel
+ * is bad or already in use. Pick another channel.
+ */
+ chanpos = pri_find_empty_chan(pri, 1);
+ }
+ }
+ if (chanpos < 0) {
+ pri_retrieve_rej(pri->pri, ev->retrieve.call,
+ ev->retrieve.flexible ? PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION
+ : PRI_CAUSE_REQUESTED_CHAN_UNAVAIL);
+ return;
+ }
+ chanpos = pri_fixup_principle(pri, chanpos, ev->retrieve.call);
+ if (chanpos < 0) {
+ /* Channel is already in use. */
+ pri_retrieve_rej(pri->pri, ev->retrieve.call,
+ PRI_CAUSE_REQUESTED_CHAN_UNAVAIL);
+ return;
+ }
+ sig_pri_lock_private(pri->pvts[chanpos]);
+ sig_pri_handle_subcmds(pri, chanpos, ev->e, ev->retrieve.channel,
+ ev->retrieve.subcmds);
+ {
+ struct ast_frame f = { AST_FRAME_CONTROL, };
+
+ f.subclass = AST_CONTROL_UNHOLD;
+ pri_queue_frame(pri->pvts[chanpos], &f, pri);
+ }
+ sig_pri_unlock_private(pri->pvts[chanpos]);
+ pri_retrieve_ack(pri->pri, ev->retrieve.call,
+ PVT_TO_CHANNEL(pri->pvts[chanpos]));
+}
+#endif /* defined(HAVE_PRI_CALL_HOLD) */
static void *pri_dchannel(void *vpri)
{
@@ -2287,6 +2528,18 @@
sig_pri_lock_private(pri->pvts[chanpos]);
sig_pri_handle_subcmds(pri, chanpos, e->e, e->hangup.channel,
e->hangup.subcmds);
+#if defined(HAVE_PRI_CALL_HOLD)
+ if (e->hangup.call_active && e->hangup.call_held
+ && 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)) {
+ break;
+ }
+ sig_pri_lock_private(pri->pvts[chanpos]);
+ }
+#endif /* defined(HAVE_PRI_CALL_HOLD) */
if (pri->pvts[chanpos]->owner) {
pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause;
if (pri->pvts[chanpos]->owner->_state == AST_STATE_UP)
@@ -2486,45 +2739,12 @@
break;
#if defined(HAVE_PRI_CALL_HOLD)
case PRI_EVENT_HOLD:
- chanpos = pri_find_principle(pri, e->hold.channel, e->hold.call);
- if (chanpos < 0) {
- ast_log(LOG_WARNING,
- "Received HOLD on unconfigured channel %d/%d span %d\n",
- PRI_SPAN(e->hold.channel), PRI_CHANNEL(e->hold.channel),
- pri->span);
+ if (sig_pri_handle_hold(pri, e)) {
pri_hold_rej(pri->pri, e->hold.call,
PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED);
- break;
- }
- if (pri->pvts[chanpos]->no_b_channel) {
- /* Call is already on hold or is call waiting call. */
- pri_hold_rej(pri->pri, e->hold.call,
- PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED);
- break;
- }
- chanpos = pri_find_empty_nobch(pri);
- if (chanpos < 0) {
- pri_hold_rej(pri->pri, e->hold.call,
- PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED);
- break;
- }
- chanpos = pri_fixup_principle(pri, chanpos, e->hold.call);
- if (chanpos < 0) {
- pri_hold_rej(pri->pri, e->hold.call,
- PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED);
- break;
- }
- sig_pri_lock_private(pri->pvts[chanpos]);
- sig_pri_handle_subcmds(pri, chanpos, e->e, e->hold.channel,
- e->hold.subcmds);
- {
- struct ast_frame f = { AST_FRAME_CONTROL, };
-
- f.subclass = AST_CONTROL_HOLD;
- pri_queue_frame(pri->pvts[chanpos], &f, pri);
- }
- sig_pri_unlock_private(pri->pvts[chanpos]);
- pri_hold_ack(pri->pri, e->hold.call);
+ } else {
+ pri_hold_ack(pri->pri, e->hold.call);
+ }
break;
#endif /* defined(HAVE_PRI_CALL_HOLD) */
#if defined(HAVE_PRI_CALL_HOLD)
@@ -2539,52 +2759,7 @@
#endif /* defined(HAVE_PRI_CALL_HOLD) */
#if defined(HAVE_PRI_CALL_HOLD)
case PRI_EVENT_RETRIEVE:
- if (!(e->retrieve.channel & PRI_HELD_CALL)
- || pri_find_principle(pri, e->retrieve.channel, e->retrieve.call) < 0) {
- /* The call is not currently held. */
- pri_retrieve_rej(pri->pri, e->retrieve.call,
- PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED);
- break;
- }
- if (PRI_CHANNEL(e->retrieve.channel) == 0xFF) {
- chanpos = pri_find_empty_chan(pri, 1);
- } else {
- chanpos = pri_find_principle(pri,
- e->retrieve.channel & ~PRI_HELD_CALL, e->retrieve.call);
- if (e->retrieve.flexible
- && (chanpos < 0 || pri->pvts[chanpos]->owner)) {
- /*
- * Channel selection is flexible and the requested channel
- * is bad or already in use. Pick another channel.
- */
- chanpos = pri_find_empty_chan(pri, 1);
- }
- }
- if (chanpos < 0) {
- pri_retrieve_rej(pri->pri, e->retrieve.call,
- e->retrieve.flexible ? PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION
- : PRI_CAUSE_REQUESTED_CHAN_UNAVAIL);
- break;
- }
- chanpos = pri_fixup_principle(pri, chanpos, e->retrieve.call);
- if (chanpos < 0) {
- /* Channel is already in use. */
- pri_retrieve_rej(pri->pri, e->retrieve.call,
- PRI_CAUSE_REQUESTED_CHAN_UNAVAIL);
- break;
- }
- sig_pri_lock_private(pri->pvts[chanpos]);
- sig_pri_handle_subcmds(pri, chanpos, e->e, e->retrieve.channel,
- e->retrieve.subcmds);
- {
- struct ast_frame f = { AST_FRAME_CONTROL, };
-
- f.subclass = AST_CONTROL_UNHOLD;
- pri_queue_frame(pri->pvts[chanpos], &f, pri);
- }
- sig_pri_unlock_private(pri->pvts[chanpos]);
- pri_retrieve_ack(pri->pri, e->retrieve.call,
- PVT_TO_CHANNEL(pri->pvts[chanpos]));
+ sig_pri_handle_retrieve(pri, e);
break;
#endif /* defined(HAVE_PRI_CALL_HOLD) */
#if defined(HAVE_PRI_CALL_HOLD)
Modified: team/rmudgett/dahdi_facility/channels/sig_pri.h
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/dahdi_facility/channels/sig_pri.h?view=diff&rev=220222&r1=220221&r2=220222
==============================================================================
--- team/rmudgett/dahdi_facility/channels/sig_pri.h (original)
+++ team/rmudgett/dahdi_facility/channels/sig_pri.h Thu Sep 24 13:34:53 2009
@@ -29,6 +29,7 @@
#include "asterisk/frame.h"
#include <libpri.h>
#include <dahdi/user.h>
+#define HAVE_PRI_CALL_HOLD 1 /* BUGBUG remove this line and put test in configure.ac */
enum sig_pri_tone {
SIG_PRI_TONE_RINGTONE = 0,
@@ -189,6 +190,9 @@
#ifdef HAVE_PRI_INBANDDISCONNECT
unsigned int inbanddisconnect:1; /*!< Should we support inband audio after receiving DISCONNECT? */
#endif
+#if defined(HAVE_PRI_CALL_HOLD)
+ unsigned int hold_disconnect_transfer:1; /*!< TRUE if held calls are transferred on disconnect. */
+#endif /* defined(HAVE_PRI_CALL_HOLD) */
int dialplan; /*!< Dialing plan */
int localdialplan; /*!< Local dialing plan */
char internationalprefix[10]; /*!< country access code ('00' for european dialplans) */
Modified: team/rmudgett/dahdi_facility/configs/chan_dahdi.conf.sample
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/dahdi_facility/configs/chan_dahdi.conf.sample?view=diff&rev=220222&r1=220221&r2=220222
==============================================================================
--- team/rmudgett/dahdi_facility/configs/chan_dahdi.conf.sample (original)
+++ team/rmudgett/dahdi_facility/configs/chan_dahdi.conf.sample Thu Sep 24 13:34:53 2009
@@ -181,6 +181,12 @@
; Allow inband audio (progress) when a call is DISCONNECTed by the far end of a PRI
;
;inbanddisconnect=yes
+;
+; Allow a held call to be transferred to the active call on disconnect.
+; This is useful on BRI PTMP NT lines where an ISDN phone can simulate the
+; transfer feature of an analog phone.
+; The default is no.
+;hold_disconnect_transfer=yes
;
; PRI Out of band indications.
; Enable this to report Busy and Congestion on a PRI using out-of-band
More information about the asterisk-commits
mailing list