[libpri-commits] rmudgett: branch group/ccss r1392 - in /team/group/ccss: ./ doc/

SVN commits to the libpri project libpri-commits at lists.digium.com
Thu Dec 17 15:53:35 CST 2009


Author: rmudgett
Date: Thu Dec 17 15:53:33 2009
New Revision: 1392

URL: http://svnview.digium.com/svn/libpri?view=rev&rev=1392
Log:
PTP monitor FSM written.  Needs testing.

*  Added CC PTP monitor FSM pseudo code document files in the doc
directory.
*  Changed pri_cc_req() API to not promice a PRI_SUBCMD_CC_REQ_RSP event
if the CC request is cancelled by pri_cc_cancel().  The cc_id is no longer
valid after pri_cc_cancel() anyway.
*  Made a call's cc_record and cc_record unlink from each other better.
*  Made sure that the FSM indirect action timer is canceled when the FSM
self destructs.

Added:
    team/group/ccss/doc/cc_ptp_monitor.fsm   (with props)
    team/group/ccss/doc/cc_ptp_monitor_flattened.fsm   (with props)
Modified:
    team/group/ccss/doc/cc_ptmp_monitor.fsm
    team/group/ccss/doc/cc_ptmp_monitor_flattened.fsm
    team/group/ccss/pri_cc.c
    team/group/ccss/pri_facility.c
    team/group/ccss/pri_facility.h
    team/group/ccss/pri_internal.h
    team/group/ccss/q931.c

Modified: team/group/ccss/doc/cc_ptmp_monitor.fsm
URL: http://svnview.digium.com/svn/libpri/team/group/ccss/doc/cc_ptmp_monitor.fsm?view=diff&rev=1392&r1=1391&r2=1392
==============================================================================
--- team/group/ccss/doc/cc_ptmp_monitor.fsm (original)
+++ team/group/ccss/doc/cc_ptmp_monitor.fsm Thu Dec 17 15:53:33 2009
@@ -95,8 +95,6 @@
 		/* We were in the middle of a cc-request when we were asked to cancel. */
 		Epilog {
 			Action Stop_T_ACTIVATE;
-			/* Claim it was a timeout */
-			Action Pass_Up_CC_Req_Rsp_Timeout;
 		}
 		Stimulus CC_EVENT_CC_REQUEST_ACCEPT {
 			/*

Modified: team/group/ccss/doc/cc_ptmp_monitor_flattened.fsm
URL: http://svnview.digium.com/svn/libpri/team/group/ccss/doc/cc_ptmp_monitor_flattened.fsm?view=diff&rev=1392&r1=1391&r2=1392
==============================================================================
--- team/group/ccss/doc/cc_ptmp_monitor_flattened.fsm (original)
+++ team/group/ccss/doc/cc_ptmp_monitor_flattened.fsm Thu Dec 17 15:53:33 2009
@@ -111,30 +111,22 @@
 			 */
 			Action Send_CC_Deactivate_Req;
 			Action Stop_T_ACTIVATE;
-			/* Claim it was a timeout */
-			Action Pass_Up_CC_Req_Rsp_Timeout;
 			Action Set_Selfdestruct;
 			Next_State CC_STATE_IDLE;
 		}
 		Stimulus CC_EVENT_CC_REQUEST_FAIL {
 			Action Stop_T_ACTIVATE;
-			/* Claim it was a timeout */
-			Action Pass_Up_CC_Req_Rsp_Timeout;
 			Action Set_Selfdestruct;
 			Next_State CC_STATE_IDLE;
 		}
 		Stimulus CC_EVENT_TIMEOUT_T_ACTIVATE {
 			Action Stop_T_ACTIVATE;
-			/* Claim it was a timeout */
-			Action Pass_Up_CC_Req_Rsp_Timeout;
 			Action Set_Selfdestruct;
 			Next_State CC_STATE_IDLE;
 		}
 		Stimulus CC_EVENT_LINK_CANCEL {
 			/* Received CCBSErase */
 			Action Stop_T_ACTIVATE;
-			/* Claim it was a timeout */
-			Action Pass_Up_CC_Req_Rsp_Timeout;
 			Action Set_Selfdestruct;
 			Next_State CC_STATE_IDLE;
 		}

