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

SVN commits to the libpri project libpri-commits at lists.digium.com
Wed Sep 2 18:20:31 CDT 2009


Author: rmudgett
Date: Wed Sep  2 18:20:27 2009
New Revision: 1067

URL: http://svn.asterisk.org/svn-view/libpri?view=rev&rev=1067
Log:
Initial HOLD/RETRIEVE message implementation.

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

Modified: team/rmudgett/facility/libpri.h
URL: http://svn.asterisk.org/svn-view/libpri/team/rmudgett/facility/libpri.h?view=diff&rev=1067&r1=1066&r2=1067
==============================================================================
--- team/rmudgett/facility/libpri.h (original)
+++ team/rmudgett/facility/libpri.h Wed Sep  2 18:20:27 2009
@@ -91,6 +91,12 @@
 #define PRI_EVENT_KEYPAD_DIGIT	18	/* When we receive during ACTIVE state (INFORMATION) */
 #define PRI_EVENT_SERVICE       19	/* SERVICE maintenance message */
 #define PRI_EVENT_SERVICE_ACK   20	/* SERVICE maintenance acknowledgement message */
+#define PRI_EVENT_HOLD			21	/* HOLD request received */
+#define PRI_EVENT_HOLD_ACK		22	/* HOLD_ACKNOWLEDGE received */
+#define PRI_EVENT_HOLD_REJ		23	/* HOLD_REJECT received */
+#define PRI_EVENT_RETRIEVE		24	/* RETRIEVE request received */
+#define PRI_EVENT_RETRIEVE_ACK	25	/* RETRIEVE_ACKNOWLEDGE received */
+#define PRI_EVENT_RETRIEVE_REJ	26	/* RETRIEVE_REJECT received */
 
 /* Simple states */
 #define PRI_STATE_DOWN		0
@@ -497,13 +503,14 @@
  * Event channel parameter encoding:
  * 3322 2222 2222 1111 1111 1100 0000 0000
  * 1098 7654 3210 9876 5432 1098 7654 3210
- * xxxx xxxx xxxx xxDC BBBBBBBBB AAAAAAAAA
+ * xxxx xxxx xxxx xEDC BBBBBBBBB AAAAAAAAA
  *
  * Bit field
  * A - B channel
  * B - Span (DS1) (0 - 127)
  * C - DS1 Explicit bit
  * D - D channel (cis_call) bit (status only)
+ * E - Call is held bit (status only)
  *
  * B channel values:
  * 0     - No channel (ISDN uses for call waiting feature)
@@ -671,6 +678,45 @@
 	int changestatus;
 } pri_event_service_ack;
 
+struct pri_event_hold {
+	int e;
+	int channel;
+	q931_call *call;
+};
+
+struct pri_event_hold_ack {
+	int e;
+	int channel;
+	q931_call *call;
+};
+
+struct pri_event_hold_rej {
+	int e;
+	int channel;
+	q931_call *call;
+	int cause;
+};
+
+struct pri_event_retrieve {
+	int e;
+	int channel;
+	q931_call *call;
+	int flexible;				/* Are we flexible with our channel selection? */
+};
+
+struct pri_event_retrieve_ack {
+	int e;
+	int channel;
+	q931_call *call;
+};
+
+struct pri_event_retrieve_rej {
+	int e;
+	int channel;
+	q931_call *call;
+	int cause;
+};
+
 typedef union {
 	int e;
 	pri_event_generic gen;		/* Generic view */
@@ -689,6 +735,12 @@
 	pri_event_service service;				/* service message */
 	pri_event_service_ack service_ack;		/* service acknowledgement message */
 	struct pri_event_facility facility;
+	struct pri_event_hold hold;
+	struct pri_event_hold_ack hold_ack;
+	struct pri_event_hold_rej hold_rej;
+	struct pri_event_retrieve retrieve;
+	struct pri_event_retrieve_ack retrieve_ack;
+	struct pri_event_retrieve_rej retrieve_rej;
 } pri_event;
 
 struct pri;
@@ -950,6 +1002,76 @@
 int pri_notify(struct pri *pri, q931_call *c, int channel, int info);
 
 int pri_callrerouting_facility(struct pri *pri, q931_call *call, const char *dest, const char* original, const char* reason);
