[libpri-commits] rmudgett: branch rmudgett/ntptmp r1089 - /team/rmudgett/ntptmp/

SVN commits to the libpri project libpri-commits at lists.digium.com
Wed Sep 16 19:10:12 CDT 2009


Author: rmudgett
Date: Wed Sep 16 19:10:08 2009
New Revision: 1089

URL: http://svn.asterisk.org/svn-view/libpri?view=rev&rev=1089
Log:
Merged team/mattf/libpri-1.4-ntptmp branch up to -r1071.

Modified:
    team/rmudgett/ntptmp/   (props changed)
    team/rmudgett/ntptmp/libpri.h
    team/rmudgett/ntptmp/pri.c
    team/rmudgett/ntptmp/pri_internal.h
    team/rmudgett/ntptmp/pri_q921.h
    team/rmudgett/ntptmp/q921.c
    team/rmudgett/ntptmp/q931.c

Propchange: team/rmudgett/ntptmp/
------------------------------------------------------------------------------
    svnmerge-integrated = /branches/1.4:1-1088

Modified: team/rmudgett/ntptmp/libpri.h
URL: http://svn.asterisk.org/svn-view/libpri/team/rmudgett/ntptmp/libpri.h?view=diff&rev=1089&r1=1088&r2=1089
==============================================================================
--- team/rmudgett/ntptmp/libpri.h (original)
+++ team/rmudgett/ntptmp/libpri.h Wed Sep 16 19:10:08 2009
@@ -802,7 +802,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/ntptmp/pri.c
URL: http://svn.asterisk.org/svn-view/libpri/team/rmudgett/ntptmp/pri.c?view=diff&rev=1089&r1=1088&r2=1089
==============================================================================
--- team/rmudgett/ntptmp/pri.c (original)
+++ team/rmudgett/ntptmp/pri.c Wed Sep 16 19:10:08 2009
@@ -150,6 +150,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 */
 
 	/* Set any switch specific override default values */
 	switch (switchtype) {
@@ -223,7 +224,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)
@@ -838,14 +839,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)
@@ -1014,7 +1024,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);
 }
@@ -1027,7 +1037,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/ntptmp/pri_internal.h
URL: http://svn.asterisk.org/svn-view/libpri/team/rmudgett/ntptmp/pri_internal.h?view=diff&rev=1089&r1=1088&r2=1089
==============================================================================
--- team/rmudgett/ntptmp/pri_internal.h (original)
+++ team/rmudgett/ntptmp/pri_internal.h Wed Sep 16 19:10:08 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 */
@@ -468,6 +469,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);
@@ -512,4 +525,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/ntptmp/pri_q921.h
URL: http://svn.asterisk.org/svn-view/libpri/team/rmudgett/ntptmp/pri_q921.h?view=diff&rev=1089&r1=1088&r2=1089
==============================================================================
--- team/rmudgett/ntptmp/pri_q921.h (original)
+++ team/rmudgett/ntptmp/pri_q921.h Wed Sep 16 19:10:08 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/ntptmp/q921.c
URL: http://svn.asterisk.org/svn-view/libpri/team/rmudgett/ntptmp/q921.c?view=diff&rev=1089&r1=1088&r2=1089
==============================================================================
--- team/rmudgett/ntptmp/q921.c (original)
+++ team/rmudgett/ntptmp/q921.c Wed Sep 16 19:10:08 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/ntptmp/q931.c
URL: http://svn.asterisk.org/svn-view/libpri/team/rmudgett/ntptmp/q931.c?view=diff&rev=1089&r1=1088&r2=1089
==============================================================================
--- team/rmudgett/ntptmp/q931.c (original)
+++ team/rmudgett/ntptmp/q931.c Wed Sep 16 19:10:08 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 } },
@@ -819,7 +821,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;
@@ -3025,17 +3027,13 @@
 	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) {
-			return cur;
+			goto done;
 		}
 		prev = cur;
 		cur = cur->next;
@@ -3075,29 +3073,24 @@
 	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);
 	q931_party_id_init(&cur->remote_id);
 	q931_party_redirecting_init(&cur->redirecting);
 
