[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