+
+/*!
+ * \brief Send the HOLD message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int pri_hold(struct pri *ctrl, q931_call *call);
+
+/*!
+ * \brief Send the HOLD ACKNOWLEDGE message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int pri_hold_ack(struct pri *ctrl, q931_call *call);
+
+/*!
+ * \brief Send the HOLD REJECT message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg
+ * \param cause Q.931 cause code for rejecting the hold request.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int pri_hold_rej(struct pri *ctrl, q931_call *call, int cause);
+
+/*!
+ * \brief Send the RETRIEVE message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg
+ * \param channel Encoded channel id to use.  If zero do not send channel id.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int pri_retrieve(struct pri *ctrl, q931_call *call, int channel);
+
+/*!
+ * \brief Send the RETRIEVE ACKNOWLEDGE message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg
+ * \param channel Encoded channel id to use.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int pri_retrieve_ack(struct pri *ctrl, q931_call *call, int channel);
+
+/*!
+ * \brief Send the RETRIEVE REJECT message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg
+ * \param cause Q.931 cause code for rejecting the retrieve request.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int pri_retrieve_rej(struct pri *ctrl, q931_call *call, int cause);
 
 /* Get/Set PRI Timers  */
 #define PRI_GETSET_TIMERS
@@ -993,6 +1115,9 @@
 	PRI_TIMER_TM20,	/*!< Maximum time awaiting XID response */
 	PRI_TIMER_NM20,	/*!< Number of XID retransmits */
 
+	PRI_TIMER_T_HOLD,	/*!< Maximum time to wait for HOLD request response. */
+	PRI_TIMER_T_RETRIEVE,	/*!< Maximum time to wait for RETRIEVE request response. */
+
 	/* Must be last in the enum list */
 	_PRI_MAX_TIMERS,
 	PRI_MAX_TIMERS = (_PRI_MAX_TIMERS < 32) ? 32 : _PRI_MAX_TIMERS

Modified: team/rmudgett/facility/pri.c
URL: http://svn.asterisk.org/svn-view/libpri/team/rmudgett/facility/pri.c?view=diff&rev=1067&r1=1066&r2=1067
==============================================================================
--- team/rmudgett/facility/pri.c (original)
+++ team/rmudgett/facility/pri.c Wed Sep  2 18:20:27 2009
@@ -86,6 +86,8 @@
 	{ "T320",           PRI_TIMER_T320,             PRI_ALL_SWITCHES },
 	{ "T321",           PRI_TIMER_T321,             PRI_ALL_SWITCHES },
 	{ "T322",           PRI_TIMER_T322,             PRI_ALL_SWITCHES },
+	{ "T-HOLD",         PRI_TIMER_T_HOLD,           PRI_ALL_SWITCHES },
+	{ "T-RETRIEVE",     PRI_TIMER_T_RETRIEVE,       PRI_ALL_SWITCHES },
 /* *INDENT-ON* */
 };
 
@@ -150,6 +152,9 @@
 	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_T_HOLD] = 4 * 1000;	/* Wait for HOLD request response. */
+	ctrl->timers[PRI_TIMER_T_RETRIEVE] = 4 * 1000;/* Wait for RETRIEVE request response. */
 
 	/* Set any switch specific override default values */
 	switch (switchtype) {
@@ -1274,6 +1279,54 @@
 	}
 }
 
+int pri_hold(struct pri *ctrl, q931_call *call)
+{
+	if (!ctrl || !call) {
+		return -1;
+	}
+	return q931_send_hold(ctrl, call);
+}
+
+int pri_hold_ack(struct pri *ctrl, q931_call *call)
+{
+	if (!ctrl || !call) {
+		return -1;
+	}
+	return q931_send_hold_ack(ctrl, call);
+}
+
+int pri_hold_rej(struct pri *ctrl, q931_call *call, int cause)
+{
+	if (!ctrl || !call) {
+		return -1;
+	}
+	return q931_send_hold_rej(ctrl, call, cause);
+}
+
+int pri_retrieve(struct pri *ctrl, q931_call *call, int channel)
+{
+	if (!ctrl || !call) {
+		return -1;
+	}
+	return q931_send_retrieve(ctrl, call, channel);
+}
+
+int pri_retrieve_ack(struct pri *ctrl, q931_call *call, int channel)
+{
+	if (!ctrl || !call) {
+		return -1;
+	}
+	return q931_send_retrieve_ack(ctrl, call, channel);
+}
+
+int pri_retrieve_rej(struct pri *ctrl, q931_call *call, int cause)
+{
+	if (!ctrl || !call) {
+		return -1;
+	}
+	return q931_send_retrieve_rej(ctrl, call, cause);
+}
+
 void pri_sr_set_reversecharge(struct pri_sr *sr, int requested)
 {
 	sr->reversecharge = requested;

Modified: team/rmudgett/facility/pri_internal.h
URL: http://svn.asterisk.org/svn-view/libpri/team/rmudgett/facility/pri_internal.h?view=diff&rev=1067&r1=1066&r2=1067
==============================================================================
--- team/rmudgett/facility/pri_internal.h (original)
+++ team/rmudgett/facility/pri_internal.h Wed Sep  2 18:20:27 2009
@@ -333,6 +333,22 @@
 	INCOMING_CT_STATE_POST_CONNECTED_LINE
 };
 