-	/* PRI is set to whoever called us */
-	if (q931_is_ptmp(ctrl) && (ctrl->localtype == PRI_CPE)) {
-		/*
-		 * Point to the master to avoid stale pointer problems if
-		 * the TEI is removed later.
-		 */
-		cur->pri = master;
-	} else {
-		cur->pri = ctrl;
-	}
-
 	/* Append to end of list */
 	if (prev) {
 		prev->next = cur;
 	} else {
 		*master->callpool = cur;
 	}
+
+done:
+	/* PRI is set to whoever called us */
+	cur->pri = ctrl;
 
 	return cur;
 }
@@ -3126,17 +3119,114 @@
 	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 (c->subcalls[i] == slave) {
+						pri_error(ctrl, "Destroying subcall %p of call %p at %d\n", slave, c, i);
+						cleanup_and_free_call(slave);
+						c->subcalls[i] = NULL;
+						slaveidx = i;
+						break;
+					}
+				}
+			}
+
+			for (i = 0; i < Q931_MAX_TEI; i++) {
+				if (c->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) &&
+				((c->pri_winner < 0) || (slave && slaveidx != c->pri_winner))) {
+				pri_create_fake_clearing(c, ctrl);
+				return;
+			}
+
+			if (!slavesleft)
+				goto ultimate_destruction;
+
+			return;
+
+ultimate_destruction:
+			if ((c->pri_winner > -1) && c->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
@@ -3146,9 +3236,7 @@
 					"NEW_HANGUP DEBUG: Destroying the call, ourstate %s, peerstate %s\n",
 					q931_call_state_str(cur->ourcallstate),
 					q931_call_state_str(cur->peercallstate));
-			pri_schedule_del(ctrl, cur->retranstimer);
-			pri_call_apdu_queue_cleanup(cur);
-			free(cur);
+			cleanup_and_free_call(c);
 			return;
 		}
 		prev = cur;
@@ -3157,10 +3245,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) 
@@ -3344,9 +3434,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 */
@@ -3414,7 +3515,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(call->pri, h, len, 1, (msgtype == Q931_SETUP) ? 1 : 0);
 	}
 	call->acked = 1;
 	return 0;
@@ -3757,10 +3859,10 @@
 	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;
 	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 */
@@ -3902,6 +4004,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 = 18;
+		pri_fake_clearing(c);
+	}
+}
 
 int q931_setup(struct pri *ctrl, q931_call *c, struct pri_sr *req)
 {
@@ -3980,6 +4131,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;
 	
@@ -4002,7 +4158,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;
 }
 
@@ -4020,10 +4176,34 @@
 	return 0;
 }
 
