[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