+/*! Call hold supplementary states. */
+enum Q931_HOLD_STATE {
+	/*! \brief No call hold activity. */
+	Q931_HOLD_STATE_IDLE,
+	/*! \brief Request made to hold call. */
+	Q931_HOLD_STATE_HOLD_REQ,
+	/*! \brief Request received to hold call. */
+	Q931_HOLD_STATE_HOLD_IND,
+	/*! \brief Call is held. */
+	Q931_HOLD_STATE_CALL_HELD,
+	/*! \brief Request made to retrieve call. */
+	Q931_HOLD_STATE_RETRIEVE_REQ,
+	/*! \brief Request received to retrieve call. */
+	Q931_HOLD_STATE_RETRIEVE_IND,
+};
+
 /* q931_call datastructure */
 struct q931_call {
 	struct pri *pri;	/* PRI */
@@ -448,6 +464,10 @@
 
 	/*! \brief Incoming call transfer state. */
 	enum INCOMING_CT_STATE incoming_ct_state;
+	/*! Call hold supplementary state. */
+	enum Q931_HOLD_STATE hold_state;
+	/*! Call hold event timer */
+	int hold_timer;
 
 	int useruserprotocoldisc;
 	char useruserinfo[256];
@@ -505,9 +525,9 @@
 void q931_party_id_fixup(const struct pri *ctrl, struct q931_party_id *id);
 int q931_party_id_presentation(const struct q931_party_id *id);
 
-const char *q931_call_state_str(int callstate);
-
-int q931_is_ptmp(struct pri *ctrl);
+const char *q931_call_state_str(enum Q931_CALL_STATE callstate);
+
+int q931_is_ptmp(const struct pri *ctrl);
 struct pri_subcommand *q931_alloc_subcommand(struct pri *ctrl);
 
 int q931_notify_redirection(struct pri *ctrl, q931_call *call, int notify, const struct q931_party_number *number);

Modified: team/rmudgett/facility/pri_q931.h
URL: http://svn.asterisk.org/svn-view/libpri/team/rmudgett/facility/pri_q931.h?view=diff&rev=1067&r1=1066&r2=1067
==============================================================================
--- team/rmudgett/facility/pri_q931.h (original)
+++ team/rmudgett/facility/pri_q931.h Wed Sep  2 18:20:27 2009
@@ -466,4 +466,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);
+
 #endif

Modified: team/rmudgett/facility/q931.c
URL: http://svn.asterisk.org/svn-view/libpri/team/rmudgett/facility/q931.c?view=diff&rev=1067&r1=1066&r2=1067
==============================================================================
--- team/rmudgett/facility/q931.c (original)
+++ team/rmudgett/facility/q931.c Wed Sep  2 18:20:27 2009
@@ -80,10 +80,10 @@
 	/* Call Management */
 	{ Q931_HOLD, "HOLD" },
 	{ Q931_HOLD_ACKNOWLEDGE, "HOLD ACKNOWLEDGE" },
-	{ Q931_HOLD_REJECT, "HOLD REJECT" },
+	{ Q931_HOLD_REJECT, "HOLD REJECT", { Q931_CAUSE } },
 	{ Q931_RETRIEVE, "RETRIEVE" },
 	{ Q931_RETRIEVE_ACKNOWLEDGE, "RETRIEVE ACKNOWLEDGE" },
-	{ Q931_RETRIEVE_REJECT, "RETRIEVE REJECT" },
+	{ Q931_RETRIEVE_REJECT, "RETRIEVE REJECT", { Q931_CAUSE } },
 	{ Q931_RESUME, "RESUME" },
 	{ Q931_RESUME_ACKNOWLEDGE, "RESUME ACKNOWLEDGE", { Q931_CHANNEL_IDENT } },
 	{ Q931_RESUME_REJECT, "RESUME REJECT", { Q931_CAUSE } },
@@ -228,15 +228,37 @@
 
 #if 1
 /* Update call state with transition trace. */
-#define UPDATE_OURCALLSTATE(ctrl,c,newstate) do {\
-	if (ctrl->debug & (PRI_DEBUG_Q931_STATE) && c->ourcallstate != newstate) \
-		pri_message(ctrl, DBGHEAD "call %d on channel %d enters state %d (%s)\n", DBGINFO, \
-			c->cr, c->channelno, newstate, q931_call_state_str(newstate)); \
-		c->ourcallstate = newstate; \
+#define UPDATE_OURCALLSTATE(ctrl, call, newstate) \
+	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)); \
+		} \
+		(call)->ourcallstate = (newstate); \
 	} while (0)
 #else
 /* Update call state with no trace. */
