[libpri-commits] rmudgett: branch rmudgett/facility r1137 - /team/rmudgett/facility/

SVN commits to the libpri project libpri-commits at lists.digium.com
Thu Sep 24 16:39:37 CDT 2009


Author: rmudgett
Date: Thu Sep 24 16:39:33 2009
New Revision: 1137

URL: http://svnview.digium.com/svn/libpri?view=rev&rev=1137
Log:
Merged team/rmudgett/ntptmp branch differences up to -r1136.

Modified:
    team/rmudgett/facility/libpri.h
    team/rmudgett/facility/pri.c
    team/rmudgett/facility/pri_internal.h
    team/rmudgett/facility/pri_q921.h
    team/rmudgett/facility/pri_q931.h
    team/rmudgett/facility/q921.c
    team/rmudgett/facility/q931.c

Modified: team/rmudgett/facility/libpri.h
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/facility/libpri.h?view=diff&rev=1137&r1=1136&r2=1137
==============================================================================
--- team/rmudgett/facility/libpri.h (original)
+++ team/rmudgett/facility/libpri.h Thu Sep 24 16:39:33 2009
@@ -623,10 +623,22 @@
 	int channel;				/* Channel requested */
 	int cause;
 	int cref;
-	q931_call *call;			/* Opaque call pointer */
+	q931_call *call;			/* Opaque call pointer of call hanging up. */
 	long aoc_units;				/* Advise of Charge number of charged units */
 	char useruserinfo[260];		/* User->User info */
 	struct pri_subcommands *subcmds;