-int q931_hangup(struct pri *ctrl, q931_call *c, int cause)
+static int pri_internal_clear(void *data);
+
+/* Fake RELEASE for NT-PTMP initiated SETUPs w/o response */
+static void pri_fake_clearing(void *data)
+{
+	struct q931_call *c = data;
+	struct pri *ctrl = c->pri;
+
+	c->performing_fake_clearing = 1;
+	if (pri_internal_clear(c) == Q931_RES_HAVEEVENT)
+		ctrl->schedev = 1;
+}
+
+static void pri_create_fake_clearing(struct q931_call *c, struct pri *master)
+{
+	c->pri = master;
+
+	pri_schedule_del(master, c->retranstimer);
+	c->retranstimer = pri_schedule_event(master, 0, pri_fake_clearing, c);
+}
+
+//static int q931_get_subcall_count(struct q931_call *call);
+
+static int __q931_hangup(struct pri *ctrl, q931_call *c, int cause)
 {
 	int disconnect = 1;
 	int release_compl = 0;
+	int t303_was_running = c->master_call->t303_timer;
 
 	if (ctrl->debug & PRI_DEBUG_Q931_STATE)
 		pri_message(ctrl,
@@ -4058,16 +4238,28 @@
 		disconnect = 0;
 	}
 
+	c->hangupinitiated = 1;
+	stop_t303(c);
+
 	/* All other causes we send with DISCONNECT */
 	switch(c->ourcallstate) {
 	case Q931_CALL_STATE_NULL:
 		if (c->peercallstate == Q931_CALL_STATE_NULL)
 			/* free the resources if we receive or send REL_COMPL */
-			q931_destroycall(ctrl, c->cr);
+			__q931_destroycall(ctrl, c);
 		else if (c->peercallstate == Q931_CALL_STATE_RELEASE_REQUEST)
 			q931_release_complete(ctrl,c,cause);
 		break;
 	case Q931_CALL_STATE_CALL_INITIATED:
+	if (BRI_NT_PTMP(ctrl) && (c->master_call == c) && c->outboundbroadcast && t303_was_running) {
+		//c->fakeclearing = 1;
+		//c->alive = 0;
+		/* We need to fake a received clearing sequence in this case... */
+		pri_error(ctrl, "Faking clearing\n");
+		pri_create_fake_clearing(c, PRI_MASTER(ctrl));
+		/* This means that we never got a response from a TEI */
+		return 0;
+	}
 		/* we sent SETUP */
 	case Q931_CALL_STATE_OVERLAP_SENDING:
 		/* received SETUP_ACKNOWLEDGE */
@@ -4151,6 +4343,46 @@
 	return 0;
 }
 
+static void initiate_hangup_if_needed(struct pri *pri, q931_call *call, int cause);
+
+int q931_hangup(struct pri *ctrl, q931_call *call, int cause)
+{
+	int i;
+
+	/* Master is called with hangup - initiate hangup with slaves */
+	if (BRI_NT_PTMP(ctrl) && call->master_call->outboundbroadcast) {
+		if (call->master_call == call) {
+			int slaves = 0;
+			for (i = 0; i < Q931_MAX_TEI; i++) {
+				if (call->subcalls[i]) {
+					slaves++;
+					if (i == call->master_call->pri_winner) {
+						__q931_hangup(call->subcalls[i]->pri, call->subcalls[i], cause);
+					} else {
+						initiate_hangup_if_needed(call->subcalls[i]->pri, call->subcalls[i], cause);
+					}
+					pri_error(ctrl, "%s: Hanging up %d, winner %d\n", __FUNCTION__, i, call->master_call->pri_winner);
+				}
+			}
+
+			call->hangupinitiated = 1;
+
+			if ((!slaves && (call->master_call->pri_winner < 0)) || (call->performing_fake_clearing)) {
+				__q931_hangup(ctrl, call, cause);
+			}
+			pri_error(ctrl, "%s: Slaves %d\n", __FUNCTION__, slaves);
+			return 0;
+		} else {
+			pri_error(ctrl, "%s: Slave hangup\n", __FUNCTION__);
+			return __q931_hangup(ctrl, call, cause);
+		}
+	} else {
+		pri_error(ctrl, "%s: other hangup\n", __FUNCTION__);
+		return __q931_hangup(ctrl, call, cause);
+	}
+	return 0;
+}
+
 static int prepare_to_handle_maintenance_message(struct pri *ctrl, q931_mh *mh, q931_call *c)
 {
 	if ((!ctrl) || (!mh) || (!c)) {
@@ -4307,10 +4539,106 @@
 		pri_error(ctrl, "!! Don't know how to pre-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg);
 		q931_status(ctrl,c, PRI_CAUSE_MESSAGE_TYPE_NONEXIST);
 		if (c->newcall) 
-			q931_destroycall(ctrl,c->cr);
+			__q931_destroycall(ctrl, c);
 		return -1;
 	}
 	return 0;
+}
+
+static struct q931_call * q931_get_subcall_winner(struct q931_call *call)
+{
+	if (call->master_call->pri_winner < 0) {
+		return NULL;
+	} else 
+		return call->subcalls[call->pri_winner];
+
+}
+
+static void initiate_hangup_if_needed(struct pri *pri, q931_call *call, int cause)
+{
+	if (!call->hangupinitiated) {
+		q931_hangup(pri, call, cause);
+		call->alive = 0;
+	}
+}
+
+#if 0
+static int q931_get_subcall_count(struct q931_call *call)
+{
+	int count = 0;
+	int i;
+	call = call->master_call;
+
+	for (i = 0; i < Q931_MAX_TEI; i++) {
+		if (call->subcalls[i])
+			count++;
+	}
+
+	return count;
+}
+#endif
+
+static void q931_set_subcall_winner(struct q931_call *subcall)
+{
+	struct q931_call *realcall = subcall->master_call;
+	int i;
+
+	/* Set the winner first */
+	for (i = 0; i < Q931_MAX_TEI; i++) {
+		if (realcall->subcalls[i] && realcall->subcalls[i] == subcall) {
+			realcall->pri_winner = i;
+		}
+	}
+
+	if (realcall->pri_winner < 0) {
+		pri_message(subcall->pri, "We should always find the winner in the list!\n");
+		return;
+	}
+
+	/* Start tear down of calls that were not chosen */
+	for (i = 0; i < Q931_MAX_TEI; i++) {
+		if (realcall->subcalls[i] && realcall->subcalls[i] != subcall) {
+			initiate_hangup_if_needed(realcall->subcalls[i]->pri, realcall->subcalls[i], 26);
+		}
+	}
+}
+
+static struct q931_call * q931_get_subcall(struct q931_call *call, struct pri *ctrl)
+{
+	int i;
+	struct q931_call *cur = NULL;
+	int firstfree = -1;
+
+	/* First try to locate our subcall */
+	for (i = 0; i < Q931_MAX_TEI; i++) {
+		if (call->subcalls[i]) {
+			if (call->subcalls[i]->pri == ctrl) {
+				return call->subcalls[i];
+			}
+		} else if (firstfree == -1)
+			firstfree = i;
+	}
+
+	if (firstfree < 0) {
+		pri_error(ctrl, "Tried to add more than %d TEIs to call and failed\n", Q931_MAX_TEI);
+		return NULL;
+	}
+
+	cur = malloc(sizeof(*cur));
+	if (!cur) {
+		pri_error(ctrl, "Unable to allocate call\n");
+		return NULL;
+	}
+	*cur = *call;
+	call->subcalls[firstfree] = cur;
+	cur->pri = ctrl;
+	cur->master_call = call;
+	/* T303 should only be on on the master call */
+	cur->t303_timer = 0;
+	pri_error(ctrl, "Adding subcall %p for TEI %d to call %p at position %d\n", cur, ctrl->tei, call, firstfree);
+	/* Should only get here if the TEI is not found */
+	return cur;
+
 }
 
 int q931_receive(struct pri *ctrl, q931_h *h, int len)
@@ -4347,7 +4675,7 @@
 		   KLUDGE this by changing byte 4 from a 0xf (SERVICE) 
 		   to a 0x7 (SERVICE ACKNOWLEDGE) */
 		h->raw[h->crlen + 2] -= 0x8;
-		q931_xmit(ctrl, h, len, 1);
+		q931_xmit(ctrl, h, len, 1, 0);
 		return 0;
 	}
 	cref = q931_cr(h);
@@ -4356,6 +4684,16 @@
 		pri_error(ctrl, "Unable to locate call %d\n", cref);
 		return -1;
 	}
