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

SVN commits to the libpri project libpri-commits at lists.digium.com
Tue Dec 15 17:17:58 CST 2009


Author: rmudgett
Date: Tue Dec 15 17:17:55 2009
New Revision: 1388

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

*  Added some sanity checks to CC API functions.
*  Added CC PTMP monitor FSM pseudo code document files in the doc
directory.

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

Added: team/group/ccss/doc/cc_ptmp_monitor.fsm
URL: http://svnview.digium.com/svn/libpri/team/group/ccss/doc/cc_ptmp_monitor.fsm?view=auto&rev=1388
==============================================================================
--- team/group/ccss/doc/cc_ptmp_monitor.fsm (added)
+++ team/group/ccss/doc/cc_ptmp_monitor.fsm Tue Dec 15 17:17:55 2009
@@ -1,0 +1,177 @@
+/*
+ * FSM pseudo code used in the design/implementation of the CC PTMP monitor.
+ *
+ * The CCBSStatusRequest messages are handled independently from this FSM.
+ *
+ * The CCBSInterrogate/CCNRInterrogate messages are initiated by a dialplan
+ * application/AMI/CLI (future) and are handled outside of this FSM.
+ */
+FSM CC_PTMP_Monitor
+{
+	State CC_STATE_IDLE {
+		Init {
+		}
+		Prolog {
+			Action Set_Selfdestruct;
+		}
+		Stimulus CC_EVENT_AVAILABLE {
+			/*
+			 * Before event is posted:
+			 *   Received CallInfoRetain
+			 *   Created cc_record
+			 *   Saved CallLinkageID
+			 */
+			Action Pass_Up_CC_Available;
+			Next_State CC_STATE_AVAILABLE;
+		}
+		Stimulus CC_EVENT_CANCEL {
+			Action Set_Selfdestruct;
+		}
+	}
+	State CC_STATE_AVAILABLE {
+		/*
+		 * For PTMP TE mode the T_RETENTION timer is not defined.  However,
+		 * we will use it anyway in this state to protect our resources from
+		 * leaks caused by the network cable being disconnected.  This
+		 * timer should be set much longer than the network so normally
+		 * the CC records will be cleaned up by network activity.
+		 */
+		Epilog {
+			Action Stop_T_RETENTION;
+		}
+		Stimulus CC_EVENT_MSG_RELEASE {
+			Action Stop_T_RETENTION;
+			Action Start_T_RETENTION;
+		}
+		Stimulus CC_EVENT_MSG_RELEASE_COMPLETE {
+			Action Stop_T_RETENTION;
+			Action Start_T_RETENTION;
+		}
+		Stimulus CC_EVENT_CC_REQUEST {
+			/* cc_record->is_ccnr is set before event posted. */
+			Action Queue_CC_Request;
+			Action Start_T_ACTIVATE;
+			Next_State CC_STATE_REQUESTED;
+		}
+		Stimulus CC_EVENT_TIMEOUT_T_RETENTION {
+			/*
+			 * Received EraseCallLinkageID
+			 * or T_RETENTION really timed out.
+			 */
+			Action Pass_Up_CC_Cancel;
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_CANCEL {
+			Next_State CC_STATE_IDLE;
+		}
+	}
+	State CC_STATE_REQUESTED {
+		Stimulus CC_EVENT_CC_REQUEST_ACCEPT {
+			/*
+			 * Before event is posted:
+			 *   Received CCBSRequest/CCNRRequest response
+			 *   Saved CCBSReference
+			 */
+			Action Relese_LinkID;
+			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;
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_TIMEOUT_T_ACTIVATE {
+			Action Pass_Up_CC_Req_Rsp_Timeout;
+			Action Pass_Up_CC_Cancel;
+			Action Stop_T_ACTIVATE;
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_LINK_CANCEL {
+			/* Received CCBSErase */
+			/* 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 {
+			Next_State CC_STATE_WAIT_DESTRUCTION;
+		}
+	}
+	State CC_STATE_WAIT_DESTRUCTION {
+		/* 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 {
+			/*
+			 * Before event is posted:
+			 *   Received CCBSRequest/CCNRRequest response
+			 *   Saved CCBSReference
+			 */
+			Action Send_CC_Deactivate_Req;
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_CC_REQUEST_FAIL {
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_TIMEOUT_T_ACTIVATE {
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_LINK_CANCEL {
+			/* Received CCBSErase */
+			Next_State CC_STATE_IDLE;
+		}
+	}
+	State CC_STATE_ACTIVATED {
+		Prolog {
+			/*
+			 * Start T_CCBS2 or T_CCNR2 depending upon CC mode.
+			 * For PTMP TE mode these timers are not defined.  However,
+			 * we will use them anyway to protect our resources from leaks
+			 * caused by the network cable being disconnected.  These
+			 * timers should be set much longer than the network
+			 * so normally the CC records will be cleaned up by network
+			 * activity.
+			 */
+			Action Start_T_CCBS2;
+		}
+		Epilog {
+			Action Stop_T_CCBS2;
+		}
+		Stimulus CC_EVENT_B_FREE {
+			/* Received CCBSBFree */
+			Action Pass_Up_B_Free;
+		}
+		Stimulus CC_EVENT_REMOTE_USER_FREE {
+			/* Received CCBSRemoteUserFree */
+			Action Pass_Up_Remote_User_Free;
+		}
+		Stimulus CC_EVENT_STOP_ALERTING {
+			Action Pass_Up_Stop_Alerting;
+		}
+		Stimulus CC_EVENT_RECALL {
+			/* The original call parameters have already been set. */
+			Action Queue_SETUP_Recall;
+		}
+		Stimulus CC_EVENT_TIMEOUT_T_CCBS2 {
+			Action Send_CC_Deactivate_Req;
+			Action Pass_Up_CC_Cancel;
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_LINK_CANCEL {
+			/* Received CCBSErase */
+			Action Pass_Up_CC_Cancel;
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_CANCEL {
+			Action Send_CC_Deactivate_Req;
+			Next_State CC_STATE_IDLE;
+		}
+	}
+}

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

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

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

Added: 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=auto&rev=1388
==============================================================================
--- team/group/ccss/doc/cc_ptmp_monitor_flattened.fsm (added)
+++ team/group/ccss/doc/cc_ptmp_monitor_flattened.fsm Tue Dec 15 17:17:55 2009
@@ -1,0 +1,189 @@
+/*
+ * FSM pseudo code used in the design/implementation of the CC PTMP monitor.
+ *
+ * The CCBSStatusRequest messages are handled independently from this FSM.
+ *
+ * The CCBSInterrogate/CCNRInterrogate messages are initiated by a dialplan
+ * application/AMI/CLI (future) and are handled outside of this FSM.
+ */
+FSM CC_PTMP_Monitor
+{
+	State CC_STATE_IDLE {
+		Stimulus CC_EVENT_AVAILABLE {
+			/*
+			 * Before event is posted:
+			 *   Received CallInfoRetain
+			 *   Created cc_record
+			 *   Saved CallLinkageID
+			 */
+			Action Pass_Up_CC_Available;
+			Next_State CC_STATE_AVAILABLE;
+		}
+		Stimulus CC_EVENT_CANCEL {
+			Action Set_Selfdestruct;
+		}
+	}
+	State CC_STATE_AVAILABLE {
+		/*
+		 * For PTMP TE mode the T_RETENTION timer is not defined.  However,
+		 * we will use it anyway in this state to protect our resources from
+		 * leaks caused by the network cable being disconnected.  This
+		 * timer should be set much longer than the network so normally
+		 * the CC records will be cleaned up by network activity.
+		 */
+		Stimulus CC_EVENT_MSG_RELEASE {
+			Action Stop_T_RETENTION;
+			Action Start_T_RETENTION;
+		}
+		Stimulus CC_EVENT_MSG_RELEASE_COMPLETE {
+			Action Stop_T_RETENTION;
+			Action Start_T_RETENTION;
+		}
+		Stimulus CC_EVENT_CC_REQUEST {
+			/* cc_record->is_ccnr is set before event posted. */
+			Action Queue_CC_Request;
+			Action Start_T_ACTIVATE;
+			Action Stop_T_RETENTION;
+			Next_State CC_STATE_REQUESTED;
+		}
+		Stimulus CC_EVENT_TIMEOUT_T_RETENTION {
+			/*
+			 * Received EraseCallLinkageID
+			 * or T_RETENTION really timed out.
+			 */
+			Action Pass_Up_CC_Cancel;
+			Action Stop_T_RETENTION;
+			Action Set_Selfdestruct;
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_CANCEL {
+			Action Stop_T_RETENTION;
+			Action Set_Selfdestruct;
+			Next_State CC_STATE_IDLE;
+		}
+	}
+	State CC_STATE_REQUESTED {
+		Stimulus CC_EVENT_CC_REQUEST_ACCEPT {
+			/*
+			 * Before event is posted:
+			 *   Received CCBSRequest/CCNRRequest response
+			 *   Saved CCBSReference
+			 */
+			Action Relese_LinkID;
+			Action Pass_Up_CC_Req_Rsp_Success;
+			Action Stop_T_ACTIVATE;
+			/*
+			 * Start T_CCBS2 or T_CCNR2 depending upon CC mode.
+			 * For PTMP TE mode these timers are not defined.  However,
+			 * we will use them anyway to protect our resources from leaks
+			 * caused by the network cable being disconnected.  These
+			 * timers should be set much longer than the network
+			 * so normally the CC records will be cleaned up by network
+			 * activity.
+			 */
+			Action Start_T_CCBS2;
+			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;
+			Action Set_Selfdestruct;
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_TIMEOUT_T_ACTIVATE {
+			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_LINK_CANCEL {
+			/* Received CCBSErase */
+			/* 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 {
+			Next_State CC_STATE_WAIT_DESTRUCTION;
+		}
+	}
+	State CC_STATE_WAIT_DESTRUCTION {
+		/* We were in the middle of a cc-request when we were asked to cancel. */
+		Stimulus CC_EVENT_CC_REQUEST_ACCEPT {
+			/*
+			 * Before event is posted:
+			 *   Received CCBSRequest/CCNRRequest response
+			 *   Saved CCBSReference
+			 */
+			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;
+		}
+	}
+	State CC_STATE_ACTIVATED {
+		Stimulus CC_EVENT_B_FREE {
+			/* Received CCBSBFree */
+			Action Pass_Up_B_Free;
+		}
+		Stimulus CC_EVENT_REMOTE_USER_FREE {
+			/* Received CCBSRemoteUserFree */
+			Action Pass_Up_Remote_User_Free;
+		}
+		Stimulus CC_EVENT_STOP_ALERTING {
+			Action Pass_Up_Stop_Alerting;
+		}
+		Stimulus CC_EVENT_RECALL {
+			/* The original call parameters have already been set. */
+			Action Queue_SETUP_Recall;
+		}
+		Stimulus CC_EVENT_TIMEOUT_T_CCBS2 {
+			Action Send_CC_Deactivate_Req;
+			Action Pass_Up_CC_Cancel;
+			Action Stop_T_CCBS2;
+			Action Set_Selfdestruct;
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_LINK_CANCEL {
+			/* Received CCBSErase */
+			Action Pass_Up_CC_Cancel;
+			Action Stop_T_CCBS2;
+			Action Set_Selfdestruct;
+			Next_State CC_STATE_IDLE;
+		}
+		Stimulus CC_EVENT_CANCEL {
+			Action Send_CC_Deactivate_Req;
+			Action Stop_T_CCBS2;
+			Action Set_Selfdestruct;
+			Next_State CC_STATE_IDLE;
+		}
+	}
+}

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

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

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

Modified: team/group/ccss/libpri.h
URL: http://svnview.digium.com/svn/libpri/team/group/ccss/libpri.h?view=diff&rev=1388&r1=1387&r2=1388
==============================================================================
--- team/group/ccss/libpri.h (original)
+++ team/group/ccss/libpri.h Tue Dec 15 17:17:55 2009
@@ -599,6 +599,7 @@
 	int fail_code;
 	/*!
 	 * \brief TRUE if negotiated to retain CC service if B busy again.
+	 * \note Valid when status is success.
 	 */
 	int retain_service;
 };
@@ -1449,7 +1450,7 @@
 void pri_cc_remote_user_free(struct pri *ctrl, long cc_id);
 void pri_cc_b_free(struct pri *ctrl, long cc_id);
 void pri_cc_stop_alerting(struct pri *ctrl, long cc_id);
-int pri_cc_status_req(struct pri *ctrl, long cc_id);
+void pri_cc_status_req(struct pri *ctrl, long cc_id);
 void pri_cc_status_req_rsp(struct pri *ctrl, long cc_id, int status);
 void pri_cc_status(struct pri *ctrl, long cc_id, int status);
 int pri_cc_call(struct pri *ctrl, long cc_id, q931_call *call, struct pri_sr *req);

Modified: team/group/ccss/pri_cc.c
URL: http://svnview.digium.com/svn/libpri/team/group/ccss/pri_cc.c?view=diff&rev=1388&r1=1387&r2=1388
==============================================================================
--- team/group/ccss/pri_cc.c (original)
+++ team/group/ccss/pri_cc.c Tue Dec 15 17:17:55 2009
@@ -299,6 +299,8 @@
 	cc_record->party_b_is_remote = call->cc.party_b_is_remote;
 	cc_record->saved_ie_contents = call->cc.saved_ie_contents;
 	cc_record->bc = call->bc;
+	cc_record->option.recall_mode = ctrl->cc.option.recall_mode;
+	cc_record->option.retain_service = ctrl->cc.option.retain_service;
 /*! \todo BUGBUG need more initialization?? */
 
 	/*
@@ -694,6 +696,120 @@
 
 /*!
  * \internal
+ * \brief Encode ETSI PTMP CCBSRequest/CCNRRequest 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_ptmp_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_CCNRRequest : ROSE_ETSI_CCBSRequest;
+
+	msg.args.etsi.CCBSRequest.call_linkage_id = cc_record->call_linkage_id;
+
+	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.
+ * \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_ptmp_cc_deactivate(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 = ROSE_ETSI_CCBSDeactivate;
+
+	msg.args.etsi.CCBSDeactivate.ccbs_reference = cc_record->ccbs_reference_id;
+
+	pos = rose_encode_invoke(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode and queue an CCBSDeactivate message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode CCBSDeactivate.
+ * \param cc_record Call completion record to process event.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int rose_cc_deactivate_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
+{
+	unsigned char buffer[256];
+	unsigned char *end;
+
+	end =
+		enc_etsi_ptmp_cc_deactivate(ctrl, buffer, buffer + sizeof(buffer), cc_record);
+	if (!end) {
+		return -1;
+	}
+
+	return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL);
+}
+
+/*!
+ * \internal
+ * \brief Encode and send an CCBSDeactivate message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode CCBSDeactivate.
+ * \param cc_record Call completion record to process event.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int send_cc_deactivate_req(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
+{
+	if (rose_cc_deactivate_encode(ctrl, call, cc_record)
+		|| q931_facility(ctrl, call)) {
+		pri_message(ctrl,
+			"Could not schedule facility message for CCBSDeactivate.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*!
+ * \internal
  * \brief Encode ETSI PTMP CCBSBFree message.
  *
  * \param ctrl D channel controller for diagnostic messages or global options.
@@ -990,6 +1106,80 @@
 
 /*!
  * \internal
+ * \brief Encode ETSI PTMP CCBSCall 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_ptmp_cc_recall(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 = ROSE_ETSI_CCBSCall;
+
+	msg.args.etsi.CCBSCall.ccbs_reference = cc_record->ccbs_reference_id;
+
+	pos = rose_encode_invoke(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode and queue a cc-recall message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode cc-recall.
+ * \param cc_record Call completion record to process event.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int rose_cc_recall_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
+{
+	unsigned char buffer[256];
+	unsigned char *end;
+
+	switch (ctrl->switchtype) {
+	case PRI_SWITCH_EUROISDN_E1:
+	case PRI_SWITCH_EUROISDN_T1:
+		if (q931_is_ptmp(ctrl)) {
+			end =
+				enc_etsi_ptmp_cc_recall(ctrl, buffer, buffer + sizeof(buffer), cc_record);
+		} else {
+			end =
+				enc_etsi_ptp_cc_operation(ctrl, buffer, buffer + sizeof(buffer),
+					ROSE_ETSI_CCBS_T_Call);
+		}
+		break;
+	case PRI_SWITCH_QSIG:
+		/*! \todo BUGBUG rose_cc_recall_encode(Q.SIG) not written */
+		return -1;
+	default:
+		return -1;
+	}
+	if (!end) {
+		return -1;
+	}
+
+	return pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer, NULL);
+}
+
+/*!
+ * \internal
  * \brief Copy the cc information into the ETSI ROSE call-information record.
  *
  * \param ctrl D channel controller for diagnostic messages or global options.
@@ -1411,6 +1601,17 @@
 	case CC_EVENT_MSG_RELEASE_COMPLETE:
 		str = "CC_EVENT_MSG_RELEASE_COMPLETE";
 		break;
+	case CC_EVENT_TIMEOUT_T_ACTIVATE:
+		str = "CC_EVENT_TIMEOUT_T_ACTIVATE";
+		break;
+#if 0
+	case CC_EVENT_TIMEOUT_T_DEACTIVATE:
+		str = "CC_EVENT_TIMEOUT_T_DEACTIVATE";
+		break;
+#endif
+	case CC_EVENT_TIMEOUT_T_INTERROGATE:
+		str = "CC_EVENT_TIMEOUT_T_INTERROGATE";
+		break;
 	case CC_EVENT_TIMEOUT_T_RETENTION:
 		str = "CC_EVENT_TIMEOUT_T_RETENTION";
 		break;
@@ -1857,6 +2058,175 @@
 
 /*!
  * \internal
+ * \brief FSM action to stop the PTMP T_ACTIVATE timer.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_stop_t_activate(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	struct apdu_event *msg;
+
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+
+	if (!cc_record->signaling) {
+		return;
+	}
+	msg = pri_call_apdu_find(cc_record->signaling, cc_record->t_activate_invoke_id);
+	if (msg) {
+		pri_call_apdu_delete(cc_record->signaling, msg);
+	}
+}
+
+/*!
+ * \internal
+ * \brief cc-request PTMP 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_ptmp(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:
+		/*
+		 * Since we received this facility, we will not be allocating any
+		 * reference and linkage id's.
+		 */
+		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 Encode and queue a cc-request message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode cc-request.
+ * \param cc_record Call completion record to process event.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int rose_cc_request(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
+{
+	unsigned char buffer[256];
+	unsigned char *end;
+	struct apdu_callback_data response;
+	int msgtype;
+
+	memset(&response, 0, sizeof(response));
+
+	switch (ctrl->switchtype) {
+	case PRI_SWITCH_EUROISDN_E1:
+	case PRI_SWITCH_EUROISDN_T1:
+		if (q931_is_ptmp(ctrl)) {
+			end =
+				enc_etsi_ptmp_cc_request(ctrl, buffer, buffer + sizeof(buffer),
+					cc_record);
+			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;
+		}
+		response.timeout_time = ctrl->timers[PRI_TIMER_T_ACTIVATE];
+		break;
+	case PRI_SWITCH_QSIG:
+		/* \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];
+		return -1;
+	default:
+		return -1;
+	}
+	if (!end) {
+		return -1;
+	}
+
+	response.user.ptr = cc_record;
+	response.invoke_id = ctrl->last_invoke;
+	cc_record->t_activate_invoke_id = ctrl->last_invoke;
+	return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, &response);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to queue the cc-request message.
+ *
+ * \param ctrl D channel controller.
+ * \param call Call leg from which to encode cc-request.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_queue_cc_request(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	if (rose_cc_request(ctrl, call, cc_record)) {
+		pri_message(ctrl, "Could not queue message for cc-request.\n");
+	}
+}
+
+/*!
+ * \internal
+ * \brief FSM action to send the CCBSDeactivate message.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_send_cc_deactivate_req(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	send_cc_deactivate_req(ctrl, cc_record->signaling, cc_record);
+}
+
+/*!
+ * \internal
  * \brief FSM action to send the CCBSBFree message.
  *
  * \param ctrl D channel controller.
@@ -2183,6 +2553,185 @@
 
 /*!
  * \internal
+ * \brief FSM action to pass up CC available to upper layer.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_pass_up_cc_available(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	struct pri_subcommand *subcmd;
+
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+
+	subcmd = q931_alloc_subcommand(ctrl);
+	if (!subcmd) {
+		return;
+	}
+
+	subcmd->cmd = PRI_SUBCMD_CC_AVAILABLE;
+	subcmd->u.cc_available.cc_id = cc_record->record_id;
+}
+
+/*!
+ * \internal
+ * \brief FSM action to pass up CC request response is success to upper layer.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_pass_up_cc_req_rsp_success(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	struct pri_subcommand *subcmd;
+
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+
+	subcmd = q931_alloc_subcommand(ctrl);
+	if (!subcmd) {
+		return;
+	}
+
+	subcmd->cmd = PRI_SUBCMD_CC_REQ_RSP;
+	subcmd->u.cc_request_rsp.cc_id = cc_record->record_id;
+	subcmd->u.cc_request_rsp.status = 0;/* success */
+	subcmd->u.cc_request_rsp.fail_code = 0;
+	subcmd->u.cc_request_rsp.retain_service = cc_record->option.retain_service;
+}
+
+/*!
+ * \internal
+ * \brief FSM action to pass up CC request response is failed to upper layer.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_pass_up_cc_req_rsp_fail(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	struct pri_subcommand *subcmd;
+
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+
+	subcmd = q931_alloc_subcommand(ctrl);
+	if (!subcmd) {
+		return;
+	}
+
+	subcmd->cmd = PRI_SUBCMD_CC_REQ_RSP;
+	subcmd->u.cc_request_rsp.cc_id = cc_record->record_id;
+	subcmd->u.cc_request_rsp.status =
+		(cc_record->msg.cc_req_rsp.reason == APDU_CALLBACK_REASON_MSG_ERROR)
+		? 2 /* error */ : 3 /* reject */;
+	subcmd->u.cc_request_rsp.fail_code = cc_record->msg.cc_req_rsp.code;
+	subcmd->u.cc_request_rsp.retain_service = 0;
+}
+
+/*!
+ * \internal
+ * \brief FSM action to pass up CC request response is timeout to upper layer.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_pass_up_cc_req_rsp_timeout(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	struct pri_subcommand *subcmd;
+
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+
+	subcmd = q931_alloc_subcommand(ctrl);
+	if (!subcmd) {
+		return;
+	}
+
+	subcmd->cmd = PRI_SUBCMD_CC_REQ_RSP;
+	subcmd->u.cc_request_rsp.cc_id = cc_record->record_id;
+	subcmd->u.cc_request_rsp.status = 1;/* timeout */
+	subcmd->u.cc_request_rsp.fail_code = 0;
+	subcmd->u.cc_request_rsp.retain_service = 0;
+}
+
+/*!
+ * \internal
+ * \brief FSM action to pass up CC B free to upper layer.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_pass_up_b_free(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	struct pri_subcommand *subcmd;
+
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+
+	subcmd = q931_alloc_subcommand(ctrl);
+	if (!subcmd) {
+		return;
+	}
+
+	subcmd->cmd = PRI_SUBCMD_CC_B_FREE;
+	subcmd->u.cc_b_free.cc_id =  cc_record->record_id;
+}
+
+/*!
+ * \internal
+ * \brief FSM action to pass up CC remote user free to upper layer.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_pass_up_remote_user_free(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	struct pri_subcommand *subcmd;
+
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+
+	subcmd = q931_alloc_subcommand(ctrl);
+	if (!subcmd) {
+		return;
+	}
+
+	subcmd->cmd = PRI_SUBCMD_CC_REMOTE_USER_FREE;
+	subcmd->u.cc_remote_user_free.cc_id =  cc_record->record_id;
+}
+
+/*!
+ * \internal
+ * \brief FSM action to pass up stop alerting to upper layer.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_pass_up_stop_alerting(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	struct pri_subcommand *subcmd;
+
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+
+	subcmd = q931_alloc_subcommand(ctrl);
+	if (!subcmd) {
+		return;
+	}
+
+	subcmd->cmd = PRI_SUBCMD_CC_STOP_ALERTING;
+	subcmd->u.cc_stop_alerting.cc_id =  cc_record->record_id;
+}
+
+/*!
+ * \internal
  * \brief FSM action to send error response to recall attempt.
  *
  * \param ctrl D channel controller.
@@ -2196,6 +2745,22 @@
 	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
 	rose_error_msg_encode(ctrl, cc_record->response.signaling, Q931_ANY_MESSAGE,
 		cc_record->response.invoke_id, code);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to queue CC recall marker.
+ *
+ * \param ctrl D channel controller.
+ * \param call Q.931 call leg.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_queue_setup_recall(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	rose_cc_recall_encode(ctrl, call, cc_record);
 }
 
 /*!
@@ -2806,6 +3371,266 @@
 
 /*!
  * \internal
+ * \brief CC FSM PTMP 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_ptmp_monitor_idle(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
+{
+	switch (event) {
+	case CC_EVENT_AVAILABLE:
+		/*
+		 * Before event is posted:
+		 *   Received CallInfoRetain
+		 *   Created cc_record
+		 *   Saved CallLinkageID
+		 */
+		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 PTMP 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_ptmp_monitor_avail(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
+{
+	/*
+	 * For PTMP TE mode the T_RETENTION timer is not defined.  However,
+	 * we will use it anyway in this state to protect our resources from
+	 * leaks caused by the network cable being disconnected.  This
+	 * timer should be set much longer than the network so normally
+	 * the CC records will be cleaned up by network activity.
+	 */
+	switch (event) {
+	case CC_EVENT_MSG_RELEASE:
+	case CC_EVENT_MSG_RELEASE_COMPLETE:
+		pri_cc_act_stop_t_retention(ctrl, cc_record);
+		pri_cc_act_start_t_retention(ctrl, cc_record);
+		break;
+	case CC_EVENT_CC_REQUEST:
+		/* cc_record->is_ccnr is set before event posted. */
+		pri_cc_act_queue_cc_request(ctrl, call, cc_record);
+		//pri_cc_act_start_t_activate(ctrl, cc_record);
+		pri_cc_act_stop_t_retention(ctrl, cc_record);
+		cc_record->state = CC_STATE_REQUESTED;
+		break;
+	case CC_EVENT_TIMEOUT_T_RETENTION:
+		/*
+		 * Received EraseCallLinkageID
+		 * or T_RETENTION really timed out.
+		 */
+		pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
+		pri_cc_act_stop_t_retention(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_retention(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 PTMP 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_ptmp_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:
+		/*
+		 * Before event is posted:
+		 *   Received CCBSRequest/CCNRRequest response
+		 *   Saved CCBSReference
+		 */
+		pri_cc_act_release_link_id(ctrl, cc_record);
+		pri_cc_act_pass_up_cc_req_rsp_success(ctrl, cc_record);
+		pri_cc_act_stop_t_activate(ctrl, cc_record);
+		/*
+		 * Start T_CCBS2 or T_CCNR2 depending upon CC mode.
+		 * For PTMP TE mode these timers are not defined.  However,
+		 * we will use them anyway to protect our resources from leaks
+		 * caused by the network cable being disconnected.  These
+		 * timers should be set much longer than the network
+		 * so normally the CC records will be cleaned up by network
+		 * activity.
+		 */
+		pri_cc_act_start_t_ccbs2(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);
+		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_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_LINK_CANCEL:
+		/* Received CCBSErase */
+		/* 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:
+		cc_record->state = CC_STATE_WAIT_DESTRUCTION;
+		break;
+	default:
+		break;
+	}
+}
+
+/*!
+ * \internal
+ * \brief CC FSM PTMP 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_ptmp_monitor_wait_destruction(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
+{
+	/* We were in the middle of a cc-request when we were asked to cancel. */
+	switch (event) {
+	case CC_EVENT_CC_REQUEST_ACCEPT:
+		/*
+		 * Before event is posted:
+		 *   Received CCBSRequest/CCNRRequest response
+		 *   Saved CCBSReference
+		 */
+		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;
+	default:
+		break;
+	}
+}
+
+/*!
+ * \internal
+ * \brief CC FSM PTMP 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_ptmp_monitor_activated(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
+{
+	switch (event) {
+	case CC_EVENT_B_FREE:
+		/* Received CCBSBFree */
+		pri_cc_act_pass_up_b_free(ctrl, cc_record);
+		break;
+	case CC_EVENT_REMOTE_USER_FREE:
+		/* Received CCBSRemoteUserFree */
+		pri_cc_act_pass_up_remote_user_free(ctrl, cc_record);
+		break;
+	case CC_EVENT_STOP_ALERTING:
+		pri_cc_act_pass_up_stop_alerting(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_CCBS2:
+		pri_cc_act_send_cc_deactivate_req(ctrl, cc_record);
+		pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
+		pri_cc_act_stop_t_ccbs2(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_pass_up_cc_cancel(ctrl, cc_record);
+		pri_cc_act_stop_t_ccbs2(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_send_cc_deactivate_req(ctrl, cc_record);
+		pri_cc_act_stop_t_ccbs2(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 state function type.
  *
  * \param ctrl D channel controller.
@@ -2832,6 +3657,17 @@
 /* *INDENT-ON* */
 };
 
+/*! CC FSM PTMP monitor state table. */
+static const pri_cc_fsm_state pri_cc_fsm_ptmp_monitor[CC_STATE_NUM] = {
+/* *INDENT-OFF* */
+	[CC_STATE_IDLE] = pri_cc_fsm_ptmp_monitor_idle,
+	[CC_STATE_AVAILABLE] = pri_cc_fsm_ptmp_monitor_avail,
+	[CC_STATE_REQUESTED] = pri_cc_fsm_ptmp_monitor_req,

[... 449 lines stripped ...]



More information about the libpri-commits mailing list