+	/*!
+	 * \brief Opaque held call pointer for possible transfer to active call.
+	 * \note The call_held and call_active pointers must not be NULL if
+	 * transfer held call on disconnect is available.
+	 */
+	q931_call *call_held;
+	/*!
+	 * \brief Opaque active call pointer for possible transfer with held call.
+	 * \note The call_held and call_active pointers must not be NULL if
+	 * transfer held call on disconnect is available.
+	 */
+	q931_call *call_active;
 } pri_event_hangup;
 
 typedef struct pri_event_restart_ack {
@@ -863,7 +875,24 @@
 
 /* Hangup a call */
 #define PRI_HANGUP
-int pri_hangup(struct pri *pri, q931_call *call, int cause);
+
+#define HANGUP_DEBUG
+
+#ifdef HANGUP_DEBUG
+#define pri_hangup(a, b, c) \
+	__debug_pri_hangup((a), (b), (c), __FUNCTION__)
+
+
+#else
+
+#define pri_hangup(a, b, c) \
+	__debug_pri_hangup((a), (b), (c), __FUNCTION__)
+
+#endif
+
+int __normal_pri_hangup(struct pri *pri, q931_call *call, int cause);
+
+int __debug_pri_hangup(struct pri *pri, q931_call *call, int cause, const char *caller);
 
 #define PRI_DESTROYCALL
 void pri_destroycall(struct pri *pri, q931_call *call);

Modified: team/rmudgett/facility/pri.c
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/facility/pri.c?view=diff&rev=1137&r1=1136&r2=1137
==============================================================================
--- team/rmudgett/facility/pri.c (original)
+++ team/rmudgett/facility/pri.c Thu Sep 24 16:39:33 2009
@@ -152,6 +152,7 @@
 	ctrl->timers[PRI_TIMER_T313] = 4 * 1000;	/* Wait for CONNECT acknowledge, CPE side only */
 	ctrl->timers[PRI_TIMER_TM20] = 2500;		/* Max time awaiting XID response - Q.921 Appendix IV */
 	ctrl->timers[PRI_TIMER_NM20] = 3;			/* Number of XID retransmits - Q.921 Appendix IV */
+	ctrl->timers[PRI_TIMER_T303] = 4 * 1000;			/* Length between SETUP retransmissions and timeout */
 
 	ctrl->timers[PRI_TIMER_T_HOLD] = 4 * 1000;	/* Wait for HOLD request response. */
 	ctrl->timers[PRI_TIMER_T_RETRIEVE] = 4 * 1000;/* Wait for RETRIEVE request response. */
@@ -228,7 +229,7 @@
 /* Pass in the master for this function */
 void __pri_free_tei(struct pri * p)
 {
-	free (p);
+	free(p);
 }
 
 struct pri *__pri_new_tei(int fd, int node, int switchtype, struct pri *master, pri_io_cb rd, pri_io_cb wr, void *userdata, int tei, int bri)
@@ -645,6 +646,8 @@
 int pri_connected_line_update(struct pri *ctrl, q931_call *call, const struct pri_party_connected_line *connected)
 {
 	struct q931_party_id party_id;
+	unsigned idx;
+	struct q931_call *subcall;
 
 	if (!ctrl || !call) {
 		return -1;
@@ -657,6 +660,16 @@
 		return 0;
 	}
 	call->local_id = party_id;
+
+	/* Update all subcalls with new local_id. */
+	if (call->outboundbroadcast && call->master_call == call) {
+		for (idx = 0; idx < Q931_MAX_TEI; ++idx) {
+			subcall = call->subcalls[idx];
+			if (subcall) {
+				subcall->local_id = party_id;
+			}
+		}
+	}
 
 	switch (call->ourcallstate) {
 	case Q931_CALL_STATE_CALL_INITIATED:
@@ -699,6 +712,9 @@
 
 int pri_redirecting_update(struct pri *ctrl, q931_call *call, const struct pri_party_redirecting *redirecting)
 {
+	unsigned idx;
+	struct q931_call *subcall;
+
 	if (!ctrl || !call) {
 		return -1;
 	}
@@ -707,6 +723,21 @@
 	pri_copy_party_id_to_q931(&call->redirecting.to, &redirecting->to);
 	q931_party_id_fixup(ctrl, &call->redirecting.to);
 	call->redirecting.reason = redirecting->reason;
+
+	/*
+	 * Update all subcalls with new redirecting.to information and reason.
+	 * I do not think we will ever have any subcalls when this data is relevant,
+	 * but update it just in case.
+	 */
+	if (call->outboundbroadcast && call->master_call == call) {
+		for (idx = 0; idx < Q931_MAX_TEI; ++idx) {
+			subcall = call->subcalls[idx];
+			if (subcall) {
+				subcall->redirecting.to = call->redirecting.to;
+				subcall->redirecting.reason = redirecting->reason;
+			}
+		}
+	}
 
 	switch (call->ourcallstate) {
 	case Q931_CALL_STATE_NULL:
@@ -846,14 +877,23 @@
 	}
 }
 
-int pri_hangup(struct pri *pri, q931_call *call, int cause)
+int __normal_pri_hangup(struct pri *pri, q931_call *call, int cause)
 {
 	if (!pri || !call)
 		return -1;
 	if (cause == -1)
 		/* normal clear cause */
 		cause = 16;
+
 	return q931_hangup(pri, call, cause);
+}
+
+int __debug_pri_hangup(struct pri *pri, q931_call *call, int cause, const char *caller)
+{
+	if (caller)
+		pri_error(pri, "%s:pri_hangup(%p, %p, %d)\n", caller, pri, call, cause);
+
+	return __normal_pri_hangup(pri, call, cause);
 }
 
 int pri_reset(struct pri *pri, int channel)
@@ -1022,7 +1062,7 @@
 	vsnprintf(tmp, sizeof(tmp), fmt, ap);
 	va_end(ap);
 	if (__pri_message)
-		__pri_message(pri, tmp);
+		__pri_message(PRI_MASTER(pri), tmp);
 	else
 		fputs(tmp, stdout);
 }
@@ -1035,7 +1075,7 @@
 	vsnprintf(tmp, sizeof(tmp), fmt, ap);
 	va_end(ap);
 	if (__pri_error)
-		__pri_error(pri, tmp);
+		__pri_error(PRI_MASTER(pri), tmp);
 	else
 		fputs(tmp, stderr);
 }

Modified: team/rmudgett/facility/pri_internal.h
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/facility/pri_internal.h?view=diff&rev=1137&r1=1136&r2=1137
==============================================================================
--- team/rmudgett/facility/pri_internal.h (original)
+++ team/rmudgett/facility/pri_internal.h Thu Sep 24 16:39:33 2009
@@ -300,6 +300,7 @@
 /* Internal switch types */
 #define PRI_SWITCH_GR303_EOC_PATH	19
 #define PRI_SWITCH_GR303_TMC_SWITCHING	20
+#define Q931_MAX_TEI	8
 
 struct apdu_event {
 	int message;			/* What message to send the ADPU in */
@@ -488,6 +489,18 @@
 							   -1 - No reverse charging
 							    1 - Reverse charging
 							0,2-7 - Reserved for future use */
+	int t303_timer;
+	int t303_expirycnt;
+
+	int hangupinitiated;
+	int outboundbroadcast;
+	int performing_fake_clearing;
+	/* These valid in slave call only */
+	struct q931_call *master_call;
+
+	/* These valid in master call only */
+	struct q931_call *subcalls[Q931_MAX_TEI];
+	int pri_winner;
 };
 
 extern int pri_schedule_event(struct pri *pri, int ms, void (*function)(void *data), void *data);
@@ -532,4 +545,35 @@
 
 int q931_notify_redirection(struct pri *ctrl, q931_call *call, int notify, const struct q931_party_number *number);
 
+static inline struct pri * PRI_MASTER(struct pri *mypri)
+{
+	struct pri *pri = mypri;
+	
+	if (!pri)
+		return NULL;
+
+	while (pri->master)
+		pri = pri->master;
+
+	return pri;
+}
+
+static inline int BRI_NT_PTMP(struct pri *mypri)
+{
+	struct pri *pri;
+
+	pri = PRI_MASTER(mypri);
+
+	return pri->bri && (((pri)->localtype == PRI_NETWORK) && ((pri)->tei == Q921_TEI_GROUP));
+}
+
+static inline int BRI_TE_PTMP(struct pri *mypri)
+{
+	struct pri *pri;
+
+	pri = PRI_MASTER(mypri);
+
+	return pri->bri && (((pri)->localtype == PRI_CPE) && ((pri)->tei == Q921_TEI_GROUP));
+}
+
 #endif

Modified: team/rmudgett/facility/pri_q921.h
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/facility/pri_q921.h?view=diff&rev=1137&r1=1136&r2=1137
==============================================================================
--- team/rmudgett/facility/pri_q921.h (original)
+++ team/rmudgett/facility/pri_q921.h Thu Sep 24 16:39:33 2009
@@ -192,6 +192,8 @@
 
 extern int q921_transmit_iframe(struct pri *pri, void *buf, int len, int cr);
 
+extern int q921_transmit_uiframe(struct pri *pri, void *buf, int len);
+
 extern pri_event *q921_dchannel_up(struct pri *pri);
 
 extern pri_event *q921_dchannel_down(struct pri *pri);

Modified: team/rmudgett/facility/pri_q931.h
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/facility/pri_q931.h?view=diff&rev=1137&r1=1136&r2=1137
==============================================================================
--- team/rmudgett/facility/pri_q931.h (original)
+++ team/rmudgett/facility/pri_q931.h Thu Sep 24 16:39:33 2009
@@ -470,12 +470,12 @@
 
 extern void q931_dl_indication(struct pri *pri, int event);
 
-int q931_send_hold(struct pri *ctrl, q931_call *call);
-int q931_send_hold_ack(struct pri *ctrl, q931_call *call);
-int q931_send_hold_rej(struct pri *ctrl, q931_call *call, int cause);
-
-int q931_send_retrieve(struct pri *ctrl, q931_call *call, int channel);
-int q931_send_retrieve_ack(struct pri *ctrl, q931_call *call, int channel);
-int q931_send_retrieve_rej(struct pri *ctrl, q931_call *call, int cause);
+int q931_send_hold(struct pri *ctrl, struct q931_call *call);
+int q931_send_hold_ack(struct pri *ctrl, struct q931_call *call);
+int q931_send_hold_rej(struct pri *ctrl, struct q931_call *call, int cause);
+
+int q931_send_retrieve(struct pri *ctrl, struct q931_call *call, int channel);
+int q931_send_retrieve_ack(struct pri *ctrl, struct q931_call *call, int channel);
+int q931_send_retrieve_rej(struct pri *ctrl, struct q931_call *call, int cause);
 
 #endif

Modified: team/rmudgett/facility/q921.c
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/facility/q921.c?view=diff&rev=1137&r1=1136&r2=1137
==============================================================================
--- team/rmudgett/facility/q921.c (original)
+++ team/rmudgett/facility/q921.c Thu Sep 24 16:39:33 2009
@@ -27,6 +27,7 @@
  * terms granted here.
  */
 
+#include <stdint.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
@@ -477,6 +478,45 @@
 	}
 }
 