Added: team/group/ccss/doc/cc_ptp_monitor.fsm
URL: http://svnview.digium.com/svn/libpri/team/group/ccss/doc/cc_ptp_monitor.fsm?view=auto&rev=1392
==============================================================================
--- team/group/ccss/doc/cc_ptp_monitor.fsm (added)
+++ team/group/ccss/doc/cc_ptp_monitor.fsm Thu Dec 17 15:53:33 2009
@@ -1,0 +1,170 @@
+/*
+ * FSM pseudo code used in the design/implementation of the CC PTP monitor.
+ */
+FSM CC_PTP_Monitor
+{
+	State CC_STATE_IDLE {
+		Init {
+		}
+		Prolog {
+			Action Set_Selfdestruct;
+		}
+		Stimulus CC_EVENT_AVAILABLE {
+			/* Received CCBS-T-Aailable */
+			Action Pass_Up_CC_Available;
+			Next_State CC_STATE_AVAILABLE;
+		}
+		Stimulus CC_EVENT_CANCEL {
+			Action Set_Selfdestruct;
+		}
+	}
+	State CC_STATE_AVAILABLE {
+		/*
+		 * The upper layer is responsible for canceling the CC available
+		 * offering.
+		 */
+		Stimulus CC_EVENT_CC_REQUEST {
+			/*
+			 * Before event is posted:
+			 *   cc_record->is_ccnr is set.
+			 *   The signaling connection call record is created.
+			 */
+			Action Queue_CC_Request;
+			/*
+			 * For PTP mode the T_ACTIVATE timer is not defined.  However,
+			 * we will use it to protect our resources from leaks caused
+			 * by the network cable being disconnected.
+			 * This timer should be set longer than normal so the
+			 * CC records will normally be cleaned up by network activity.
+			 */
+			Action Start_T_ACTIVATE;
+			Next_State CC_STATE_REQUESTED;
+		}
+		Stimulus CC_EVENT_CANCEL {
+			Next_State CC_STATE_IDLE;
+		}
+	}
+	State CC_STATE_REQUESTED {
+		Stimulus CC_EVENT_CC_REQUEST_ACCEPT {
+			/*
+			 * Received CCBS-T-Request/CCNR-T-Request response
+			 * Before event is posted:
+			 *   Negotiated CC retention setting saved
+			 */
+			Action Pass_Up_CC_Req_Rsp_Success;
+			Action Stop_T_ACTIVATE;
+			Next_State CC_STATE_ACTIVATED;
+		}
+		Stimulus CC_EVENT_CC_REQUEST_FAIL {
+			Action Pass_Up_CC_Req_Rsp_Fail(error/reject, code);
+			Action Pass_Up_CC_Cancel;
+			Action Stop_T_ACTIVATE;
+			/*
+			 * If this request fail comes in with the RELEASE_COMPLETE
+			 * message then the post action will never get a chance to
+			 * run.  It will be aborted because the CC_EVENT_SIGNALING_GONE
+			 * will be processed first.
+			 */
+			Action Post_HANGUP_SIGNALING;
+			Next_State CC_STATE_WAIT_DESTRUCTION;
+		}
+		Stimulus CC_EVENT_TIMEOUT_T_ACTIVATE {
+			Action Pass_Up_CC_Req_Rsp_Timeout;
+			Action Pass_Up_CC_Cancel;
+			Action Stop_T_ACTIVATE;
+			Action Post_HANGUP_SIGNALING;
+			Next_State CC_STATE_WAIT_DESTRUCTION;
+		}
+		Stimulus CC_EVENT_SIGNALING_GONE {
+			/* Claim it was a timeout */
+			Action Pass_Up_CC_Req_Rsp_Timeout;
+			Action Pass_Up_CC_Cancel;
+			Action Stop_T_ACTIVATE;
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_CANCEL {
+			Action Stop_T_ACTIVATE;
+			Action Hangup_Signaling_Link;
+			Next_State CC_STATE_IDLE;
+		}
+	}
+	State CC_STATE_WAIT_DESTRUCTION {
+		/*
+		 * Delayed disconnect of the signaling link to allow subcmd events
+		 * from the signaling link to be passed up.
+		 */
+		Stimulus CC_EVENT_SIGNALING_GONE {
+			/* Signaling link cleared. */
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_HANGUP_SIGNALING {
+			Action Hangup_Signaling_Link;
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_CANCEL {
+			Action Hangup_Signaling_Link;
+			Next_State CC_STATE_IDLE;
+		}
+	}
+	State CC_STATE_ACTIVATED {
+		Prolog {
+			Action Reset_A_Status;
+		}
+		Stimulus CC_EVENT_REMOTE_USER_FREE {
+			/* Received CCBS_T_RemoteUserFree */
+			Action Pass_Up_Remote_User_Free;
+			Test = Get_A_Status;
+			Test == Busy {
+				Next_State CC_STATE_SUSPENDED;
+			}
+			Next_State CC_STATE_WAIT_CALLBACK;
+		}
+		Stimulus CC_EVENT_SUSPEND {
+			Action Set_A_Status_Busy;
+		}
+		Stimulus CC_EVENT_RESUME {
+			Action Reset_A_Status;
+		}
+	}
+	State CC_STATE_WAIT_CALLBACK {
+		Stimulus CC_EVENT_SUSPEND {
+			Next_State CC_STATE_SUSPENDED;
+		}
+	}
+	State CC_STATE_SUSPENDED {
+		Prolog {
+			Action Send_CC_Suspend;
+		}
+		Stimulus CC_EVENT_RESUME {
+			Action Send_CC_Resume;
+			Next_State CC_STATE_ACTIVATED;
+		}
+	}
+	Superstate CC_ACTIVE(CC_STATE_ACTIVATED, CC_STATE_WAIT_CALLBACK, CC_STATE_SUSPENDED) {
+		Prolog {
+			/* Start T_CCBS6/T_CCNR6 depending upon CC mode. */
+			Action Start_T_SUPERVISION;
+		}
+		Epilog {
+			Action Stop_T_SUPERVISION;
+		}
+		Stimulus CC_EVENT_RECALL {
+			/* The original call parameters have already been set. */
+			Action Queue_SETUP_Recall;
+		}
+		Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION {
+			Action Pass_Up_CC_Cancel;
+			Action Post_HANGUP_SIGNALING;
+			Next_State CC_STATE_WAIT_DESTRUCTION;
+		}
+		Stimulus CC_EVENT_SIGNALING_GONE {
+			/* Signaling link cleared. */
+			Action Pass_Up_CC_Cancel;
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_CANCEL {
+			Action Hangup_Signaling_Link;
+			Next_State CC_STATE_IDLE;
+		}
+	}
+}

Propchange: team/group/ccss/doc/cc_ptp_monitor.fsm
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/group/ccss/doc/cc_ptp_monitor.fsm
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/group/ccss/doc/cc_ptp_monitor.fsm
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/group/ccss/doc/cc_ptp_monitor_flattened.fsm
URL: http://svnview.digium.com/svn/libpri/team/group/ccss/doc/cc_ptp_monitor_flattened.fsm?view=auto&rev=1392
==============================================================================
--- team/group/ccss/doc/cc_ptp_monitor_flattened.fsm (added)
+++ team/group/ccss/doc/cc_ptp_monitor_flattened.fsm Thu Dec 17 15:53:33 2009
@@ -1,0 +1,213 @@
+/*
+ * FSM pseudo code used in the design/implementation of the CC PTP monitor.
+ */
+FSM CC_PTP_Monitor
+{
+	State CC_STATE_IDLE {
+		Stimulus CC_EVENT_AVAILABLE {
+			/* Received CCBS-T-Aailable */
+			Action Pass_Up_CC_Available;
+			Next_State CC_STATE_AVAILABLE;
+		}
+		Stimulus CC_EVENT_CANCEL {
+			Action Set_Selfdestruct;
+		}
+	}
+	State CC_STATE_AVAILABLE {
+		/*
+		 * The upper layer is responsible for canceling the CC available
+		 * offering.
+		 */
+		Stimulus CC_EVENT_CC_REQUEST {
+			/*
+			 * Before event is posted:
+			 *   cc_record->is_ccnr is set.
+			 *   The signaling connection call record is created.
+			 */
+			Action Queue_CC_Request;
+			/*
+			 * For PTP mode the T_ACTIVATE timer is not defined.  However,
+			 * we will use it to protect our resources from leaks caused
+			 * by the network cable being disconnected.
+			 * This timer should be set longer than normal so the
+			 * CC records will normally be cleaned up by network activity.
+			 */
+			Action Start_T_ACTIVATE;
+			Next_State CC_STATE_REQUESTED;
+		}
+		Stimulus CC_EVENT_CANCEL {
+			Action Set_Selfdestruct;
+			Next_State CC_STATE_IDLE;
+		}
+	}
+	State CC_STATE_REQUESTED {
+		Stimulus CC_EVENT_CC_REQUEST_ACCEPT {
+			/*
+			 * Received CCBS-T-Request/CCNR-T-Request response
+			 * Before event is posted:
+			 *   Negotiated CC retention setting saved
+			 */
+			Action Pass_Up_CC_Req_Rsp_Success;
+			Action Stop_T_ACTIVATE;
+			/* Start T_CCBS6/T_CCNR6 depending upon CC mode. */
+			Action Start_T_SUPERVISION;
+			Action Reset_A_Status;
+			Next_State CC_STATE_ACTIVATED;
+		}
+		Stimulus CC_EVENT_CC_REQUEST_FAIL {
+			Action Pass_Up_CC_Req_Rsp_Fail(error/reject, code);
+			Action Pass_Up_CC_Cancel;
+			Action Stop_T_ACTIVATE;
+			/*
+			 * If this request fail comes in with the RELEASE_COMPLETE
+			 * message then the post action will never get a chance to
+			 * run.  It will be aborted because the CC_EVENT_SIGNALING_GONE
+			 * will be processed first.
+			 */
+			Action Post_HANGUP_SIGNALING;
+			Next_State CC_STATE_WAIT_DESTRUCTION;
+		}
+		Stimulus CC_EVENT_TIMEOUT_T_ACTIVATE {
+			Action Pass_Up_CC_Req_Rsp_Timeout;
+			Action Pass_Up_CC_Cancel;
+			Action Stop_T_ACTIVATE;
+			Action Post_HANGUP_SIGNALING;
+			Next_State CC_STATE_WAIT_DESTRUCTION;
+		}
+		Stimulus CC_EVENT_SIGNALING_GONE {
+			/* Claim it was a timeout */
+			Action Pass_Up_CC_Req_Rsp_Timeout;
+			Action Pass_Up_CC_Cancel;
+			Action Stop_T_ACTIVATE;
+			Action Set_Selfdestruct;
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_CANCEL {
+			Action Stop_T_ACTIVATE;
+			Action Hangup_Signaling_Link;
+			Action Set_Selfdestruct;
+			Next_State CC_STATE_IDLE;
+		}
+	}
+	State CC_STATE_WAIT_DESTRUCTION {
+		/*
+		 * Delayed disconnect of the signaling link to allow subcmd events
+		 * from the signaling link to be passed up.
+		 */
+		Stimulus CC_EVENT_SIGNALING_GONE {
+			/* Signaling link cleared. */
+			Action Set_Selfdestruct;
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_HANGUP_SIGNALING {
+			Action Hangup_Signaling_Link;
+			Action Set_Selfdestruct;
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_CANCEL {
+			Action Hangup_Signaling_Link;
+			Action Set_Selfdestruct;
+			Next_State CC_STATE_IDLE;
+		}
+	}
+	State CC_STATE_ACTIVATED {
+		Stimulus CC_EVENT_REMOTE_USER_FREE {
+			/* Received CCBS_T_RemoteUserFree */
+			Action Pass_Up_Remote_User_Free;
+			Test = Get_A_Status;
+			Test == Busy {
+				Action Send_CC_Suspend;
+				Next_State CC_STATE_SUSPENDED;
+			}
+			Next_State CC_STATE_WAIT_CALLBACK;
+		}
+		Stimulus CC_EVENT_SUSPEND {
+			Action Set_A_Status_Busy;
+		}
+		Stimulus CC_EVENT_RESUME {
+			Action Reset_A_Status;
+		}
+		Stimulus CC_EVENT_RECALL {
+			/* The original call parameters have already been set. */
+			Action Queue_SETUP_Recall;
+		}
+		Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION {
+			Action Pass_Up_CC_Cancel;
+			Action Post_HANGUP_SIGNALING;
+			Action Stop_T_SUPERVISION;
+			Next_State CC_STATE_WAIT_DESTRUCTION;
+		}
+		Stimulus CC_EVENT_SIGNALING_GONE {
+			/* Signaling link cleared. */
+			Action Pass_Up_CC_Cancel;
+			Action Stop_T_SUPERVISION;
+			Action Set_Selfdestruct;
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_CANCEL {
+			Action Hangup_Signaling_Link;
+			Action Stop_T_SUPERVISION;
+			Action Set_Selfdestruct;
+			Next_State CC_STATE_IDLE;
+		}
+	}
+	State CC_STATE_WAIT_CALLBACK {
+		Stimulus CC_EVENT_SUSPEND {
+			Action Send_CC_Suspend;
+			Next_State CC_STATE_SUSPENDED;
+		}
+		Stimulus CC_EVENT_RECALL {
+			/* The original call parameters have already been set. */
+			Action Queue_SETUP_Recall;
+		}
+		Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION {
+			Action Pass_Up_CC_Cancel;
+			Action Post_HANGUP_SIGNALING;
+			Action Stop_T_SUPERVISION;
+			Next_State CC_STATE_WAIT_DESTRUCTION;
+		}
+		Stimulus CC_EVENT_SIGNALING_GONE {
+			/* Signaling link cleared. */
+			Action Pass_Up_CC_Cancel;
+			Action Stop_T_SUPERVISION;
+			Action Set_Selfdestruct;
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_CANCEL {
+			Action Hangup_Signaling_Link;
+			Action Stop_T_SUPERVISION;
+			Action Set_Selfdestruct;
+			Next_State CC_STATE_IDLE;
+		}
+	}
+	State CC_STATE_SUSPENDED {
+		Stimulus CC_EVENT_RESUME {
+			Action Send_CC_Resume;
+			Action Reset_A_Status;
+			Next_State CC_STATE_ACTIVATED;
+		}
+		Stimulus CC_EVENT_RECALL {
+			/* The original call parameters have already been set. */
+			Action Queue_SETUP_Recall;
+		}
+		Stimulus CC_EVENT_TIMEOUT_T_SUPERVISION {
+			Action Pass_Up_CC_Cancel;
+			Action Post_HANGUP_SIGNALING;
+			Action Stop_T_SUPERVISION;
+			Next_State CC_STATE_WAIT_DESTRUCTION;
+		}
+		Stimulus CC_EVENT_SIGNALING_GONE {
+			/* Signaling link cleared. */
+			Action Pass_Up_CC_Cancel;
+			Action Stop_T_SUPERVISION;
+			Action Set_Selfdestruct;
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_CANCEL {
+			Action Hangup_Signaling_Link;
+			Action Stop_T_SUPERVISION;
+			Action Set_Selfdestruct;
+			Next_State CC_STATE_IDLE;
+		}
+	}
+}

Propchange: team/group/ccss/doc/cc_ptp_monitor_flattened.fsm
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/group/ccss/doc/cc_ptp_monitor_flattened.fsm
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/group/ccss/doc/cc_ptp_monitor_flattened.fsm
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: team/group/ccss/pri_cc.c
URL: http://svnview.digium.com/svn/libpri/team/group/ccss/pri_cc.c?view=diff&rev=1392&r1=1391&r2=1392
==============================================================================
--- team/group/ccss/pri_cc.c (original)
+++ team/group/ccss/pri_cc.c Thu Dec 17 15:53:33 2009
@@ -253,6 +253,16 @@
 	struct pri_cc_record **prev;
 	struct pri_cc_record *current;
 
+	/* Unlink CC signaling link associations. */
+	if (doomed->original_call) {
+		doomed->original_call->cc.record = NULL;
+		doomed->original_call = NULL;
+	}
+	if (doomed->signaling) {
+		doomed->signaling->cc.record = NULL;
+		doomed->signaling = NULL;
+	}
+
 	ctrl = PRI_MASTER(ctrl);
 	for (prev = &ctrl->cc.pool, current = ctrl->cc.pool; current;
 		prev = &current->next, current = current->next) {
@@ -291,6 +301,7 @@
 	}
 
 	/* Initialize the new record */
+	cc_record->master = ctrl;
 	cc_record->record_id = record_id;
 	cc_record->call_linkage_id = CC_PTMP_INVALID_ID;/* So it will never be found this way */
 	cc_record->ccbs_reference_id = CC_PTMP_INVALID_ID;/* So it will never be found this way */
@@ -729,6 +740,66 @@
 
 /*!
  * \internal
+ * \brief Encode ETSI PTP CCBS_T_Request/CCNR_T_Request message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param pos Starting position to encode the facility ie contents.
+ * \param end End of facility ie contents encoding data buffer.
+ * \param cc_record Call completion record to process event.
+ *
+ * \retval Start of the next ASN.1 component to encode on success.
+ * \retval NULL on error.
+ */
+static unsigned char *enc_etsi_ptp_cc_request(struct pri *ctrl,
+	unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record)
+{
+	struct rose_msg_invoke msg;
+
+	pos = facility_encode_header(ctrl, pos, end, NULL);
+	if (!pos) {
+		return NULL;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	msg.invoke_id = get_invokeid(ctrl);
+	msg.operation = cc_record->is_ccnr
+		? ROSE_ETSI_CCNR_T_Request : ROSE_ETSI_CCBS_T_Request;
+
+	if (cc_record->saved_ie_contents.length
+		<= sizeof(msg.args.etsi.CCBS_T_Request.q931ie_contents)) {
+		/* Saved BC, HLC, and LLC from initial SETUP */
+		msg.args.etsi.CCBS_T_Request.q931ie.length = cc_record->saved_ie_contents.length;
+		memcpy(msg.args.etsi.CCBS_T_Request.q931ie.contents,
+			cc_record->saved_ie_contents.data, cc_record->saved_ie_contents.length);
+	} else {
+		pri_error(ctrl, "CCBS_T_Request q931 ie contents did not fit.\n");
+	}
+
+	q931_copy_address_to_rose(ctrl, &msg.args.etsi.CCBS_T_Request.destination,
+		&cc_record->party_b);
+
+	if (cc_record->party_a.number.valid && cc_record->party_a.number.str[0]) {
+		q931_copy_id_address_to_rose(ctrl, &msg.args.etsi.CCBS_T_Request.originating,
+			&cc_record->party_a);
+
+		msg.args.etsi.CCBS_T_Request.presentation_allowed_indicator_present = 1;
+		if ((cc_record->party_a.number.presentation & PRI_PRES_RESTRICTION)
+			== PRI_PRES_ALLOWED) {
+			msg.args.etsi.CCBS_T_Request.presentation_allowed_indicator = 1;
+		}
+	}
+
+	if (cc_record->master->cc.option.retain_service) {
+		msg.args.etsi.CCBS_T_Request.retention_supported = 1;
+	}
+
+	pos = rose_encode_invoke(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
  * \brief Encode ETSI PTMP CCBSDeactivate message.
  *
  * \param ctrl D channel controller for diagnostic messages or global options.
@@ -984,7 +1055,7 @@
 		}
 		break;
 	case PRI_SWITCH_QSIG:
-		/* \todo BUGBUG rose_remote_user_free_encode(Q.SIG) not written. */
+		/*! \todo BUGBUG rose_remote_user_free_encode(Q.SIG) not written. */
 		return -1;
 	default:
 		return -1;
@@ -1021,6 +1092,148 @@
 	}
 
 	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Encode and queue a CC suspend message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode CC suspend message.
+ * \param msgtype Q.931 message type to put facility ie in.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int rose_cc_suspend_encode(struct pri *ctrl, q931_call *call, int msgtype)
+{
+	unsigned char buffer[256];
+	unsigned char *end;
+
+	switch (ctrl->switchtype) {
+	case PRI_SWITCH_EUROISDN_E1:
+	case PRI_SWITCH_EUROISDN_T1:
+		end =
+			enc_etsi_ptp_cc_operation(ctrl, buffer, buffer + sizeof(buffer),
+				ROSE_ETSI_CCBS_T_Suspend);
+		break;
+	case PRI_SWITCH_QSIG:
+		/*! \todo BUGBUG rose_cc_suspend_encode(Q.SIG) not written. */
+		return -1;
+	default:
+		return -1;
+	}
+	if (!end) {
+		return -1;
+	}
+
+	return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, NULL);
+}
+
+/*!
+ * \internal
+ * \brief Encode and send a CC suspend message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param cc_record Call completion record to process event.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int send_cc_suspend(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	q931_call *call;
+
+	switch (ctrl->switchtype) {
+	case PRI_SWITCH_EUROISDN_E1:
+	case PRI_SWITCH_EUROISDN_T1:
+		call = cc_record->signaling;
+		if (!call) {
+			return -1;
+		}
+		if (rose_cc_suspend_encode(ctrl, call, Q931_FACILITY)
+			|| q931_facility(ctrl, call)) {
+			pri_message(ctrl, "Could not schedule message for CC suspend.\n");
+			return -1;
+		}
+		return 0;
+	case PRI_SWITCH_QSIG:
+		/*! \todo BUGBUG send_cc_suspend(Q.SIG) not written */
+		return -1;
+	default:
+		return -1;
+	}
+}
+
+/*!
+ * \internal
+ * \brief Encode and queue a CC resume message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode CC resume message.
+ * \param msgtype Q.931 message type to put facility ie in.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int rose_cc_resume_encode(struct pri *ctrl, q931_call *call, int msgtype)
+{
+	unsigned char buffer[256];
+	unsigned char *end;
+
+	switch (ctrl->switchtype) {
+	case PRI_SWITCH_EUROISDN_E1:
+	case PRI_SWITCH_EUROISDN_T1:
+		end =
+			enc_etsi_ptp_cc_operation(ctrl, buffer, buffer + sizeof(buffer),
+				ROSE_ETSI_CCBS_T_Resume);
+		break;
+	case PRI_SWITCH_QSIG:
+		/*! \todo BUGBUG rose_cc_resume_encode(Q.SIG) not written. */
+		return -1;
+	default:
+		return -1;
+	}
+	if (!end) {
+		return -1;
+	}
+
+	return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, NULL);
+}
+
+/*!
+ * \internal
+ * \brief Encode and send a CC resume message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param cc_record Call completion record to process event.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int send_cc_resume(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	q931_call *call;
+
+	switch (ctrl->switchtype) {
+	case PRI_SWITCH_EUROISDN_E1:
+	case PRI_SWITCH_EUROISDN_T1:
+		call = cc_record->signaling;
+		if (!call) {
+			return -1;
+		}
+		if (rose_cc_resume_encode(ctrl, call, Q931_FACILITY)
+			|| q931_facility(ctrl, call)) {
+			pri_message(ctrl, "Could not schedule message for CC resume.\n");
+			return -1;
+		}
+		return 0;
+	case PRI_SWITCH_QSIG:
+		/*! \todo BUGBUG send_cc_resume(Q.SIG) not written */
+		return -1;
+	default:
+		return -1;
+	}
 }
 
 /*!
@@ -1433,7 +1646,7 @@
 }
 
 /*!
- * \brief Respond to the received CCBSRequest/CCNRRequest invoke message.
+ * \brief Respond to the received PTMP CCBSRequest/CCNRRequest invoke message.
  *
  * \param ctrl D channel controller for diagnostic messages or global options.
  * \param call Call leg from which the message came.
@@ -1441,7 +1654,7 @@
  *
  * \return Nothing
  */
-void pri_cc_request(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke)
+void pri_cc_ptmp_request(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke)
 {
 	struct pri_cc_record *cc_record;
 
@@ -1589,6 +1802,9 @@
 	case CC_EVENT_SIGNALING_GONE:
 		str = "CC_EVENT_SIGNALING_GONE";
 		break;
+	case CC_EVENT_HANGUP_SIGNALING:
+		str = "CC_EVENT_HANGUP_SIGNALING";
+		break;
 	case CC_EVENT_MSG_ALERTING:
 		str = "CC_EVENT_MSG_ALERTING";
 		break;
@@ -1649,6 +1865,11 @@
 static void pri_cc_act_set_self_destruct(struct pri *ctrl, struct pri_cc_record *cc_record)
 {
 	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+
+	/* Abort any pending indirect events. */
+	pri_schedule_del(ctrl, cc_record->t_indirect);
+	cc_record->t_indirect = 0;
+
 	cc_record->fsm_complete = 1;
 }
 
@@ -1698,7 +1919,7 @@
 	struct pri_cc_record *cc_record = data;
 
 	cc_record->t_retention = 0;
-	q931_cc_timeout(cc_record->signaling->pri, cc_record->signaling, cc_record,
+	q931_cc_timeout(cc_record->master, cc_record->signaling, cc_record,
 		CC_EVENT_TIMEOUT_T_RETENTION);
 }
 
@@ -1751,7 +1972,7 @@
 	struct pri_cc_record *cc_record = data;
 
 	cc_record->fsm.ptmp.extended_t_ccbs1 = 0;
-	q931_cc_timeout(cc_record->signaling->pri, cc_record->signaling, cc_record,
+	q931_cc_timeout(cc_record->master, cc_record->signaling, cc_record,
 		CC_EVENT_TIMEOUT_EXTENDED_T_CCBS1);
 }
 
@@ -1806,7 +2027,7 @@
 	struct pri_cc_record *cc_record = data;
 
 	cc_record->t_supervision = 0;
-	q931_cc_timeout(cc_record->signaling->pri, cc_record->signaling, cc_record,
+	q931_cc_timeout(cc_record->master, cc_record->signaling, cc_record,
 		CC_EVENT_TIMEOUT_T_SUPERVISION);
 }
 
@@ -1887,7 +2108,7 @@
 	struct pri_cc_record *cc_record = data;
 
 	cc_record->fsm.ptmp.t_ccbs3 = 0;
-	q931_cc_timeout(cc_record->signaling->pri, cc_record->signaling, cc_record,
+	q931_cc_timeout(cc_record->master, cc_record->signaling, cc_record,
 		CC_EVENT_TIMEOUT_T_CCBS3);
 }
 
@@ -2137,6 +2358,57 @@
 		cc_record->ccbs_reference_id =
 			msg->result->args.etsi.CCBSRequest.ccbs_reference & 0x7F;
 		cc_record->option.recall_mode = msg->result->args.etsi.CCBSRequest.recall_mode;
+
+		pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_ACCEPT);
+		break;
+	case APDU_CALLBACK_REASON_MSG_ERROR:
+		cc_record->msg.cc_req_rsp.reason = reason;
+		cc_record->msg.cc_req_rsp.code = msg->error->code;
+
+		pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_FAIL);
+		break;
+	case APDU_CALLBACK_REASON_MSG_REJECT:
+		cc_record->msg.cc_req_rsp.reason = reason;
+		cc_record->msg.cc_req_rsp.code = msg->reject->code;
+
+		pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_FAIL);
+		break;
+	default:
+		break;
+	}
+
+	/*
+	 * No more reponses are really expected.
+	 * However, the FSM will be removing the apdu_event itself instead.
+	 */
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief cc-request PTP response callback function.
+ *
+ * \param reason Reason callback is called.
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg.
+ * \param apdu APDU queued entry.  Do not change!
+ * \param msg APDU response message data.  (NULL if was not the reason called.)
+ *
+ * \return TRUE if no more responses are expected.
+ */
+static int pri_cc_req_response_ptp(enum APDU_CALLBACK_REASON reason, struct pri *ctrl, struct q931_call *call, struct apdu_event *apdu, const union apdu_msg_data *msg)
+{
+	struct pri_cc_record *cc_record;
+
+	cc_record = apdu->response.user.ptr;
+
+	switch (reason) {
+	case APDU_CALLBACK_REASON_TIMEOUT:
+		pri_cc_event(ctrl, call, cc_record, CC_EVENT_TIMEOUT_T_ACTIVATE);
+		break;
+	case APDU_CALLBACK_REASON_MSG_RESULT:
+		cc_record->option.retain_service =
+			msg->result->args.etsi.CCBS_T_Request.retention_supported;
 
 		pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_ACCEPT);
 		break;
@@ -2193,15 +2465,15 @@
 			msgtype = Q931_FACILITY;
 			response.callback = pri_cc_req_response_ptmp;
 		} else {
-			/* \todo BUGBUG rose_cc_request(PTP) not written. */
-			//msgtype = Q931_REGISTER;
-			//response.callback = pri_cc_req_response_ptp;
-			return -1;
+			end =
+				enc_etsi_ptp_cc_request(ctrl, buffer, buffer + sizeof(buffer), cc_record);
+			msgtype = Q931_REGISTER;
+			response.callback = pri_cc_req_response_ptp;
 		}
 		response.timeout_time = ctrl->timers[PRI_TIMER_T_ACTIVATE];
 		break;
 	case PRI_SWITCH_QSIG:
-		/* \todo BUGBUG rose_cc_request(Q.SIG) not written. */
+		/*! \todo BUGBUG rose_cc_request(Q.SIG) not written. */
 		//msgtype = Q931_SETUP;
 		//response.callback = pri_cc_req_response_qsig;
 		//response.timeout_time = ctrl->timers[PRI_TIMER_QSIG_CC_T1];
@@ -2280,6 +2552,36 @@
 {
 	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
 	send_remote_user_free(ctrl, cc_record->signaling, cc_record);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to send the CC suspend message.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_send_cc_suspend(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	send_cc_suspend(ctrl, cc_record);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to send the CC resume message.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_send_cc_resume(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	send_cc_resume(ctrl, cc_record);
 }
 
 /*!
@@ -2402,7 +2704,8 @@
 {
 	struct pri_cc_record *cc_record = data;
 
-	q931_cc_indirect(cc_record->signaling->pri, cc_record->signaling, cc_record,
+	cc_record->t_indirect = 0;
+	q931_cc_indirect(cc_record->master, cc_record->signaling, cc_record,
 		pri_cc_fill_status_rsp_a);
 }
 
@@ -2424,7 +2727,12 @@
 	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
 	if (cc_record->fsm.ptmp.party_a_status_acc != CC_PARTY_A_AVAILABILITY_INVALID) {
 		/* Accumulated party A status is not invalid so pass it up. */
-		pri_schedule_event(ctrl, 0, pri_cc_indirect_status_rsp_a, cc_record);
+		if (cc_record->t_indirect) {
+			pri_error(ctrl, "!! An indirect action is already active!");
+			pri_schedule_del(ctrl, cc_record->t_indirect);
+		}
+		cc_record->t_indirect = pri_schedule_event(ctrl, 0, pri_cc_indirect_status_rsp_a,
+			cc_record);
 	}
 }
 