-#define UPDATE_OURCALLSTATE(ctrl,c,newstate) c->ourcallstate = newstate
+#define UPDATE_OURCALLSTATE(ctrl, call, newstate) (call)->ourcallstate = (newstate)
+#endif
+
+#if 1
+/* Update hold state with transition trace. */
+#define UPDATE_HOLD_STATE(ctrl, call, newstate) \
+	do { \
+		if (((ctrl)->debug & PRI_DEBUG_Q931_STATE) && (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), \
+				q931_hold_state_str(newstate)); \
+		} \
+		(call)->hold_state = (newstate); \
+	} while (0)
+#else
+/* Update hold state with no trace. */
+#define UPDATE_HOLD_STATE(ctrl, call, newstate) (call)->hold_state = (newstate)
 #endif
 
 struct ie {
@@ -264,8 +286,20 @@
  */
 static int q931_encode_channel(const q931_call *call)
 {
+	int held_call;
+
+	switch (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;
+		break;
+	default:
+		held_call = 0;
+		break;
+	}
 	return call->channelno | (call->ds1no << 8) | (call->ds1explicit << 16)
-		| (call->cis_call << 17);
+		| (call->cis_call << 17) | held_call;
 }
 
 /*!
@@ -276,7 +310,7 @@
  * \retval TRUE if in PTMP mode.
  * \retval FALSE otherwise.
  */
-int q931_is_ptmp(struct pri *ctrl)
+int q931_is_ptmp(const struct pri *ctrl)
 {
 	/* Check master control structure */
 	for (; ctrl->master; ctrl = ctrl->master) {
@@ -2117,7 +2151,7 @@
  *
  * \return String equivalent of the given Q.931 call state.
  */
-const char *q931_call_state_str(int callstate)
+const char *q931_call_state_str(enum Q931_CALL_STATE callstate)
 {
 	static struct msgtype callstates[] = {
 /* *INDENT-OFF* */
@@ -2143,7 +2177,30 @@
 		{ Q931_CALL_STATE_RESTART,                  "Restart" },
 /* *INDENT-ON* */
 	};
-	return code2str(callstate, callstates, sizeof(callstates) / sizeof(callstates[0]));
+	return code2str(callstate, callstates, ARRAY_LEN(callstates));
+}
+
+/*!
+ * \internal
+ * \brief Convert the Q.932 supplementary hold state to a string.
+ *
+ * \param state Q.932 supplementary hold state.
+ *
+ * \return String equivalent of the given hold state.
+ */
+static const char *q931_hold_state_str(enum Q931_HOLD_STATE state)
+{
+	static struct msgtype hold_states[] = {
+/* *INDENT-OFF* */
+		{ Q931_HOLD_STATE_IDLE,         "Idle" },
+		{ Q931_HOLD_STATE_HOLD_REQ,     "Hold Request" },
+		{ Q931_HOLD_STATE_HOLD_IND,     "Hold Indication" },
+		{ Q931_HOLD_STATE_CALL_HELD,    "Call Held" },
+		{ Q931_HOLD_STATE_RETRIEVE_REQ, "Retrieve Request" },
+		{ Q931_HOLD_STATE_RETRIEVE_IND, "Retrieve Indication" },
+/* *INDENT-ON* */
+	};
+	return code2str(state, hold_states, ARRAY_LEN(hold_states));
 }
 
 static void dump_call_state(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
@@ -3113,10 +3170,12 @@
 				*ctrl->callpool = cur->next;
 			if (ctrl->debug & PRI_DEBUG_Q931_STATE)
 				pri_message(ctrl,
-					"NEW_HANGUP DEBUG: Destroying the call, ourstate %s, peerstate %s\n",
+					"NEW_HANGUP DEBUG: Destroying the call, ourstate %s, peerstate %s, hold-state %s\n",
 					q931_call_state_str(cur->ourcallstate),
-					q931_call_state_str(cur->peercallstate));
+					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);
 			return;
@@ -3975,6 +4034,385 @@
 	return 0;
 }
 
+/*!
+ * \internal
+ * \brief Send HOLD message response wait timeout.
+ *
+ * \param data Q.931 call leg.
+ *
+ * \return Nothing
+ */
+static void q931_hold_timeout(void *data)
+{
+	struct q931_call *call = data;
+	struct pri *ctrl = call->pri;
+
+	if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
+		pri_message(ctrl, "Time-out waiting for HOLD response\n");
+	}
+
+	/* Ensure that the timer is deleted. */
+	pri_schedule_del(ctrl, call->hold_timer);
+	call->hold_timer = 0;
+
+	UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_IDLE);
+
+	ctrl->schedev = 1;
+	ctrl->ev.e = PRI_EVENT_HOLD_REJ;
+	ctrl->ev.hold_rej.channel = q931_encode_channel(call);
+	ctrl->ev.hold_rej.call = call;
+	ctrl->ev.hold_rej.cause = PRI_CAUSE_MESSAGE_TYPE_NONEXIST;
+}
+
+/*!
+ * \internal
+ * \brief Determine if a hold request is allowed now.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg
+ *
+ * \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)
+{
+	int allowed;
+
+	allowed = 0;
+	switch (call->ourcallstate) {
+	case Q931_CALL_STATE_CALL_RECEIVED:
+	case Q931_CALL_STATE_CONNECT_REQUEST:
+	case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING:
+		if (q931_is_ptmp(ctrl)) {
+			/* HOLD request only allowed in these states if point-to-point mode. */
+			break;
+		}
+		/* Fall through */
+	case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING:
+	case Q931_CALL_STATE_CALL_DELIVERED:
+	case Q931_CALL_STATE_ACTIVE:
+		switch (call->hold_state) {
+		case Q931_HOLD_STATE_IDLE:
+			allowed = 1;
+			break;
+		default:
+			break;
+		}
+		break;
+	case Q931_CALL_STATE_DISCONNECT_INDICATION:
+	case Q931_CALL_STATE_RELEASE_REQUEST:
+		/* Ignore HOLD request in these states. */
+		break;
+	default:
+		break;
+	}
+
+	return allowed;
+}
+
+static int hold_ies[] = {
+	-1
+};
+
+/*!
+ * \brief Send the HOLD message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg
+ *
+ * \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)) {
+		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)) {
+		pri_schedule_del(ctrl, call->hold_timer);
+		call->hold_timer = 0;
+		return -1;
+	}
+	UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_HOLD_REQ);
+	return 0;
+}
+
+static int hold_ack_ies[] = {
+	-1
+};
+
+/*!
+ * \brief Send the HOLD ACKNOWLEDGE message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int q931_send_hold_ack(struct pri *ctrl, q931_call *call)
+{
+	/* 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);
+
+	/* 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);
+}
+
+static int hold_reject_ies[] = {
+	Q931_CAUSE,
+	-1
+};
+
+/*!
+ * \brief Send the HOLD REJECT message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg
+ * \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);
+
+	call->cause = cause;
+	call->causecode = CODE_CCITT;
+	call->causeloc = LOC_PRIV_NET_LOCAL_USER;
+	return send_message(ctrl, call, Q931_HOLD_REJECT, hold_reject_ies);
+}
+
+/*!
+ * \internal
+ * \brief Send HOLD message response guard timeout.
+ *
+ * \param data Q.931 call leg.
+ *
+ * \return Nothing
+ */
+static void q931_hold_guard_timeout(void *data)
+{
+	struct q931_call *call = data;
+	struct pri *ctrl = call->pri;
+
+	if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
+		pri_message(ctrl,
+			"Time-out for upper layer HOLD response (assume not implemented)\n");
+	}
+
+	q931_send_hold_rej(ctrl, call, PRI_CAUSE_FACILITY_NOT_IMPLEMENTED);
+}
+
+/*!
+ * \internal
+ * \brief Send RETRIEVE message response wait timeout.
+ *
+ * \param data Q.931 call leg.
+ *
+ * \return Nothing
+ */
+static void q931_retrieve_timeout(void *data)
+{
+	struct q931_call *call = data;
+	struct pri *ctrl = call->pri;
+
+	if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
+		pri_message(ctrl, "Time-out waiting for RETRIEVE response\n");
+	}
+
+	/* Ensure that the timer is deleted. */
+	pri_schedule_del(ctrl, call->hold_timer);
+	call->hold_timer = 0;
+
+	UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_CALL_HELD);
+
+	/* Call is still on hold so forget the channel. */
+	call->channelno = 0;/* No channel */
+	call->ds1no = 0;
+	call->ds1explicit = 0;
+	call->chanflags = 0;
+
+	ctrl->schedev = 1;
+	ctrl->ev.e = PRI_EVENT_RETRIEVE_REJ;
+	ctrl->ev.retrieve_rej.channel = q931_encode_channel(call);
+	ctrl->ev.retrieve_rej.call = call;
+	ctrl->ev.retrieve_rej.cause = PRI_CAUSE_MESSAGE_TYPE_NONEXIST;
+}
+
+/*!
+ * \internal
+ * \brief Determine if a retrieve request is allowed now.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg
+ *
+ * \retval TRUE if we can send a RETRIEVE request.
+ * \retval FALSE if not allowed.
+ */
+static int q931_is_retrieve_allowed(const struct pri *ctrl, const q931_call *call)
+{
+	int allowed;
+
+	allowed = 0;
+	switch (call->ourcallstate) {
+	case Q931_CALL_STATE_CALL_RECEIVED:
+	case Q931_CALL_STATE_CONNECT_REQUEST:
+	case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING:
+		if (q931_is_ptmp(ctrl)) {
+			/* RETRIEVE request only allowed in these states if point-to-point mode. */
+			break;
+		}
+		/* Fall through */
+	case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING:
+	case Q931_CALL_STATE_CALL_DELIVERED:
+	case Q931_CALL_STATE_ACTIVE:
+		switch (call->hold_state) {
+		case Q931_HOLD_STATE_CALL_HELD:
+			allowed = 1;
+			break;
+		default:
+			break;
+		}
+		break;
+	case Q931_CALL_STATE_DISCONNECT_INDICATION:
+	case Q931_CALL_STATE_RELEASE_REQUEST:
+		/* Ignore RETRIEVE request in these states. */
+		break;
+	default:
+		break;
+	}
+
+	return allowed;
+}
+
+static int retrieve_ies[] = {
+	Q931_CHANNEL_IDENT,
+	-1
+};
+
+/*!
+ * \brief Send the RETRIEVE message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg
+ * \param channel Encoded channel id to use.  If zero do not send channel id.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int q931_send_retrieve(struct pri *ctrl, q931_call *call, int channel)
+{
+	if (!q931_is_retrieve_allowed(ctrl, call)) {
+		return -1;
+	}
+
+	if (channel) {
+		call->ds1no = (channel & 0xff00) >> 8;
+		call->ds1explicit = (channel & 0x10000) >> 16;
+		call->channelno = channel & 0xff;
+		if (ctrl->localtype == PRI_NETWORK) {
+			call->chanflags = FLAG_EXCLUSIVE;
+		} else {
+			call->chanflags = FLAG_PREFERRED;
+		}
+	} else {
+		/* Do not send Q931_CHANNEL_IDENT */
+		call->chanflags = 0;
+	}
+
+	pri_schedule_del(ctrl, call->hold_timer);
+	call->hold_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T_RETRIEVE],
+		q931_retrieve_timeout, call);
+	if (send_message(ctrl, call, Q931_RETRIEVE, retrieve_ies)) {
+		pri_schedule_del(ctrl, call->hold_timer);
+		call->hold_timer = 0;
+
+		/* Call is still on hold so forget the channel. */
+		call->channelno = 0;/* No channel */
+		call->ds1no = 0;
+		call->ds1explicit = 0;
+		call->chanflags = 0;
+		return -1;
+	}
+	UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_RETRIEVE_REQ);
+	return 0;
+}
+
+static int retrieve_ack_ies[] = {
+	Q931_CHANNEL_IDENT,
+	-1
+};
+
+/*!
+ * \brief Send the RETRIEVE ACKNOWLEDGE message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg
+ * \param channel Encoded channel id to use.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int q931_send_retrieve_ack(struct pri *ctrl, q931_call *call, int channel)
+{
+	call->ds1no = (channel & 0xff00) >> 8;
+	call->ds1explicit = (channel & 0x10000) >> 16;
+	call->channelno = channel & 0xff;
+	call->chanflags = FLAG_EXCLUSIVE;
+
+	UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_IDLE);
+
+	return send_message(ctrl, call, Q931_RETRIEVE_ACKNOWLEDGE, retrieve_ack_ies);
+}
+
+static int retrieve_reject_ies[] = {
+	Q931_CAUSE,
+	-1
+};
+
+/*!
+ * \brief Send the RETRIEVE REJECT message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg
+ * \param cause Q.931 cause code for rejecting the retrieve request.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int q931_send_retrieve_rej(struct pri *ctrl, q931_call *call, int cause)
+{
+	/* Call is still on hold so forget the channel. */
+	call->channelno = 0;/* No channel */
+	call->ds1no = 0;
+	call->ds1explicit = 0;
+	call->chanflags = 0;
+
+	UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_CALL_HELD);
+
+	call->cause = cause;
+	call->causecode = CODE_CCITT;
+	call->causeloc = LOC_PRIV_NET_LOCAL_USER;
+	return send_message(ctrl, call, Q931_RETRIEVE_REJECT, retrieve_reject_ies);
+}
+
 int q931_hangup(struct pri *ctrl, q931_call *c, int cause)
 {
 	int disconnect = 1;
@@ -3982,9 +4420,10 @@
 
 	if (ctrl->debug & PRI_DEBUG_Q931_STATE)
 		pri_message(ctrl,
-			"NEW_HANGUP DEBUG: Calling q931_hangup, ourstate %s, peerstate %s\n",
+			"NEW_HANGUP DEBUG: Calling q931_hangup, ourstate %s, peerstate %s, hold-state %s\n",
 			q931_call_state_str(c->ourcallstate),
-			q931_call_state_str(c->peercallstate));
+			q931_call_state_str(c->peercallstate),
+			q931_hold_state_str(c->hold_state));
 	if (!ctrl || !c)
 		return -1;
 	/* If mandatory IE was missing, insist upon that cause code */