+int q921_transmit_uiframe(struct pri *pri, void *buf, int len)
+{
+	uint8_t ubuf[512];
+	q921_h *h = (void *)&ubuf[0];
+
+	if (len >= 512) {
+		pri_error(pri, "Requested to send UI frame larger than 512 bytes!\n");
+		return -1;
+	}
+
+	memset(ubuf, 0, sizeof(ubuf));
+	h->h.sapi = 0;
+	h->h.ea1 = 0;
+	h->h.ea2 = 1;
+	h->h.tei = pri->tei;
+	h->u.m3 = 0;
+	h->u.m2 = 0;
+	h->u.p_f = 0;	/* Poll bit set */
+	h->u.ft = Q921_FRAMETYPE_U;
+
+	switch(pri->localtype) {
+	case PRI_NETWORK:
+		h->h.c_r = 1;
+		break;
+	case PRI_CPE:
+		h->h.c_r = 0;
+		break;
+	default:
+		pri_error(pri, "Don't know how to U/A on a type %d node\n", pri->localtype);
+		return -1;
+	}
+
+	memcpy(h->u.data, buf, len);
+
+	q921_transmit(pri, h, len + 3);
+
+	return 0;
+}
+
 int q921_transmit_iframe(struct pri *pri, void *buf, int len, int cr)
 {
 	q921_frame *f, *prev=NULL;
@@ -515,6 +555,7 @@
 			pri->txqueue = f;
 		/* Immediately transmit unless we're in a recovery state, or the window
 		   size is too big */
+		pri_message(pri, "TEI/SAPI: %d/%d state %d retran %d busy %d\n", pri->tei, pri->sapi, pri->q921_state, pri->retrans, pri->busy);
 		if ((pri->q921_state == Q921_LINK_CONNECTION_ESTABLISHED) && (!pri->retrans && !pri->busy)) {
 			if (pri->windowlen < pri->window) {
 				q921_send_queued_iframes(pri);
@@ -559,10 +600,11 @@
 		pri->t203_timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T200], t200_expire, pri);
 	} else {
 		if (pri->debug & PRI_DEBUG_Q921_DUMP)
-			pri_message(pri, "T203 counter expired in weird state %d\n", pri->q921_state);
+			pri_message(pri, "T203 counter expired in weird state %d on pri with sapi %d and tei %d\n", pri->q921_state, pri->sapi, pri->tei);
 		pri->t203_timer = 0;
 	}
 }