+
+	if (BRI_NT_PTMP(ctrl) && (ctrl != PRI_MASTER(ctrl)) && !c->newcall && c->outboundbroadcast) {
+		c = q931_get_subcall(c, ctrl);
+		if (!c) {
+			pri_error(ctrl, "Unable to locate subcall %d\n", cref);
+			return -1;
+		}
+	}
+
+	pri_message(ctrl, "Received message for call %p on %p TEI/SAPI %d/%d, call->pri is %p TEI/SAPI %d/%d\n", c, ctrl, ctrl->tei, ctrl->sapi, c->pri, c->pri->tei, c->pri->sapi);
 	/* Preliminary handling */
 	if ((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) {
 		prepare_to_handle_maintenance_message(ctrl, mh, c);
@@ -4455,7 +4793,21 @@
 	if ((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) {
 		res = post_handle_maintenance_message(ctrl, h->pd, mh, c);
 	} else {
-		res = post_handle_q931_message(ctrl, mh, c, missingmand);
+		int allow_event = 1, allow_posthandle = 1;
+
+		if (BRI_NT_PTMP(ctrl) && c->master_call->outboundbroadcast) {
+			nt_ptmp_handle_q931_message(ctrl, mh, c, &allow_event, &allow_posthandle);
+		}
+
+		if (allow_posthandle) {
+			res = post_handle_q931_message(ctrl, mh, c, missingmand);
+
+			if (res == Q931_RES_HAVEEVENT && !allow_event) {
+				res = 0;
+			}
+		} else {
+			res = 0;
+		}
 	}
 	return res;
 }
@@ -4515,6 +4867,67 @@
 	return -1;
 }
 
+/* This is where we interact the subcalls state with the master_call's state */
+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 q931_call *master = c->master_call;
+	struct q931_call *winner = q931_get_subcall_winner(master);
+	int newstate;
+
+	/* For broadcast calls, we default to not allowing events to keep events received to a minimum
+	 * and to allow post processing, since that is where hangup and subcall state handling and other processing is done */
+	*allow_event = 0;
+	*allow_posthandle = 1;
+	switch (mh->msg) {
+	case Q931_CALL_PROCEEDING:
+		if (master->ourcallstate < Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING) {
+			*allow_event = 1;
+			UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING);
+		}
+		break;
+	case Q931_ALERTING:
+		if (master->ourcallstate < Q931_CALL_STATE_CALL_DELIVERED) {
+			*allow_event = 1;
+			UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_CALL_DELIVERED);
+		}
+		break;
+	case Q931_CONNECT:
+		if (!winner) {
+			UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_ACTIVE);
+			q931_set_subcall_winner(c);
+			*allow_event = 1;
+		} else {
+			/* Call clearing occurs of non selected calls occurs in
+			 * q931_set_subcall_winner() - All we need to do is make sure
+			 * that this connect is not acknowledged */
+			*allow_posthandle = 0;
+		}
+		break;
+	case Q931_DISCONNECT:
+		newstate = Q931_CALL_STATE_DISCONNECT_INDICATION;
+		goto process_hangup;
+	case Q931_RELEASE:
+	case Q931_RELEASE_COMPLETE:
+		newstate = Q931_CALL_STATE_NULL;
+process_hangup:
+		if (!winner) {
+			/* If there's not a winner, we just take the cause and pass it up to the
+			 * master_call */
+			master->cause = c->cause;
+		} else {
+			/* There *is* a winner */
+			if (c == winner) {
+				/* .. and we're it: */
+				*allow_event = 1;
+				UPDATE_OURCALLSTATE(ctrl, master, newstate);
+			}
+		}
+		break;
+	default:
+		break;
+	}
+}
+
 /*!
  * \internal
  * \brief Fill in the FACILITY event fields.
@@ -4530,7 +4943,7 @@
 	ctrl->ev.facility.subcmds = &ctrl->subcmds;
 	ctrl->ev.facility.channel = q931_encode_channel(call);
 	ctrl->ev.facility.cref = call->cr;
-	ctrl->ev.facility.call = call;
+	ctrl->ev.facility.call = call->master_call;
 
 	/* Need to do this for backward compatibility with struct pri_event_facname */
 	libpri_copy_string(ctrl->ev.facility.callingname, call->remote_id.name.str,
@@ -4564,7 +4977,7 @@
 	case Q931_RESTART:
 		if (missingmand) {
 			q931_status(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING);
-			q931_destroycall(ctrl, c->cr);
+			__q931_destroycall(ctrl, c);
 			break;
 		}
 		UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_RESTART);