@@ -4252,15 +4691,26 @@
 	case Q931_NOTIFY:
 		q931_party_number_init(&c->redirection_number);
 		break;
+	case Q931_HOLD:
+		break;
+	case Q931_HOLD_ACKNOWLEDGE:
+		break;
+	case Q931_HOLD_REJECT:
+		c->cause = -1;
+		break;
+	case Q931_RETRIEVE:
+		c->channelno = 0xFF;
+		c->ds1no = 0;
+		c->ds1explicit = 0;
+		break;
+	case Q931_RETRIEVE_ACKNOWLEDGE:
+		break;
+	case Q931_RETRIEVE_REJECT:
+		c->cause = -1;
+		break;
 	case Q931_USER_INFORMATION:
 	case Q931_SEGMENT:
 	case Q931_CONGESTION_CONTROL:
-	case Q931_HOLD:
-	case Q931_HOLD_ACKNOWLEDGE:
-	case Q931_HOLD_REJECT:
-	case Q931_RETRIEVE:
-	case Q931_RETRIEVE_ACKNOWLEDGE:
-	case Q931_RETRIEVE_REJECT:
 	case Q931_RESUME:
 	case Q931_RESUME_ACKNOWLEDGE:
 	case Q931_RESUME_REJECT:
@@ -5061,15 +5511,207 @@
 			break;
 		}
 		return res;