+
 static pri_event *q921_handle_iframe(struct pri *pri, q921_i *i, int len)
 {
 	int res;
@@ -865,8 +907,14 @@
 static pri_event *q921_receive_MDL(struct pri *pri, q921_u *h, int len)
 {
 	int ri;
-	struct pri *sub;
+	struct pri *sub = pri;
 	int tei;
+
+	if (!BRI_NT_PTMP(pri) && !BRI_TE_PTMP(pri)) {
+		pri_error(pri, "Received MDL/TEI managemement message, but configured for mode other than PTMP!\n");
+		return NULL;
+	}
+
 	if (pri->debug & PRI_DEBUG_Q921_STATE)
 		pri_message(pri, "Received MDL message\n");
 	if (h->data[0] != 0x0f) {
@@ -881,17 +929,19 @@
 	tei = (h->data[4] >> 1);
 	switch(h->data[3]) {
 	case Q921_TEI_IDENTITY_REQUEST:
+		if (!BRI_NT_PTMP(pri)) {
+			return NULL;
+		}
+
 		if (tei != 127) {
 			pri_error(pri, "Received TEI identity request with invalid TEI %d\n", tei);
 			q921_send_tei(pri, Q921_TEI_IDENTITY_DENIED, ri, tei, 1);
 		}
-		/* Go to master */
-		for (sub = pri; sub->master; sub = sub->master);
 		tei = 64;
-/*! \todo XXX Error:  The following loop never terminates! */
-		while(sub->subchannel) {
-			if(sub->subchannel->tei == tei)
+		while (sub->subchannel) {
+			if (sub->subchannel->tei == tei)
 				++tei;
+			sub = sub->subchannel;
 		}
 		sub->subchannel = __pri_new_tei(-1, pri->localtype, pri->switchtype, pri, NULL, NULL, NULL, tei, 1);
 		if (!sub->subchannel) {
@@ -901,6 +951,9 @@
 		q921_send_tei(pri, Q921_TEI_IDENTITY_ASSIGNED, ri, tei, 1);
 		break;
 	case Q921_TEI_IDENTITY_ASSIGNED:
+		if (!BRI_TE_PTMP(pri))
+			return NULL;
+
 		if (ri != pri->ri) {
 			pri_message(pri, "TEI assignment received for invalid Ri %02x (our is %02x)\n", ri, pri->ri);
 			return NULL;
@@ -922,6 +975,8 @@
 		pri->q921_state = Q921_TEI_ASSIGNED;
 		break;
 	case Q921_TEI_IDENTITY_CHECK_REQUEST:
+		if (!BRI_TE_PTMP(pri))
+			return NULL;
 		/* We're assuming one TEI per PRI in TE PTMP mode */
 
 		/* If no subchannel (TEI) ignore */
@@ -934,6 +989,8 @@
 
 		break;
 	case Q921_TEI_IDENTITY_REMOVE:
+		if (!BRI_TE_PTMP(pri))
+			return NULL;
 		/* XXX: Assuming multiframe mode has been disconnected already */
 		if (!pri->subchannel)
 			return NULL;
@@ -975,6 +1032,10 @@
 			pri_error(pri, "!! Received short I-frame (expected 4, got %d)\n", len);
 			break;
 		}
+
+		/* T203 is rescheduled only on reception of I frames or S frames */
+		reschedule_t203(pri);
+
 		return q921_handle_iframe(pri, &h->i, len);	
 		break;
 	case 1:
@@ -986,6 +1047,10 @@
 			pri_error(pri, "!! Received short S-frame (expected 4, got %d)\n", len);
 			break;
 		}
+
+		/* T203 is rescheduled only on reception of I frames or S frames */
+		reschedule_t203(pri);
+
 		switch(h->s.ss) {
 		case 0:
 			/* Receiver Ready */
@@ -1135,7 +1200,6 @@
 			/* Acknowledge */
 			q921_send_ua(pri, h->u.p_f);
 			ev = q921_dchannel_down(pri);
-			q921_restart(pri, 0);
 			return ev;
 		case 3:
 			if (h->u.m2 == 3) {
@@ -1195,6 +1259,31 @@
 	return NULL;
 }
 
+static pri_event *q921_handle_unmatched_frame(struct pri *pri, q921_h *h, int len)
+{
+	pri = PRI_MASTER(pri);
+
+	pri_error(pri, "Could not find candidate subchannel for received frame with SAPI/TEI of %d/%d.\n", h->h.sapi, h->h.tei);
+
+	if (h->h.tei < 64) {
+		pri_error(pri, "Do not support manual TEI range. Discarding\n");
+		return NULL;
+	}
+
+	if (h->h.sapi != Q921_SAPI_CALL_CTRL) {
+		pri_error(pri, "Message with SAPI other than CALL CTRL is discarded\n");
+		return NULL;
+	}
+
+	pri_error(pri, "Sending TEI release, in order to re-establish TEI state\n");
+
+	/* Q.921 says we should send the remove message twice, in case of link corruption */
+	q921_send_tei(pri, Q921_TEI_IDENTITY_REMOVE, 0, h->h.tei, 1);
+	q921_send_tei(pri, Q921_TEI_IDENTITY_REMOVE, 0, h->h.tei, 1);
+
+	return NULL;
+}
+
 static pri_event *__q921_receive(struct pri *pri, q921_h *h, int len)
 {
 	pri_event *ev;
@@ -1208,26 +1297,22 @@
 	if (h->h.ea1 || !(h->h.ea2))
 		return NULL;
 
-#if 0 /* Will be rejected by subchannel analyzis */
-	/* Check for broadcasts - not yet handled */
-	if (h->h.tei == Q921_TEI_GROUP)
-		return NULL;
-#endif
-
 	if (!((h->h.sapi == pri->sapi) && ((h->h.tei == pri->tei) || (h->h.tei == Q921_TEI_GROUP)))) {
 		/* Check for SAPIs we don't yet handle */
 		/* If it's not us, try any subchannels we have */
 		if (pri->subchannel)
 			return q921_receive(pri->subchannel, h, len + 2);
 		else {
-			return NULL;
+			/* This means we couldn't find a candidate subchannel for it...
+			 * Time for some corrective action */
+
+			return q921_handle_unmatched_frame(pri, h, len);
 		}
 
 	}
 	if (pri->debug & PRI_DEBUG_Q921_DUMP)
 		pri_message(pri, "Handling message for SAPI/TEI=%d/%d\n", h->h.sapi, h->h.tei);
 	ev = __q921_receive_qualified(pri, h, len);
-	reschedule_t203(pri);
 	return ev;
 }
 

Modified: team/rmudgett/facility/q931.c
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/facility/q931.c?view=diff&rev=1137&r1=1136&r2=1137
==============================================================================
--- team/rmudgett/facility/q931.c (original)
+++ team/rmudgett/facility/q931.c Thu Sep 24 16:39:33 2009
@@ -91,7 +91,9 @@
 	{ Q931_SUSPEND_ACKNOWLEDGE, "SUSPEND ACKNOWLEDGE" },
 	{ Q931_SUSPEND_REJECT, "SUSPEND REJECT" },
 };
+
 static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *c, int missingmand);
+static void nt_ptmp_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *c, int *allow_event, int *allow_posthandle);
 
 struct msgtype att_maintenance_msgs[] = {
 	{ ATT_SERVICE, "SERVICE", { Q931_CHANNEL_IDENT } },
@@ -116,6 +118,7 @@
 	{ PRI_CAUSE_NO_ANSWER, "User alerting, no answer" },
 	{ PRI_CAUSE_CALL_REJECTED, "Call Rejected" },
 	{ PRI_CAUSE_NUMBER_CHANGED, "Number changed" },
+	{ PRI_CAUSE_NONSELECTED_USER_CLEARING, "Non-selected user clearing" },
 	{ PRI_CAUSE_DESTINATION_OUT_OF_ORDER, "Destination out of order" },
 	{ PRI_CAUSE_INVALID_NUMBER_FORMAT, "Invalid number format" },
 	{ PRI_CAUSE_FACILITY_REJECTED, "Facility rejected" },
@@ -128,13 +131,14 @@
 	{ PRI_CAUSE_ACCESS_INFO_DISCARDED, "Access information discarded" },
 	{ PRI_CAUSE_REQUESTED_CHAN_UNAVAIL, "Requested channel not available" },
 	{ PRI_CAUSE_PRE_EMPTED, "Pre-empted" },
+	{ PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED, "Resource unavailable, unspecified" },
 	{ PRI_CAUSE_FACILITY_NOT_SUBSCRIBED, "Facility not subscribed" },
 	{ PRI_CAUSE_OUTGOING_CALL_BARRED, "Outgoing call barred" },
 	{ PRI_CAUSE_INCOMING_CALL_BARRED, "Incoming call barred" },
 	{ PRI_CAUSE_BEARERCAPABILITY_NOTAUTH, "Bearer capability not authorized" },
 	{ PRI_CAUSE_BEARERCAPABILITY_NOTAVAIL, "Bearer capability not available" },
+	{ PRI_CAUSE_SERVICEOROPTION_NOTAVAIL, "Service or option not available, unspecified" },
 	{ PRI_CAUSE_BEARERCAPABILITY_NOTIMPL, "Bearer capability not implemented" },
-	{ PRI_CAUSE_SERVICEOROPTION_NOTAVAIL, "Service or option not available, unspecified" },
 	{ PRI_CAUSE_CHAN_NOT_IMPLEMENTED, "Channel not implemented" },
 	{ PRI_CAUSE_FACILITY_NOT_IMPLEMENTED, "Facility not implemented" },
 	{ PRI_CAUSE_INVALID_CALL_REFERENCE, "Invalid call reference value" },
@@ -237,9 +241,10 @@
 	do { \
 		if (((ctrl)->debug & PRI_DEBUG_Q931_STATE) && (call)->ourcallstate != (newstate)) { \
 			pri_message((ctrl), \
-				DBGHEAD "Call %d on channel %d enters state %d (%s).  Hold state: %s\n", \
-				DBGINFO, (call)->cr, (call)->channelno, (newstate), \
-				q931_call_state_str(newstate), q931_hold_state_str((call)->hold_state)); \
+				DBGHEAD "%s %d enters state %d (%s).  Hold state: %s\n", \
+				DBGINFO, ((call) == (call)->master_call) ? "Call" : "Subcall", \
+				(call)->cr, (newstate), q931_call_state_str(newstate), \
+				q931_hold_state_str((call)->master_call->hold_state)); \
 		} \
 		(call)->ourcallstate = (newstate); \
 	} while (0)
@@ -250,20 +255,21 @@
 
 #if 1
 /* Update hold state with transition trace. */
-#define UPDATE_HOLD_STATE(ctrl, call, newstate) \
+#define UPDATE_HOLD_STATE(ctrl, master_call, newstate) \
 	do { \
-		if (((ctrl)->debug & PRI_DEBUG_Q931_STATE) && (call)->hold_state != (newstate)) { \
+		if (((ctrl)->debug & PRI_DEBUG_Q931_STATE) \
+			&& (master_call)->hold_state != (newstate)) { \
 			pri_message((ctrl), \
-				DBGHEAD "Call %d on channel %d in state %d (%s) enters Hold state: %s\n", \
-				DBGINFO, (call)->cr, (call)->channelno, (call)->ourcallstate, \
-				q931_call_state_str((call)->ourcallstate), \
+				DBGHEAD "Call %d in state %d (%s) enters Hold state: %s\n", \
+				DBGINFO, (master_call)->cr, (master_call)->ourcallstate, \
+				q931_call_state_str((master_call)->ourcallstate), \
 				q931_hold_state_str(newstate)); \
 		} \
-		(call)->hold_state = (newstate); \
+		(master_call)->hold_state = (newstate); \
 	} while (0)
 #else
 /* Update hold state with no trace. */
-#define UPDATE_HOLD_STATE(ctrl, call, newstate) (call)->hold_state = (newstate)
+#define UPDATE_HOLD_STATE(ctrl, master_call, newstate) (master_call)->hold_state = (newstate)
 #endif
 
 struct ie {
@@ -292,19 +298,27 @@
 static int q931_encode_channel(const q931_call *call)
 {
 	int held_call;
-
-	switch (call->hold_state) {
+	int channelno;
+	int ds1no;
+
+	switch (call->master_call->hold_state) {
 	case Q931_HOLD_STATE_CALL_HELD:
 	case Q931_HOLD_STATE_RETRIEVE_REQ:
 	case Q931_HOLD_STATE_RETRIEVE_IND:
 		held_call = 1 << 18;
+
+		/* So a -1 does not wipe out the held_call flag. */
+		channelno = call->channelno & 0xFF;
+		ds1no = call->ds1no & 0xFF;
 		break;
 	default:
 		held_call = 0;
-		break;
-	}
-	return call->channelno | (call->ds1no << 8) | (call->ds1explicit << 16)
-		| (call->cis_call << 17) | held_call;
+		channelno = call->channelno;
+		ds1no = call->ds1no;
+		break;
+	}
+	return channelno | (ds1no << 8) | (call->ds1explicit << 16) | (call->cis_call << 17)
+		| held_call;
 }
 
 /*!
@@ -853,7 +867,7 @@
 		return 0;
 	}
 
-	if (((ctrl->switchtype != PRI_SWITCH_QSIG) && (call->ds1no > 0)) || call->ds1explicit) {
+	if (!ctrl->bri && (((ctrl->switchtype != PRI_SWITCH_QSIG) && (call->ds1no > 0)) || call->ds1explicit)) {
 		/* We are specifying the interface.  Octet 3.1 */
 		ie->data[pos++] |= 0x40;
 		ie->data[pos++] = 0x80 | call->ds1no;
@@ -3082,20 +3096,25 @@
 	struct pri *master;
 
 	/* Find the master  - He has the call pool */
-	if (ctrl->master) {
-		master = ctrl->master;
-	} else {
-		master = ctrl;
-	}
+	master = PRI_MASTER(ctrl);
 
 	cur = *master->callpool;
 	prev = NULL;
 	while (cur) {
 		if (cur->cr == cr) {
-			if (!ctrl->bri && ctrl->switchtype != PRI_SWITCH_GR303_EOC_PATH
-				&& ctrl->switchtype != PRI_SWITCH_GR303_TMC_SWITCHING
-				&& cur->pri != ctrl) {
-				cur->pri = ctrl;
+			/* Found existing call. */
+			switch (ctrl->switchtype) {
+			case PRI_SWITCH_GR303_EOC:
+			case PRI_SWITCH_GR303_EOC_PATH:
+			case PRI_SWITCH_GR303_TMC:
+			case PRI_SWITCH_GR303_TMC_SWITCHING:
+				break;
+			default:
+				if (!ctrl->bri) {
+					/* PRI is set to whoever called us */
+					cur->pri = ctrl;
+				}
+				break;
 			}
 			return cur;
 		}
@@ -3137,6 +3156,8 @@
 	cur->aoc_units = -1;
 	cur->changestatus = -1;
 	cur->reversecharge = -1;
+	cur->pri_winner = -1;
+	cur->master_call = cur;
 	q931_party_number_init(&cur->redirection_number);
 	q931_party_address_init(&cur->called);
 	q931_party_id_init(&cur->local_id);
@@ -3144,7 +3165,7 @@
 	q931_party_redirecting_init(&cur->redirecting);
 
 	/* PRI is set to whoever called us */
-	if (q931_is_ptmp(ctrl) && (ctrl->localtype == PRI_CPE)) {
+	if (BRI_TE_PTMP(ctrl)) {
 		/*
 		 * Point to the master to avoid stale pointer problems if
 		 * the TEI is removed later.
@@ -3188,17 +3209,113 @@
 	return q931_getcall(ctrl, ctrl->cref | 0x8000);
 }
 
+static void stop_t303(struct q931_call *call);
+
+static void cleanup_and_free_call(struct q931_call *cur)
+{
+	stop_t303(cur);
+	pri_schedule_del(cur->pri, cur->retranstimer);
+	pri_call_apdu_queue_cleanup(cur);
+	free(cur);
+}
+
+static void pri_create_fake_clearing(struct q931_call *c, struct pri *master);
+
 static void q931_destroy(struct pri *ctrl, int cr, q931_call *c)
 {
 	q931_call *cur, *prev;
+	q931_call *slave = NULL;
+	int i, slavesleft = 0;
+	int slaveidx = -1;
+
+	if (c->master_call != c) {
+		slave = c;
+		c = slave->master_call;
+	}
 
 	/* For destroying, make sure we are using the master span, since it maintains the call pool */
-	for (;ctrl->master; ctrl = ctrl->master);
+	ctrl = PRI_MASTER(ctrl);
 
 	prev = NULL;
 	cur = *ctrl->callpool;
 	while(cur) {
 		if ((c && (cur == c)) || (!c && (cur->cr == cr))) {
+			if (slave) {
+				for (i = 0; i < Q931_MAX_TEI; i++) {
+					if (cur->subcalls[i] == slave) {
+						pri_error(ctrl, "Destroying subcall %p of call %p at %d\n", slave, cur, i);
+						cleanup_and_free_call(slave);
+						cur->subcalls[i] = NULL;
+						slaveidx = i;
+						break;
+					}
+				}
+			}
+
+			for (i = 0; i < Q931_MAX_TEI; i++) {
+				if (cur->subcalls[i]) {
+					pri_error(ctrl, "Subcall still present at %d\n", i);
+					slavesleft++;
+				}
+			}
+
+			/* We have 3 different phases to deal with:
+			 * 1.) Sent outbound call, but no response, indicated by t203 present
+			 * 2.) Sent outbound call, with responses, indicated by lack of t203 and subcalls present
+			 * 3.) Outbound call connected, indicated by pri_winner > -1
+			 *
+			 * If chan_dahdi hangs up in phase:
+			 * 1.) T303 will be present, and we will fake clear in this case
+			 * 2.) pri_winner will be < 0 and subcalls will be present.
+			 * 3.) pri_winner will be > -1 and we will free the master when the winner dies.
+			 *
+			 * If remote ends hang up in phase:
+			 * 1.) Impossible, defined by phase.
+			 * 2.) When last end hangs up, we should cause a fake clearing.
+			 * 3.) Pass events to winner up and be freed when winner is freed
+			 *
+			 * Exceptional conditions in phase:
+			 * 1.) None.
+			 * 2.) None.
+			 * 3.) We hang up a call so quickly that it hangs up before other competing lines finish hangup sequence
+			 * 	Subcalls present still even though we have hung up the winner.
+			 *
+			 * 	So, we could say:
+			 * 	If, when the library user hangs up the master call, and there are more than one subcall up, we fake clear
+			 * 	regardless of whether or not we drop down to one subcall left in the clearing process.
+			 *
+			 * 	If there are only one call up, we mirror what it does.
+			 *
+			 * 	OR
+			 *
+			 * 	Phase 2. them clearing:
+			 * 	For handling of Phase 2 (indicated by not running and pri_winner not present):
+			 * 	We create a fake hangup sequence after all the subcalls have been destroyed and after
+			 *
+			 * 	"" us clearing:
+			 * 	For we need to start the fake clearing, but it needs to be half of a fake clearing, not a full one (since we already had a hangup).
+			 *
+			 * 	For handling of Phase 3 plus exceptions:
+			 *
+			 * 	If pri_winner exists, we mirror him in terms of events (which provides our hangup sequence), and when we have the complete
+			 * 	hangup sequence completed (destroy called on master call), if there still exist non winner subcalls at this time, we declare the master
+			 * 	call as dead and free it when the last subcall clears.
+			 */
+
+			if ((slave && !slavesleft) &&
+				((cur->pri_winner < 0) || (slave && slaveidx != cur->pri_winner))) {
+				pri_create_fake_clearing(cur, ctrl);
+				return;
+			}
+
+			if (slavesleft) {
+				return;
+			}
+
+			/* Master call or normal call destruction. */
+			if ((cur->pri_winner > -1) && cur->outboundbroadcast) {
+				pri_error(ctrl, "Since we already had a winner, we should just be able to kill the call anyways\n");
+			}
 			if (prev)
 				prev->next = cur->next;
 			else
@@ -3209,10 +3326,8 @@
 					q931_call_state_str(cur->ourcallstate),
 					q931_call_state_str(cur->peercallstate),
 					q931_hold_state_str(cur->hold_state));
-			pri_schedule_del(ctrl, cur->retranstimer);
 			pri_schedule_del(ctrl, cur->hold_timer);
-			pri_call_apdu_queue_cleanup(cur);
-			free(cur);
+			cleanup_and_free_call(cur);
 			return;
 		}
 		prev = cur;
@@ -3221,10 +3336,12 @@
 	pri_error(ctrl, "Can't destroy call %d!\n", cr);
 }
 
+#if 0
 static void q931_destroycall(struct pri *ctrl, int cr)
 {
 	q931_destroy(ctrl, cr, NULL);
 }
+#endif
 
 
 void __q931_destroycall(struct pri *ctrl, q931_call *call) 
@@ -3408,9 +3525,20 @@
 	*mhb = mh;
 }
 
-static int q931_xmit(struct pri *ctrl, q931_h *h, int len, int cr)
-{
-	q921_transmit_iframe(ctrl, h, len, cr);
+static int q931_xmit(struct pri *ctrl, q931_h *h, int len, int cr, int uiframe)
+{
+	/* 
+	 * For NT-PTMP mode, we need to check the following:
+	 * MODE = NT-PTMP
+	 * MESSAGE = SETUP
+	 *
+	 * If those are true, we need to send the SETUP in a UI frame
+	 * instead of an I-frame.
+	 */
+	if (BRI_NT_PTMP(ctrl) && uiframe)
+		q921_transmit_uiframe(ctrl, h, len);
+	else
+		q921_transmit_iframe(ctrl, h, len, cr);
 	/* The transmit operation might dump the q921 header, so logging the q931
 	   message body after the transmit puts the sections of the message in the
 	   right order in the log */
@@ -3447,6 +3575,13 @@
 	int offset=0;
 	int x;
 	int codeset;
+
+	if (call->outboundbroadcast && call->master_call == call && msgtype != Q931_SETUP) {
+		pri_error(ctrl,
+			"Attempting to use master call record to send %s on BRI PTMP NT %p\n",
+			msg2str(msgtype), ctrl);
+		return -1;
+	}
 
 	memset(buf, 0, sizeof(buf));
 	len = sizeof(buf);
@@ -3469,7 +3604,7 @@
 	len = sizeof(buf) - len;
 
 	ctrl = call->pri;
-	if (q931_is_ptmp(ctrl) && (ctrl->localtype == PRI_CPE)) {
+	if (BRI_TE_PTMP(ctrl)) {
 		/*
 		 * Must use the BRI subchannel structure to send with the correct TEI.
 		 * Note: If the subchannel is NULL then there is no TEI assigned and
@@ -3478,7 +3613,8 @@
 		ctrl = ctrl->subchannel;
 	}
 	if (ctrl) {
-		q931_xmit(ctrl, h, len, 1);
+		pri_message(ctrl, "Sending message for call %p on %p TEI/SAPI %d/%d, call->pri is %p, TEI/SAPI %d/%d\n", call, ctrl, ctrl->tei, ctrl->sapi, call->pri, call->pri->tei, call->pri->sapi);
+		q931_xmit(ctrl, h, len, 1, (msgtype == Q931_SETUP) ? 1 : 0);
 	}
 	call->acked = 1;
 	return 0;
@@ -3601,7 +3737,8 @@
 static int notify_ies[] = { Q931_IE_NOTIFY_IND, Q931_IE_REDIRECTION_NUMBER, -1 };
 
 /*!
- * \brief Send a NOTIFY message with optional redirection number.
+ * \internal
+ * \brief Actually send a NOTIFY message with optional redirection number.
  *
  * \param ctrl D channel controller.
  * \param call Q.931 call leg
@@ -3611,7 +3748,7 @@
  * \retval 0 on success.
  * \retval -1 on error.
  */
-int q931_notify_redirection(struct pri *ctrl, q931_call *call, int notify, const struct q931_party_number *number)
+static int q931_notify_redirection_helper(struct pri *ctrl, q931_call *call, int notify, const struct q931_party_number *number)
 {
 	if (number) {
 		call->redirection_number = *number;
@@ -3620,6 +3757,48 @@
 	}
 	call->notify = notify;
 	return send_message(ctrl, call, Q931_NOTIFY, notify_ies);
+}
+
+/*!
+ * \brief Send a NOTIFY message with optional redirection number.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg
+ * \param notify Notification indicator
+ * \param number Redirection number to send if not NULL.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int q931_notify_redirection(struct pri *ctrl, q931_call *call, int notify, const struct q931_party_number *number)
+{
+	int status;
+	unsigned idx;
+	struct q931_call *subcall;
+
+	if (call->outboundbroadcast && call->master_call == call) {
+		status = 0;
+		for (idx = 0; idx < Q931_MAX_TEI; ++idx) {
+			subcall = call->subcalls[idx];
+			if (subcall) {
+				/* Send to all subcalls that have given a positive response. */
+				switch (subcall->ourcallstate) {
+				case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING:
+				case Q931_CALL_STATE_CALL_DELIVERED:
+				case Q931_CALL_STATE_ACTIVE:
+					if (q931_notify_redirection_helper(ctrl, subcall, notify, number)) {
+						status = -1;
+					}
+					break;
+				default:
+					break;
+				}
+			}
+		}
+	} else {
+		status = q931_notify_redirection_helper(ctrl, call, notify, number);
+	}
+	return status;
 }
 
 int q931_notify(struct pri *ctrl, q931_call *c, int channel, int info)
@@ -3821,10 +4000,12 @@
 	ctrl->ev.hangup.channel = q931_encode_channel(c);
 	ctrl->ev.hangup.cause = c->cause;
 	ctrl->ev.hangup.cref = c->cr;
-	ctrl->ev.hangup.call = c;
+	ctrl->ev.hangup.call = c->master_call;
 	ctrl->ev.hangup.aoc_units = c->aoc_units;
+	ctrl->ev.hangup.call_held = NULL;
+	ctrl->ev.hangup.call_active = NULL;
 	libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo));
-	q931_hangup(ctrl, c, c->cause);
+	pri_hangup(ctrl, c, c->cause);
 }
 
 /* T305 expiry, first time */
@@ -3966,6 +4147,55 @@
 
 /*! Call Independent Signalling SETUP ie's */
 static int cis_setup_ies[] = { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, Q931_IE_FACILITY, Q931_CALLING_PARTY_NUMBER, Q931_CALLED_PARTY_NUMBER, Q931_SENDING_COMPLETE, -1 };
+
+static void stop_t303(struct q931_call *call)
+{
+	/* T303 should only be running on the master call */
+	pri_schedule_del(call->master_call->pri, call->master_call->t303_timer);
+	call->master_call->t303_timer = 0;
+}
+
+static void t303_expiry(void *data);
+
+static void start_t303(struct q931_call *call)
+{
+	if (call->t303_timer) {
+		pri_error(call->pri, "Should not have T303 set when starting again.  Stopping first\n");
+		stop_t303(call);
+	}
+
+	//pri_error(call->pri, "T303 should be %d\n", call->pri->timers[PRI_TIMER_T303]);
+	call->t303_timer = pri_schedule_event(call->pri, call->pri->timers[PRI_TIMER_T303], t303_expiry, call);
+}
+
+static void pri_fake_clearing(void *data);
+
+static void t303_expiry(void *data)
+{
+	struct q931_call *c = data;
+	struct pri *ctrl = c->pri;
+	int res;
+
+	c->t303_expirycnt++;
+	c->t303_timer = 0;
+
+	if (c->t303_expirycnt < 2) {
+		if (ctrl->subchannel && !ctrl->bri)
+			res = send_message(ctrl, c, Q931_SETUP, gr303_setup_ies);
+		else if (c->cis_call)
+			res = send_message(ctrl, c, Q931_SETUP, cis_setup_ies);
+		else
+			res = send_message(ctrl, c, Q931_SETUP, setup_ies);
+
+		if (res) {
+			pri_error(c->pri, "Error resending setup message!\n");
+		}
+		start_t303(c);
+	} else {
+		c->cause = PRI_CAUSE_NO_USER_RESPONSE;
+		pri_fake_clearing(c);
+	}
+}
 
 int q931_setup(struct pri *ctrl, q931_call *c, struct pri_sr *req)
 {
@@ -4044,6 +4274,11 @@
 		c->sendhangupack = 1;
 		UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_INITIATED);
 		c->peercallstate = Q931_CALL_STATE_OVERLAP_SENDING;	
+		c->t303_expirycnt = 0;
+		if (BRI_NT_PTMP(ctrl)) {
+			c->outboundbroadcast = 1;
+		}
+		start_t303(c);
 	}
 	return res;
 	
@@ -4066,7 +4301,7 @@
 		res = send_message(ctrl, c, Q931_RELEASE_COMPLETE, release_complete_ies);
 	c->alive = 0;
 	/* release the structure */
-	res += q931_hangup(ctrl,c,cause);
+	res += pri_hangup(ctrl,c,cause);
 	return res;
 }
 
@@ -4086,9 +4321,35 @@
 
 /*!
  * \internal
+ * \brief Find the winning subcall if it exists or current call if not outboundbroadcast.
+ *
+ * \param call Starting Q.931 call record of search.
+ *
+ * \retval winning-call or given call if not outboundbroadcast.
+ * \retval NULL if no winning call yet.
+ */
+static struct q931_call *q931_find_winning_call(struct q931_call *call)
+{
+	struct q931_call *master;
+
+	master = call->master_call;
+	if (master->outboundbroadcast) {
+		/* We have potential subcalls.  Now get the winning call if declared yet. */
+		if (master->pri_winner < 0) {
+			/* Winner not declared yet.*/
+			call = NULL;
+		} else {
+			call = master->subcalls[master->pri_winner];
+		}
+	}
+	return call;
+}
+
+/*!
+ * \internal
  * \brief Send HOLD message response wait timeout.
  *
- * \param data Q.931 call leg.
+ * \param data Q.931 call leg. (Master Q.931 subcall structure)
  *
  * \return Nothing
  */
@@ -4121,12 +4382,12 @@
  * \brief Determine if a hold request is allowed now.
  *
  * \param ctrl D channel controller.
- * \param call Q.931 call leg
+ * \param call Q.931 call leg. (Master Q.931 subcall structure)
  *
  * \retval TRUE if we can send a HOLD request.
  * \retval FALSE if not allowed.
  */
-static int q931_is_hold_allowed(const struct pri *ctrl, const q931_call *call)
+static int q931_is_hold_allowed(const struct pri *ctrl, const struct q931_call *call)
 {
 	int allowed;
 
@@ -4170,20 +4431,23 @@
  * \brief Send the HOLD message.
  *
  * \param ctrl D channel controller.
- * \param call Q.931 call leg
+ * \param call Q.931 call leg. (Master Q.931 subcall structure)
  *
  * \retval 0 on success.
  * \retval -1 on error.
  */
-int q931_send_hold(struct pri *ctrl, q931_call *call)
-{
-	if (!q931_is_hold_allowed(ctrl, call)) {
+int q931_send_hold(struct pri *ctrl, struct q931_call *call)
+{
+	struct q931_call *winner;
+
+	winner = q931_find_winning_call(call);
+	if (!winner || !q931_is_hold_allowed(ctrl, call)) {
 		return -1;
 	}
 	pri_schedule_del(ctrl, call->hold_timer);
 	call->hold_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T_HOLD],
 		q931_hold_timeout, call);
-	if (send_message(ctrl, call, Q931_HOLD, hold_ies)) {
+	if (send_message(ctrl, winner, Q931_HOLD, hold_ies)) {
 		pri_schedule_del(ctrl, call->hold_timer);
 		call->hold_timer = 0;
 		return -1;
@@ -4200,26 +4464,33 @@
  * \brief Send the HOLD ACKNOWLEDGE message.
  *
  * \param ctrl D channel controller.
- * \param call Q.931 call leg
+ * \param call Q.931 call leg. (Master Q.931 subcall structure)
  *
  * \retval 0 on success.
  * \retval -1 on error.
  */
-int q931_send_hold_ack(struct pri *ctrl, q931_call *call)
-{
+int q931_send_hold_ack(struct pri *ctrl, struct q931_call *call)
+{
+	struct q931_call *winner;
+
 	/* Stop the upper layer does not implement guard timer. */
 	pri_schedule_del(ctrl, call->hold_timer);
 	call->hold_timer = 0;
 
 	UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_CALL_HELD);
 
+	winner = q931_find_winning_call(call);
+	if (!winner) {
+		return -1;
+	}
+
 	/* Call is now on hold so forget the channel. */
-	call->channelno = 0;/* No channel */
-	call->ds1no = 0;
-	call->ds1explicit = 0;
-	call->chanflags = 0;
-
-	return send_message(ctrl, call, Q931_HOLD_ACKNOWLEDGE, hold_ack_ies);
+	winner->channelno = 0;/* No channel */
+	winner->ds1no = 0;
+	winner->ds1explicit = 0;
+	winner->chanflags = 0;
+
+	return send_message(ctrl, winner, Q931_HOLD_ACKNOWLEDGE, hold_ack_ies);
 }
 
 static int hold_reject_ies[] = {
@@ -4228,23 +4499,18 @@
 };
 
 /*!
- * \brief Send the HOLD REJECT message.
+ * \internal
+ * \brief Send the HOLD REJECT message only.
  *
  * \param ctrl D channel controller.
- * \param call Q.931 call leg
+ * \param call Q.931 call leg. (subcall)
  * \param cause Q.931 cause code for rejecting the hold request.
  *
  * \retval 0 on success.
  * \retval -1 on error.
  */
-int q931_send_hold_rej(struct pri *ctrl, q931_call *call, int cause)
-{
-	/* Stop the upper layer does not implement guard timer. */
-	pri_schedule_del(ctrl, call->hold_timer);
-	call->hold_timer = 0;
-
-	UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_IDLE);
-
+static int q931_send_hold_rej_msg(struct pri *ctrl, struct q931_call *call, int cause)
+{
 	call->cause = cause;
 	call->causecode = CODE_CCITT;
 	call->causeloc = LOC_PRIV_NET_LOCAL_USER;
@@ -4252,10 +4518,38 @@
 }
 
 /*!
+ * \brief Send the HOLD REJECT message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg. (Master Q.931 subcall structure)
+ * \param cause Q.931 cause code for rejecting the hold request.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int q931_send_hold_rej(struct pri *ctrl, struct q931_call *call, int cause)
+{
+	struct q931_call *winner;
+
+	/* Stop the upper layer does not implement guard timer. */
+	pri_schedule_del(ctrl, call->hold_timer);
+	call->hold_timer = 0;
+
+	UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_IDLE);
+
+	winner = q931_find_winning_call(call);
+	if (!winner) {
+		return -1;
+	}
+
+	return q931_send_hold_rej_msg(ctrl, winner, cause);
+}
+
+/*!
  * \internal

[... 1273 lines stripped ...]



More information about the libpri-commits mailing list