@@ -4670,7 +5083,7 @@
 
 		ctrl->ev.ring.flexible = ! (c->chanflags & FLAG_EXCLUSIVE);
 		ctrl->ev.ring.cref = c->cr;
-		ctrl->ev.ring.call = c;
+		ctrl->ev.ring.call = c->master_call;
 		ctrl->ev.ring.layer1 = c->userl1;
 		ctrl->ev.ring.complete = c->complete; 
 		ctrl->ev.ring.ctype = c->transcapability;
@@ -4690,6 +5103,7 @@
 
 		return Q931_RES_HAVEEVENT;
 	case Q931_ALERTING:
+		stop_t303(c);
 		if (c->newcall) {
 			q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
 			break;
@@ -4700,7 +5114,7 @@
 		ctrl->ev.ringing.subcmds = &ctrl->subcmds;
 		ctrl->ev.ringing.channel = q931_encode_channel(c);
 		ctrl->ev.ringing.cref = c->cr;
-		ctrl->ev.ringing.call = c;
+		ctrl->ev.ringing.call = c->master_call;
 		ctrl->ev.ringing.progress = c->progress;
 		ctrl->ev.ringing.progressmask = c->progressmask;
 
@@ -4718,6 +5132,7 @@
 
 		return Q931_RES_HAVEEVENT;
 	case Q931_CONNECT:
+		stop_t303(c);
 		if (c->newcall) {
 			q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
 			break;
@@ -4733,7 +5148,7 @@
 		ctrl->ev.answer.subcmds = &ctrl->subcmds;
 		ctrl->ev.answer.channel = q931_encode_channel(c);
 		ctrl->ev.answer.cref = c->cr;
-		ctrl->ev.answer.call = c;
+		ctrl->ev.answer.call = c->master_call;
 		ctrl->ev.answer.progress = c->progress;
 		ctrl->ev.answer.progressmask = c->progressmask;
 		libpri_copy_string(ctrl->ev.answer.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.answer.useruserinfo));
@@ -4782,13 +5197,14 @@
 	case Q931_PROGRESS:
 		if (missingmand) {
 			q931_status(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING);
-			q931_destroycall(ctrl, c->cr);
+			__q931_destroycall(ctrl, c);
 			break;
 		}
 		ctrl->ev.e = PRI_EVENT_PROGRESS;
 		ctrl->ev.proceeding.cause = c->cause;
 		/* Fall through */
 	case Q931_CALL_PROCEEDING:
+		stop_t303(c);
 		ctrl->ev.proceeding.subcmds = &ctrl->subcmds;
 		if (c->newcall) {
 			q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
@@ -4810,7 +5226,7 @@
 		ctrl->ev.proceeding.progress = c->progress;
 		ctrl->ev.proceeding.progressmask = c->progressmask;
 		ctrl->ev.proceeding.cref = c->cr;
-		ctrl->ev.proceeding.call = c;
+		ctrl->ev.proceeding.call = c->master_call;
 
 		cur = c->apdus;
 		while (cur) {
@@ -4838,7 +5254,7 @@
 	case Q931_STATUS:
 		if (missingmand) {
 			q931_status(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING);
-			q931_destroycall(ctrl, c->cr);
+			__q931_destroycall(ctrl, c);
 			break;
 		}
 		if (c->newcall) {
@@ -4865,7 +5281,7 @@
 			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;
 			libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo));
 			/* Free resources */
@@ -4878,9 +5294,9 @@
 			} else if (c->sendhangupack) {
 				res = Q931_RES_HAVEEVENT;
 				ctrl->ev.e = PRI_EVENT_HANGUP_ACK;
-				q931_hangup(ctrl, c, c->cause);
+				pri_hangup(ctrl, c, c->cause);
 			} else {
-				q931_hangup(ctrl, c, c->cause);
+				pri_hangup(ctrl, c, c->cause);
 				res = 0;
 			}
 			if (res)
@@ -4888,13 +5304,15 @@
 		}
 		break;
 	case Q931_RELEASE_COMPLETE:
+		stop_t303(c);
+		c->hangupinitiated = 1;
 		UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL);
 		c->peercallstate = Q931_CALL_STATE_NULL;
 		ctrl->ev.hangup.subcmds = &ctrl->subcmds;
 		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;
 		libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo));
 		c->useruserinfo[0] = '\0';
@@ -4909,12 +5327,14 @@
 			pri_hangup(ctrl, c, c->cause);
 		} else
 			res = 0;
+
 		if (res)
 			return res;
 		else
-			q931_hangup(ctrl,c,c->cause);
+			pri_hangup(ctrl,c,c->cause);
 		break;
 	case Q931_RELEASE:
+		c->hangupinitiated = 1;
 		if (missingmand) {
 			/* Force cause to be mandatory IE missing */
 			c->cause = PRI_CAUSE_MANDATORY_IE_MISSING;
@@ -4930,7 +5350,7 @@
 		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;
 		libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo));
 		c->useruserinfo[0] = '\0';
@@ -4938,10 +5358,13 @@
 		   while we sent it, assume a NULL state */
 		if (c->newcall)
 			q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
-		else 
+		else if (c->outboundbroadcast && (c != q931_get_subcall_winner(c->master_call)))
+			return pri_hangup(ctrl, c, -1);
+		else
 			return Q931_RES_HAVEEVENT;
 		break;
 	case Q931_DISCONNECT:
+		c->hangupinitiated = 1;
 		if (missingmand) {
 			/* Still let user call release */
 			c->cause = PRI_CAUSE_MANDATORY_IE_MISSING;
@@ -4965,14 +5388,14 @@
 		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;
 		libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo));
 		c->useruserinfo[0] = '\0';
 		if (c->alive)
 			return Q931_RES_HAVEEVENT;
 		else
-			q931_hangup(ctrl,c,c->cause);
+			pri_hangup(ctrl,c,c->cause);
 		break;
 	case Q931_RESTART_ACKNOWLEDGE:
 		UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL);
@@ -4992,14 +5415,14 @@

[... 78 lines stripped ...]



More information about the libpri-commits mailing list