@@ -2471,6 +2779,21 @@
 {
 	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
 	cc_record->party_a_status = CC_PARTY_A_AVAILABILITY_INVALID;
+}
+
+/*!
+ * \internal
+ * \brief FSM action to set A status as busy.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_set_a_status_busy(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	cc_record->party_a_status = CC_PARTY_A_AVAILABILITY_BUSY;
 }
 
 /*!
@@ -2803,6 +3126,58 @@
 {
 	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
 	call->cc.hangup_call = 1;
+}
+
+/*!
+ * \internal
+ * \brief Post the CC_EVENT_HANGUP_SIGNALING event (timeout action).
+ *
+ * \param data CC record pointer.
+ *
+ * \return Nothing
+ */
+static void pri_cc_post_hangup_signaling(void *data)
+{
+	struct pri_cc_record *cc_record = data;
+
+	cc_record->t_indirect = 0;
+	q931_cc_timeout(cc_record->master, cc_record->signaling, cc_record,
+		CC_EVENT_HANGUP_SIGNALING);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to post the CC_EVENT_HANGUP_SIGNALING event indirectly.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_post_hangup_signaling(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	if (cc_record->t_indirect) {
+		pri_error(ctrl, "!! An indirect action is already active!");
+		pri_schedule_del(ctrl, cc_record->t_indirect);
+	}
+	cc_record->t_indirect = pri_schedule_event(ctrl, 0, pri_cc_post_hangup_signaling,
+		cc_record);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to immediately hangup the signaling link.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_hangup_signaling_link(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	pri_hangup(ctrl, cc_record->signaling, -1);
 }
 
 /*!
@@ -3563,30 +3938,22 @@
 		 */
 		pri_cc_act_send_cc_deactivate_req(ctrl, cc_record);
 		pri_cc_act_stop_t_activate(ctrl, cc_record);
-		/* Claim it was a timeout */
-		pri_cc_act_pass_up_cc_req_rsp_timeout(ctrl, cc_record);
 		pri_cc_act_set_self_destruct(ctrl, cc_record);
 		cc_record->state = CC_STATE_IDLE;
 		break;
 	case CC_EVENT_CC_REQUEST_FAIL:
 		pri_cc_act_stop_t_activate(ctrl, cc_record);
-		/* Claim it was a timeout */
-		pri_cc_act_pass_up_cc_req_rsp_timeout(ctrl, cc_record);
 		pri_cc_act_set_self_destruct(ctrl, cc_record);
 		cc_record->state = CC_STATE_IDLE;
 		break;
 	case CC_EVENT_TIMEOUT_T_ACTIVATE:
 		pri_cc_act_stop_t_activate(ctrl, cc_record);
-		/* Claim it was a timeout */
-		pri_cc_act_pass_up_cc_req_rsp_timeout(ctrl, cc_record);
 		pri_cc_act_set_self_destruct(ctrl, cc_record);
 		cc_record->state = CC_STATE_IDLE;
 		break;
 	case CC_EVENT_LINK_CANCEL:
 		/* Received CCBSErase */
 		pri_cc_act_stop_t_activate(ctrl, cc_record);
-		/* Claim it was a timeout */
-		pri_cc_act_pass_up_cc_req_rsp_timeout(ctrl, cc_record);
 		pri_cc_act_set_self_destruct(ctrl, cc_record);
 		cc_record->state = CC_STATE_IDLE;
 		break;
@@ -3640,6 +4007,329 @@
 		break;
 	case CC_EVENT_CANCEL:
 		pri_cc_act_send_cc_deactivate_req(ctrl, cc_record);
+		pri_cc_act_stop_t_supervision(ctrl, cc_record);
+		pri_cc_act_set_self_destruct(ctrl, cc_record);
+		cc_record->state = CC_STATE_IDLE;
+		break;
+	default:
+		break;
+	}
+}
+
+/*!
+ * \internal
+ * \brief CC FSM PTP monitor CC_STATE_IDLE.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg.
+ * \param cc_record Call completion record to process event.
+ * \param event Event to process.
+ *
+ * \return Nothing
+ */
+static void pri_cc_fsm_ptp_monitor_idle(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
+{
+	switch (event) {
+	case CC_EVENT_AVAILABLE:
+		/* Received CCBS-T-Aailable */
+		pri_cc_act_pass_up_cc_available(ctrl, cc_record);
+		cc_record->state = CC_STATE_AVAILABLE;
+		break;
+	case CC_EVENT_CANCEL:
+		pri_cc_act_set_self_destruct(ctrl, cc_record);
+		break;
+	default:
+		break;
+	}
+}
+
+/*!
+ * \internal
+ * \brief CC FSM PTP monitor CC_STATE_AVAILABLE.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg.
+ * \param cc_record Call completion record to process event.
+ * \param event Event to process.
+ *
+ * \return Nothing
+ */
+static void pri_cc_fsm_ptp_monitor_avail(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
+{
+	/* The upper layer is responsible for canceling the CC available offering. */
+	switch (event) {
+	case CC_EVENT_CC_REQUEST:
+		/*
+		 * Before event is posted:
+		 *   cc_record->is_ccnr is set.
+		 *   The signaling connection call record is created.
+		 */
+		pri_cc_act_queue_cc_request(ctrl, call, cc_record);
+		/*
+		 * For PTP mode the T_ACTIVATE timer is not defined.  However,
+		 * we will use it to protect our resources from leaks caused
+		 * by the network cable being disconnected.
+		 * This timer should be set longer than normal so the
+		 * CC records will normally be cleaned up by network activity.
+		 */
+		//pri_cc_act_start_t_activate(ctrl, cc_record);
+		cc_record->state = CC_STATE_REQUESTED;
+		break;
+	case CC_EVENT_CANCEL:
+		pri_cc_act_set_self_destruct(ctrl, cc_record);
+		cc_record->state = CC_STATE_IDLE;
+		break;
+	default:
+		break;
+	}
+}
+
+/*!
+ * \internal
+ * \brief CC FSM PTP monitor CC_STATE_REQUESTED.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg.
+ * \param cc_record Call completion record to process event.
+ * \param event Event to process.
+ *
+ * \return Nothing
+ */
+static void pri_cc_fsm_ptp_monitor_req(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
+{
+	switch (event) {
+	case CC_EVENT_CC_REQUEST_ACCEPT:
+		/*
+		 * Received CCBS-T-Request/CCNR-T-Request response
+		 * Before event is posted:
+		 *   Negotiated CC retention setting saved
+		 */
+		pri_cc_act_pass_up_cc_req_rsp_success(ctrl, cc_record);
+		pri_cc_act_stop_t_activate(ctrl, cc_record);
+		/* Start T_CCBS6/T_CCNR6 depending upon CC mode. */
+		pri_cc_act_start_t_supervision(ctrl, cc_record);
+		pri_cc_act_reset_a_status(ctrl, cc_record);
+		cc_record->state = CC_STATE_ACTIVATED;
+		break;
+	case CC_EVENT_CC_REQUEST_FAIL:
+		pri_cc_act_pass_up_cc_req_rsp_fail(ctrl, cc_record);
+		pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
+		pri_cc_act_stop_t_activate(ctrl, cc_record);
+		/*
+		 * If this request fail comes in with the RELEASE_COMPLETE
+		 * message then the post action will never get a chance to
+		 * run.  It will be aborted because the CC_EVENT_SIGNALING_GONE
+		 * will be processed first.
+		 */
+		pri_cc_act_post_hangup_signaling(ctrl, cc_record);
+		cc_record->state = CC_STATE_WAIT_DESTRUCTION;
+		break;
+	case CC_EVENT_TIMEOUT_T_ACTIVATE:
+		pri_cc_act_pass_up_cc_req_rsp_timeout(ctrl, cc_record);
+		pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
+		pri_cc_act_stop_t_activate(ctrl, cc_record);
+		pri_cc_act_post_hangup_signaling(ctrl, cc_record);
+		cc_record->state = CC_STATE_WAIT_DESTRUCTION;
+		break;
+	case CC_EVENT_SIGNALING_GONE:
+		/* Claim it was a timeout */
+		pri_cc_act_pass_up_cc_req_rsp_timeout(ctrl, cc_record);
+		pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
+		pri_cc_act_stop_t_activate(ctrl, cc_record);
+		pri_cc_act_set_self_destruct(ctrl, cc_record);
+		cc_record->state = CC_STATE_IDLE;
+		break;
+	case CC_EVENT_CANCEL:
+		pri_cc_act_stop_t_activate(ctrl, cc_record);
+		pri_cc_act_hangup_signaling_link(ctrl, cc_record);
+		pri_cc_act_set_self_destruct(ctrl, cc_record);
+		cc_record->state = CC_STATE_IDLE;
+		break;
+	default:
+		break;
+	}
+}
+
+/*!
+ * \internal
+ * \brief CC FSM PTP monitor CC_STATE_WAIT_DESTRUCTION.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg.
+ * \param cc_record Call completion record to process event.
+ * \param event Event to process.
+ *
+ * \return Nothing
+ */
+static void pri_cc_fsm_ptp_monitor_wait_destruction(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
+{
+	/*
+	 * Delayed disconnect of the signaling link to allow subcmd events
+	 * from the signaling link to be passed up.
+	 */
+	switch (event) {
+	case CC_EVENT_SIGNALING_GONE:
+		/* Signaling link cleared. */
+		pri_cc_act_set_self_destruct(ctrl, cc_record);
+		cc_record->state = CC_STATE_IDLE;
+		break;
+	case CC_EVENT_HANGUP_SIGNALING:
+		pri_cc_act_hangup_signaling_link(ctrl, cc_record);
+		pri_cc_act_set_self_destruct(ctrl, cc_record);
+		cc_record->state = CC_STATE_IDLE;
+		break;
+	case CC_EVENT_CANCEL:
+		pri_cc_act_hangup_signaling_link(ctrl, cc_record);
+		pri_cc_act_set_self_destruct(ctrl, cc_record);
+		cc_record->state = CC_STATE_IDLE;
+		break;
+	default:
+		break;
+	}
+}
+
+/*!
+ * \internal
+ * \brief CC FSM PTP monitor CC_STATE_ACTIVATED.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg.
+ * \param cc_record Call completion record to process event.
+ * \param event Event to process.
+ *
+ * \return Nothing
+ */
+static void pri_cc_fsm_ptp_monitor_activated(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
+{
+	switch (event) {
+	case CC_EVENT_REMOTE_USER_FREE:
+		/* Received CCBS_T_RemoteUserFree */
+		pri_cc_act_pass_up_remote_user_free(ctrl, cc_record);
+		if (cc_record->party_a_status == CC_PARTY_A_AVAILABILITY_BUSY) {
+			pri_cc_act_send_cc_suspend(ctrl, cc_record);
+			cc_record->state = CC_STATE_SUSPENDED;
+		} else {
+			cc_record->state = CC_STATE_WAIT_CALLBACK;
+		}
+		break;
+	case CC_EVENT_SUSPEND:
+		pri_cc_act_set_a_status_busy(ctrl, cc_record);
+		break;
+	case CC_EVENT_RESUME:
+		pri_cc_act_reset_a_status(ctrl, cc_record);
+		break;
+	case CC_EVENT_RECALL:
+		/* The original call parameters have already been set. */
+		pri_cc_act_queue_setup_recall(ctrl, call, cc_record);
+		break;
+	case CC_EVENT_TIMEOUT_T_SUPERVISION:
+		pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
+		pri_cc_act_post_hangup_signaling(ctrl, cc_record);
+		pri_cc_act_stop_t_supervision(ctrl, cc_record);
+		cc_record->state = CC_STATE_WAIT_DESTRUCTION;
+		break;
+	case CC_EVENT_SIGNALING_GONE:
+		/* Signaling link cleared. */
+		pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
+		pri_cc_act_stop_t_supervision(ctrl, cc_record);
+		pri_cc_act_set_self_destruct(ctrl, cc_record);
+		cc_record->state = CC_STATE_IDLE;
+		break;
+	case CC_EVENT_CANCEL:
+		pri_cc_act_hangup_signaling_link(ctrl, cc_record);
+		pri_cc_act_stop_t_supervision(ctrl, cc_record);
+		pri_cc_act_set_self_destruct(ctrl, cc_record);
+		cc_record->state = CC_STATE_IDLE;
+		break;
+	default:
+		break;
+	}
+}
+

[... 484 lines stripped ...]



More information about the libpri-commits mailing list