+	case Q931_HOLD:
+		switch (c->ourcallstate) {
+		case Q931_CALL_STATE_CALL_RECEIVED:
+		case Q931_CALL_STATE_CONNECT_REQUEST:
+		case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING:
+			if (q931_is_ptmp(ctrl)) {
+				/* HOLD request only allowed in these states if point-to-point mode. */
+				q931_send_hold_rej(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE);
+				break;
+			}
+			/* Fall through */
+		case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING:
+		case Q931_CALL_STATE_CALL_DELIVERED:
+		case Q931_CALL_STATE_ACTIVE:
+			switch (c->hold_state) {
+			case Q931_HOLD_STATE_HOLD_REQ:
+				if (ctrl->localtype == PRI_NETWORK) {
+					/* The network ignores HOLD request on a hold collision. */
+					break;
+				}
+				/* Fall through */
+			case Q931_HOLD_STATE_IDLE:
+				ctrl->ev.e = PRI_EVENT_HOLD;
+				ctrl->ev.hold.channel = q931_encode_channel(c);
+				ctrl->ev.hold.call = c;
+				res = Q931_RES_HAVEEVENT;
+
+				UPDATE_HOLD_STATE(ctrl, c, Q931_HOLD_STATE_HOLD_IND);
+
+				/* Start the upper layer does not implement guard timer. */
+				pri_schedule_del(ctrl, c->hold_timer);
+				c->hold_timer = pri_schedule_event(ctrl,
+					ctrl->timers[PRI_TIMER_T_HOLD] / 2, q931_hold_guard_timeout, c);
+				break;
+			default:
+				q931_send_hold_rej(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE);
+				break;
+			}
+			break;
+		case Q931_CALL_STATE_DISCONNECT_INDICATION:
+		case Q931_CALL_STATE_RELEASE_REQUEST:
+			/* Ignore HOLD request in these states. */
+			break;
+		default:
+			q931_send_hold_rej(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE);
+			break;
+		}
+		break;
+	case Q931_HOLD_ACKNOWLEDGE:
+		switch (c->hold_state) {
+		case Q931_HOLD_STATE_HOLD_REQ:
+			ctrl->ev.e = PRI_EVENT_HOLD_ACK;
+			ctrl->ev.hold_ack.channel = q931_encode_channel(c);
+			ctrl->ev.hold_ack.call = c;
+			res = Q931_RES_HAVEEVENT;
+
+			UPDATE_HOLD_STATE(ctrl, c, Q931_HOLD_STATE_CALL_HELD);
+
+			/* Call is now on hold so forget the channel. */
+			c->channelno = 0;/* No channel */
+			c->ds1no = 0;
+			c->ds1explicit = 0;
+			c->chanflags = 0;
+
+			/* Stop T-HOLD timer */
+			pri_schedule_del(ctrl, c->hold_timer);
+			c->hold_timer = 0;
+			break;
+		default:
+			/* Ignore response.  Response is late or spurrious. */
+			break;
+		}
+		break;
+	case Q931_HOLD_REJECT:
+		switch (c->hold_state) {
+		case Q931_HOLD_STATE_HOLD_REQ:
+			if (missingmand) {
+				/* Still, let hold rejection continue. */
+				c->cause = PRI_CAUSE_MANDATORY_IE_MISSING;
+			}
+			ctrl->ev.e = PRI_EVENT_HOLD_REJ;
+			ctrl->ev.hold_rej.channel = q931_encode_channel(c);
+			ctrl->ev.hold_rej.call = c;
+			ctrl->ev.hold_rej.cause = c->cause;
+			res = Q931_RES_HAVEEVENT;
+
+			UPDATE_HOLD_STATE(ctrl, c, Q931_HOLD_STATE_IDLE);
+
+			/* Stop T-HOLD timer */
+			pri_schedule_del(ctrl, c->hold_timer);
+			c->hold_timer = 0;
+			break;
+		default:
+			/* Ignore response.  Response is late or spurrious. */
+			break;
+		}
+		break;
+	case Q931_RETRIEVE:
+		switch (c->ourcallstate) {
+		case Q931_CALL_STATE_CALL_RECEIVED:
+		case Q931_CALL_STATE_CONNECT_REQUEST:
+		case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING:
+			if (q931_is_ptmp(ctrl)) {
+				/* RETRIEVE request only allowed in these states if point-to-point mode. */
+				q931_send_retrieve_rej(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE);
+				break;
+			}
+			/* Fall through */
+		case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING:
+		case Q931_CALL_STATE_CALL_DELIVERED:
+		case Q931_CALL_STATE_ACTIVE:
+			switch (c->hold_state) {
+			case Q931_HOLD_STATE_RETRIEVE_REQ:
+				if (ctrl->localtype == PRI_NETWORK) {
+					/* The network ignores RETRIEVE request on a retrieve collision. */
+					break;
+				}
+				/* Fall through */
+			case Q931_HOLD_STATE_CALL_HELD:
+				ctrl->ev.e = PRI_EVENT_RETRIEVE;
+				ctrl->ev.retrieve.channel = q931_encode_channel(c);
+				ctrl->ev.retrieve.call = c;
+				ctrl->ev.retrieve.flexible = !(c->chanflags & FLAG_EXCLUSIVE);
+				res = Q931_RES_HAVEEVENT;
+
+				UPDATE_HOLD_STATE(ctrl, c, Q931_HOLD_STATE_RETRIEVE_IND);
+
+				/*
+				 * Stop any T-RETRIEVE timer.
+				 * The upper layer must implement HOLD for a call to even get
+				 * on hold.
+				 */
+				pri_schedule_del(ctrl, c->hold_timer);
+				c->hold_timer = 0;
+				break;
+			default:
+				q931_send_retrieve_rej(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE);
+				break;
+			}
+			break;
+		case Q931_CALL_STATE_DISCONNECT_INDICATION:
+		case Q931_CALL_STATE_RELEASE_REQUEST:
+			/* Ignore RETRIEVE request in these states. */
+			break;
+		default:
+			q931_send_retrieve_rej(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE);
+			break;
+		}
+		break;
+	case Q931_RETRIEVE_ACKNOWLEDGE:
+		switch (c->hold_state) {
+		case Q931_HOLD_STATE_RETRIEVE_REQ:
+			UPDATE_HOLD_STATE(ctrl, c, Q931_HOLD_STATE_IDLE);
+
+			/* Stop T-RETRIEVE timer */
+			pri_schedule_del(ctrl, c->hold_timer);
+			c->hold_timer = 0;
+
+			ctrl->ev.e = PRI_EVENT_RETRIEVE_ACK;
+			ctrl->ev.retrieve_ack.channel = q931_encode_channel(c);
+			ctrl->ev.retrieve_ack.call = c;
+			res = Q931_RES_HAVEEVENT;
+			break;
+		default:
+			/* Ignore response.  Response is late or spurrious. */
+			break;
+		}
+		break;
+	case Q931_RETRIEVE_REJECT:
+		switch (c->hold_state) {
+		case Q931_HOLD_STATE_RETRIEVE_REQ:
+			UPDATE_HOLD_STATE(ctrl, c, Q931_HOLD_STATE_CALL_HELD);
+
+			/* Call is still on hold so forget the channel. */
+			c->channelno = 0;/* No channel */
+			c->ds1no = 0;
+			c->ds1explicit = 0;
+			c->chanflags = 0;
+
+			/* Stop T-RETRIEVE timer */
+			pri_schedule_del(ctrl, c->hold_timer);
+			c->hold_timer = 0;
+
+			if (missingmand) {
+				/* Still, let retrive rejection continue. */
+				c->cause = PRI_CAUSE_MANDATORY_IE_MISSING;
+			}
+			ctrl->ev.e = PRI_EVENT_RETRIEVE_REJ;
+			ctrl->ev.retrieve_rej.channel = q931_encode_channel(c);
+			ctrl->ev.retrieve_rej.call = c;
+			ctrl->ev.retrieve_rej.cause = c->cause;
+			res = Q931_RES_HAVEEVENT;
+			break;
+		default:
+			/* Ignore response.  Response is late or spurrious. */
+			break;
+		}
+		break;
 	case Q931_USER_INFORMATION:
 	case Q931_SEGMENT:
 	case Q931_CONGESTION_CONTROL:
-	case Q931_HOLD:
-	case Q931_HOLD_ACKNOWLEDGE:
-	case Q931_HOLD_REJECT:
-	case Q931_RETRIEVE:
-	case Q931_RETRIEVE_ACKNOWLEDGE:
-	case Q931_RETRIEVE_REJECT:
 	case Q931_RESUME:
 	case Q931_RESUME_ACKNOWLEDGE:
 	case Q931_RESUME_REJECT:




More information about the libpri-commits mailing list