[libpri-commits] rmudgett: branch group/ccss r1372 - /team/group/ccss/

SVN commits to the libpri project libpri-commits at lists.digium.com
Tue Dec 8 21:06:56 CST 2009


Author: rmudgett
Date: Tue Dec  8 21:06:52 2009
New Revision: 1372

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

*  Added call-completion FSM support infrastructure.
*  Added PRI_DEBUG_CC output enable flag.
*  Added conversion routines to/from rose and q931 party address
structures.
*  Added Q931_ANY_MESSAGE with support so facility messages can go out on
any Q.931 message going out next.
*  Fixed handling of ROSE APDU response messages coming in on broadcast
and specific dummy call reference call.
*  Fixed handling of ROSE APDU response message timeout to allow the
callback to be able to delete the apdu_event structure safely.

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
    team/group/ccss/pri_q931.h
    team/group/ccss/q931.c

Modified: team/group/ccss/libpri.h
URL: http://svnview.digium.com/svn/libpri/team/group/ccss/libpri.h?view=diff&rev=1372&r1=1371&r2=1372
==============================================================================
--- team/group/ccss/libpri.h (original)
+++ team/group/ccss/libpri.h Tue Dec  8 21:06:52 2009
@@ -51,6 +51,7 @@
 #define	PRI_DEBUG_Q931_ANOMALY 	(1 << 7)	/* Show unexpected events */
 #define PRI_DEBUG_APDU			(1 << 8)	/* Debug of APDU components such as ROSE */
 #define PRI_DEBUG_AOC			(1 << 9)	/* Debug of Advice of Charge ROSE Messages */
+#define PRI_DEBUG_CC			(1 << 10)	/* Debug call-completion. */
 
 #define PRI_DEBUG_ALL			(0xffff)	/* Everything */
 
@@ -1454,7 +1455,7 @@
 long pri_cc_available(struct pri *ctrl, q931_call *call);
 int pri_cc_req(struct pri *ctrl, long cc_id, int mode);
 void pri_cc_req_rsp(struct pri *ctrl, long cc_id, int status);
-void pri_cc_remote_user_free(struct pri *ctrl, long cc_id);
+int pri_cc_remote_user_free(struct pri *ctrl, long cc_id, int is_ccbs_busy);
 int 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);

Modified: team/group/ccss/pri_cc.c
URL: http://svnview.digium.com/svn/libpri/team/group/ccss/pri_cc.c?view=diff&rev=1372&r1=1371&r2=1372
==============================================================================
--- team/group/ccss/pri_cc.c (original)
+++ team/group/ccss/pri_cc.c Tue Dec  8 21:06:52 2009
@@ -344,13 +344,13 @@
  * \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 call Call leg from which to encode message.
+ * \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_available(struct pri *ctrl, unsigned char *pos,
-	unsigned char *end, q931_call *call)
+	unsigned char *end, struct pri_cc_record *cc_record)
 {
 	struct rose_msg_invoke msg;
 
@@ -363,7 +363,7 @@
 	msg.invoke_id = get_invokeid(ctrl);
 	msg.operation = ROSE_ETSI_CallInfoRetain;
 
-	msg.args.etsi.CallInfoRetain.call_linkage_id = call->cc.record->call_linkage_id;
+	msg.args.etsi.CallInfoRetain.call_linkage_id = cc_record->call_linkage_id;
 
 	pos = rose_encode_invoke(ctrl, pos, end, &msg);
 
@@ -371,16 +371,18 @@
 }
 
 /*!
+ * \internal
  * \brief Encode and queue a cc-available message.
  *
  * \param ctrl D channel controller for diagnostic messages or global options.
  * \param call Call leg from which to encode call completion available.
+ * \param cc_record Call completion record to process event.
  * \param msgtype Q.931 message type to put facility ie in.
  *
  * \retval 0 on success.
  * \retval -1 on error.
  */
-int rose_cc_available_encode(struct pri *ctrl, q931_call *call, int msgtype)
+static int rose_cc_available_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, int msgtype)
 {
 	unsigned char buffer[256];
 	unsigned char *end;
@@ -390,7 +392,8 @@
 	case PRI_SWITCH_EUROISDN_T1:
 		if (q931_is_ptmp(ctrl)) {
 			end =
-				enc_etsi_ptmp_cc_available(ctrl, buffer, buffer + sizeof(buffer), call);
+				enc_etsi_ptmp_cc_available(ctrl, buffer, buffer + sizeof(buffer),
+					cc_record);
 		} else {
 			end =
 				enc_etsi_ptp_cc_operation(ctrl, buffer, buffer + sizeof(buffer),
@@ -411,69 +414,2105 @@
 }
 
 /*!
- * \brief Send an event from the upper layer to the cc state machine.
+ * \internal
+ * \brief Encode ETSI PTMP EraseCallLinkageID 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_erase_call_linkage(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_EraseCallLinkageID;
+
+	msg.args.etsi.EraseCallLinkageID.call_linkage_id = cc_record->call_linkage_id;
+
+	pos = rose_encode_invoke(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode and queue an EraseCallLinkageID message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode EraseCallLinkageID.
+ * \param cc_record Call completion record to process event.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int rose_erase_call_linkage_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
+{
+	unsigned char buffer[256];
+	unsigned char *end;
+
+	end =
+		enc_etsi_ptmp_erase_call_linkage(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 EraseCallLinkageID message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode EraseCallLinkageID.
+ * \param cc_record Call completion record to process event.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int send_erase_call_linkage_id(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
+{
+	if (rose_erase_call_linkage_encode(ctrl, call, cc_record)
+		|| q931_facility(ctrl, call)) {
+		pri_message(ctrl,
+			"Could not schedule facility message for EraseCallLinkageID.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Encode ETSI PTMP CCBSErase 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.
+ * \param reason CCBS Erase reason
+ *  normal-unspecified(0), t-CCBS2-timeout(1), t-CCBS3-timeout(2), basic-call-failed(3)
+ *
+ * \retval Start of the next ASN.1 component to encode on success.
+ * \retval NULL on error.
+ */
+static unsigned char *enc_etsi_ptmp_ccbs_erase(struct pri *ctrl,
+	unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record, int reason)
+{
+	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_CCBSErase;
+
+/* BUGBUG need BC, HLC, and LLC from initial SETUP */
+	//msg.args.etsi.CCBSErase.q931ie.contents
+	//msg.args.etsi.CCBSErase.q931ie.length = ;
+
+	q931_copy_address_to_rose(ctrl, &msg.args.etsi.CCBSErase.address_of_b,
+		&cc_record->party_b);
+	msg.args.etsi.CCBSErase.recall_mode = cc_record->option.recall_mode;
+	msg.args.etsi.CCBSErase.ccbs_reference = cc_record->ccbs_reference_id;
+	msg.args.etsi.CCBSErase.reason = reason;
+
+	pos = rose_encode_invoke(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode and queue an CCBSErase message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode CCBSErase.
+ * \param cc_record Call completion record to process event.
+ * \param reason CCBS Erase reason
+ *  normal-unspecified(0), t-CCBS2-timeout(1), t-CCBS3-timeout(2), basic-call-failed(3)
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int rose_ccbs_erase_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, int reason)
+{
+	unsigned char buffer[256];
+	unsigned char *end;
+
+	end =
+		enc_etsi_ptmp_ccbs_erase(ctrl, buffer, buffer + sizeof(buffer), cc_record,
+			reason);
+	if (!end) {
+		return -1;
+	}
+
+	return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL);
+}
+
+/*!
+ * \internal
+ * \brief Encode and send an CCBSErase message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode EraseCallLinkageID.
+ * \param cc_record Call completion record to process event.
+ * \param reason CCBS Erase reason
+ *  normal-unspecified(0), t-CCBS2-timeout(1), t-CCBS3-timeout(2), basic-call-failed(3)
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int send_ccbs_erase(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, int reason)
+{
+	if (rose_ccbs_erase_encode(ctrl, call, cc_record, reason)
+		|| q931_facility(ctrl, call)) {
+		pri_message(ctrl,
+			"Could not schedule facility message for CCBSErase.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Encode ETSI PTMP CCBSStatusRequest 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_ccbs_status_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 = ROSE_ETSI_CCBSStatusRequest;
+
+/* BUGBUG need BC, HLC, and LLC from initial SETUP */
+	//msg.args.etsi.CCBSStatusRequest.q931ie.contents
+	//msg.args.etsi.CCBSStatusRequest.q931ie.length = ;
+
+	msg.args.etsi.CCBSStatusRequest.recall_mode = cc_record->option.recall_mode;
+	msg.args.etsi.CCBSStatusRequest.ccbs_reference = cc_record->ccbs_reference_id;
+
+	pos = rose_encode_invoke(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode ETSI PTMP CCBSBFree 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_ccbs_b_free(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_CCBSBFree;
+
+/* BUGBUG need BC, HLC, and LLC from initial SETUP */
+	//msg.args.etsi.CCBSBFree.q931ie.contents
+	//msg.args.etsi.CCBSBFree.q931ie.length = ;
+
+	q931_copy_address_to_rose(ctrl, &msg.args.etsi.CCBSBFree.address_of_b,
+		&cc_record->party_b);
+	msg.args.etsi.CCBSBFree.recall_mode = cc_record->option.recall_mode;
+	msg.args.etsi.CCBSBFree.ccbs_reference = cc_record->ccbs_reference_id;
+
+	pos = rose_encode_invoke(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode and queue an CCBSBFree message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode CCBSBFree.
+ * \param cc_record Call completion record to process event.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int rose_ccbs_b_free_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
+{
+	unsigned char buffer[256];
+	unsigned char *end;
+
+	end =
+		enc_etsi_ptmp_ccbs_b_free(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 CCBSBFree message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode CCBSBFree.
+ * \param cc_record Call completion record to process event.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int send_ccbs_b_free(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
+{
+	if (rose_ccbs_b_free_encode(ctrl, call, cc_record)
+		|| q931_facility(ctrl, call)) {
+		pri_message(ctrl,
+			"Could not schedule facility message for CCBSBFree.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Encode ETSI PTMP CCBSRemoteUserFree 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_remote_user_free(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_CCBSRemoteUserFree;
+
+/* BUGBUG need BC, HLC, and LLC from initial SETUP */
+	//msg.args.etsi.CCBSRemoteUserFree.q931ie.contents
+	//msg.args.etsi.CCBSRemoteUserFree.q931ie.length = ;
+
+	q931_copy_address_to_rose(ctrl, &msg.args.etsi.CCBSRemoteUserFree.address_of_b,
+		&cc_record->party_b);
+	msg.args.etsi.CCBSRemoteUserFree.recall_mode = cc_record->option.recall_mode;
+	msg.args.etsi.CCBSRemoteUserFree.ccbs_reference = cc_record->ccbs_reference_id;
+
+	pos = rose_encode_invoke(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode and queue a remote user free message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode remote user free message.
+ * \param cc_record Call completion record to process event.
+ * \param msgtype Q.931 message type to put facility ie in.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int rose_remote_user_free_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, int msgtype)
+{
+	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_remote_user_free(ctrl, buffer, buffer + sizeof(buffer),
+					cc_record);
+		} else {
+			end =
+				enc_etsi_ptp_cc_operation(ctrl, buffer, buffer + sizeof(buffer),
+					ROSE_ETSI_CCBS_T_RemoteUserFree);
+		}
+		break;
+	case PRI_SWITCH_QSIG:
+		/* BUGBUG rose_remote_user_free_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 an remote user free message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode remote user free.
+ * \param cc_record Call completion record to process event.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int send_remote_user_free(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
+{
+	if (rose_remote_user_free_encode(ctrl, call, cc_record, Q931_FACILITY)
+		|| q931_facility(ctrl, call)) {
+		pri_message(ctrl,
+			"Could not schedule facility message for remote user free.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Encode ETSI PTMP CCBSStopAlerting 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_ccbs_stop_alerting(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_CCBSStopAlerting;
+
+	msg.args.etsi.CCBSStopAlerting.ccbs_reference = cc_record->ccbs_reference_id;
+
+	pos = rose_encode_invoke(ctrl, pos, end, &msg);
+
+	return pos;
+}
+
+/*!
+ * \internal
+ * \brief Encode and queue an CCBSStopAlerting message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode CCBSStopAlerting.
+ * \param cc_record Call completion record to process event.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int rose_ccbs_stop_alerting_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
+{
+	unsigned char buffer[256];
+	unsigned char *end;
+
+	end =
+		enc_etsi_ptmp_ccbs_stop_alerting(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 CCBSStopAlerting message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode remote user free.
+ * \param cc_record Call completion record to process event.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int send_ccbs_stop_alerting(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
+{
+	if (rose_ccbs_stop_alerting_encode(ctrl, call, cc_record)
+		|| q931_facility(ctrl, call)) {
+		pri_message(ctrl,
+			"Could not schedule facility message for CCBSStopAlerting.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Convert the given call completion state to a string.
+ *
+ * \param state CC state to convert to string.
+ *
+ * \return String version of call completion state.
+ */
+static const char *pri_cc_fsm_state_str(enum CC_STATES state)
+{
+	const char *str;
+
+	str = "Unknown";
+	switch (state) {
+	case CC_STATE_IDLE:
+		str = "CC_STATE_IDLE";
+		break;
+	case CC_STATE_PENDING_AVAILABLE:
+		str = "CC_STATE_PENDING_AVAILABLE";
+		break;
+	case CC_STATE_AVAILABLE:
+		str = "CC_STATE_AVAILABLE";
+		break;
+	case CC_STATE_REQUESTED:
+		str = "CC_STATE_REQUESTED";
+		break;
+	case CC_STATE_ACTIVATED:
+		str = "CC_STATE_ACTIVATED";
+		break;
+	case CC_STATE_B_AVAILABLE:
+		str = "CC_STATE_B_AVAILABLE";
+		break;
+	case CC_STATE_SUSPENDED:
+		str = "CC_STATE_SUSPENDED";
+		break;
+	case CC_STATE_WAIT_CALLBACK:
+		str = "CC_STATE_WAIT_CALLBACK";
+		break;
+	case CC_STATE_CALLBACK:
+		str = "CC_STATE_CALLBACK";
+		break;
+	case CC_STATE_WAIT_DESTRUCTION:
+		str = "CC_STATE_WAIT_DESTRUCTION";
+		break;
+	case CC_STATE_NUM:
+		/* Not a real state. */
+		break;
+	}
+	return str;
+}
+
+/*!
+ * \internal
+ * \brief Convert the given call completion event to a string.
+ *
+ * \param event CC event to convert to string.
+ *
+ * \return String version of call completion event.
+ */
+static const char *pri_cc_fsm_event_str(enum CC_EVENTS event)
+{
+	const char *str;
+
+	str = "Unknown";
+	switch (event) {
+	case CC_EVENT_AVAILABLE:
+		str = "CC_EVENT_AVAILABLE";
+		break;
+	case CC_EVENT_CC_REQUEST:
+		str = "CC_EVENT_CC_REQUEST";
+		break;
+	case CC_EVENT_CC_REQUEST_ACCEPT:
+		str = "CC_EVENT_CC_REQUEST_ACCEPT";
+		break;
+	case CC_EVENT_REMOTE_USER_FREE:
+		str = "CC_EVENT_REMOTE_USER_FREE";
+		break;
+	case CC_EVENT_A_STATUS:
+		str = "CC_EVENT_A_STATUS";
+		break;
+	case CC_EVENT_A_FREE:
+		str = "CC_EVENT_A_FREE";
+		break;
+	case CC_EVENT_A_BUSY:
+		str = "CC_EVENT_A_BUSY";
+		break;
+	case CC_EVENT_SUSPEND:
+		str = "CC_EVENT_SUSPEND";
+		break;
+	case CC_EVENT_RESUME:
+		str = "CC_EVENT_RESUME";
+		break;
+	case CC_EVENT_RECALL:
+		str = "CC_EVENT_RECALL";
+		break;
+	case CC_EVENT_LINK_CANCEL:
+		str = "CC_EVENT_LINK_CANCEL";
+		break;
+	case CC_EVENT_CANCEL:
+		str = "CC_EVENT_CANCEL";
+		break;
+	case CC_EVENT_SIGNALING_GONE:
+		str = "CC_EVENT_SIGNALING_GONE";
+		break;
+	case CC_EVENT_MSG_ALERTING:
+		str = "CC_EVENT_MSG_ALERTING";
+		break;
+	case CC_EVENT_MSG_DISCONNECT:
+		str = "CC_EVENT_MSG_DISCONNECT";
+		break;
+	case CC_EVENT_MSG_RELEASE:
+		str = "CC_EVENT_MSG_RELEASE";
+		break;
+	case CC_EVENT_MSG_RELEASE_COMPLETE:
+		str = "CC_EVENT_MSG_RELEASE_COMPLETE";
+		break;
+	case CC_EVENT_TIMEOUT_T_RETENTION:
+		str = "CC_EVENT_TIMEOUT_T_RETENTION";
+		break;
+	case CC_EVENT_TIMEOUT_T_CCBS1:
+		str = "CC_EVENT_TIMEOUT_T_CCBS1";
+		break;
+	case CC_EVENT_TIMEOUT_EXTENDED_T_CCBS1:
+		str = "CC_EVENT_TIMEOUT_EXTENDED_T_CCBS1";
+		break;
+	case CC_EVENT_TIMEOUT_T_CCBS2:
+		str = "CC_EVENT_TIMEOUT_T_CCBS2";
+		break;
+	case CC_EVENT_TIMEOUT_T_CCBS3:
+		str = "CC_EVENT_TIMEOUT_T_CCBS3";
+		break;
+	}
+	return str;
+}
+
+static const char pri_cc_act_header[] = "  CC-Act: %s\n";
+#define PRI_CC_ACT_DEBUG_OUTPUT(ctrl)							\
+	if ((ctrl)->debug & PRI_DEBUG_CC) {							\
+		pri_message((ctrl), pri_cc_act_header, __FUNCTION__);	\
+	}
+
+/*!
+ * \internal
+ * \brief FSM action to mark FSM for destruction.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_set_self_destruct(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	cc_record->fsm_complete = 1;
+}
+
+/*!
+ * \internal
+ * \brief FSM action to send CC available message.
  *
  * \param ctrl D channel controller.
  * \param call Q.931 call leg.
  * \param cc_record Call completion record to process event.
+ * \param msgtype Q.931 message type to put facility ie in.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_send_cc_available(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, int msgtype)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	rose_cc_available_encode(ctrl, call, cc_record, msgtype);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to stop the PTMP T_RETENTION timer.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_stop_t_retention(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	pri_schedule_del(ctrl, cc_record->fsm.ptmp.t_retention);
+	cc_record->fsm.ptmp.t_retention = 0;
+}
+
+/*!
+ * \internal
+ * \brief T_RETENTION timeout callback.
+ *
+ * \param data CC record pointer.
+ *
+ * \return Nothing
+ */
+static void pri_cc_timeout_t_retention(void *data)
+{
+	struct pri_cc_record *cc_record = data;
+
+	cc_record->fsm.ptmp.t_retention = 0;
+	q931_cc_timeout(cc_record->signaling->pri, cc_record->signaling, cc_record,
+		CC_EVENT_TIMEOUT_T_RETENTION);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to start the PTMP T_RETENTION timer.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_start_t_retention(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	if (cc_record->fsm.ptmp.t_retention) {
+		pri_error(ctrl, "!! T_RETENTION is already running!");
+		pri_schedule_del(ctrl, cc_record->fsm.ptmp.t_retention);
+	}
+	cc_record->fsm.ptmp.t_retention = pri_schedule_event(ctrl,
+		ctrl->timers[PRI_TIMER_T_RETENTION], pri_cc_timeout_t_retention, cc_record);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to stop the PTMP EXTENDED_T_CCBS1 timer.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_stop_extended_t_ccbs1(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	pri_schedule_del(ctrl, cc_record->fsm.ptmp.extended_t_ccbs1);
+	cc_record->fsm.ptmp.extended_t_ccbs1 = 0;
+}
+
+/*!
+ * \internal
+ * \brief EXTENDED_T_CCBS1 timeout callback.
+ *
+ * \param data CC record pointer.
+ *
+ * \return Nothing
+ */
+static void pri_cc_timeout_extended_t_ccbs1(void *data)
+{
+	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,
+		CC_EVENT_TIMEOUT_EXTENDED_T_CCBS1);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to start the PTMP extended T_CCBS1 timer.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_start_extended_t_ccbs1(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	if (cc_record->fsm.ptmp.extended_t_ccbs1) {
+		pri_error(ctrl, "!! Extended T_CCBS1 is already running!");
+		pri_schedule_del(ctrl, cc_record->fsm.ptmp.extended_t_ccbs1);
+	}
+	/* Timeout is T_CCBS1 + 2 seconds. */
+	cc_record->fsm.ptmp.extended_t_ccbs1 = pri_schedule_event(ctrl,
+		ctrl->timers[PRI_TIMER_T_CCBS1] + 2000, pri_cc_timeout_extended_t_ccbs1,
+		cc_record);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to stop the PTMP T_CCBS2 timer.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_stop_t_ccbs2(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	pri_schedule_del(ctrl, cc_record->fsm.ptmp.t_ccbs2);
+	cc_record->fsm.ptmp.t_ccbs2 = 0;
+}
+
+/*!
+ * \internal
+ * \brief T_CCBS2 timeout callback.
+ *
+ * \param data CC record pointer.
+ *
+ * \return Nothing
+ */
+static void pri_cc_timeout_t_ccbs2(void *data)
+{
+	struct pri_cc_record *cc_record = data;
+
+	cc_record->fsm.ptmp.t_ccbs2 = 0;
+	q931_cc_timeout(cc_record->signaling->pri, cc_record->signaling, cc_record,
+		CC_EVENT_TIMEOUT_T_CCBS2);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to start the PTMP T_CCBS2 timer.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_start_t_ccbs2(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	if (cc_record->fsm.ptmp.t_ccbs2) {
+		pri_error(ctrl, "!! T_CCBS2/T_CCNR2 is already running!");
+		pri_schedule_del(ctrl, cc_record->fsm.ptmp.t_ccbs2);
+	}
+	cc_record->fsm.ptmp.t_ccbs2 = pri_schedule_event(ctrl,
+		ctrl->timers[cc_record->is_ccnr ? PRI_TIMER_T_CCNR2 : PRI_TIMER_T_CCBS2],
+		pri_cc_timeout_t_ccbs2, cc_record);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to stop the PTMP T_CCBS3 timer.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_stop_t_ccbs3(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	pri_schedule_del(ctrl, cc_record->fsm.ptmp.t_ccbs3);
+	cc_record->fsm.ptmp.t_ccbs3 = 0;
+}
+
+/*!
+ * \internal
+ * \brief T_CCBS3 timeout callback.
+ *
+ * \param data CC record pointer.
+ *
+ * \return Nothing
+ */
+static void pri_cc_timeout_t_ccbs3(void *data)
+{
+	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,
+		CC_EVENT_TIMEOUT_T_CCBS3);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to start the PTMP T_CCBS3 timer.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_start_t_ccbs3(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	if (cc_record->fsm.ptmp.t_ccbs3) {
+		pri_error(ctrl, "!! T_CCBS3 is already running!");
+		pri_schedule_del(ctrl, cc_record->fsm.ptmp.t_ccbs3);
+	}
+	cc_record->fsm.ptmp.t_ccbs3 = pri_schedule_event(ctrl,
+		ctrl->timers[PRI_TIMER_T_CCBS3], pri_cc_timeout_t_ccbs3, cc_record);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to send the EraseCallLinkageID message.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_send_erase_call_linkage_id(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	send_erase_call_linkage_id(ctrl, cc_record->signaling, cc_record);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to send the CCBSErase message.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ * \param reason CCBS Erase reason
+ *  normal-unspecified(0), t-CCBS2-timeout(1), t-CCBS3-timeout(2), basic-call-failed(3)
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_send_ccbs_erase(struct pri *ctrl, struct pri_cc_record *cc_record, int reason)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	send_ccbs_erase(ctrl, cc_record->signaling, cc_record, reason);
+}
+
+/*!
+ * \internal
+ * \brief Find the T_CCBS1 timer/CCBSStatusRequest message.
+ *
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Facility message pointer or NULL if not active.
+ */
+static struct apdu_event *pri_cc_get_t_ccbs1_status(struct pri_cc_record *cc_record)
+{
+	return pri_call_apdu_find(cc_record->signaling,
+		cc_record->fsm.ptmp.t_ccbs1_invoke_id);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to stop the PTMP T_CCBS1 timer.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_stop_t_ccbs1(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	struct apdu_event *msg;
+
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+
+	msg = pri_cc_get_t_ccbs1_status(cc_record);
+	if (msg) {
+		pri_call_apdu_delete(cc_record->signaling, msg);
+	}
+}
+
+/*!
+ * \internal
+ * \brief CCBSStatusRequest 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_ccbs_status_response(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_CCBS1);
+		break;
+	case APDU_CALLBACK_REASON_MSG_RESULT:
+		pri_cc_event(ctrl, call, cc_record, msg->result->args.etsi.CCBSStatusRequest.free
+			? CC_EVENT_A_FREE : CC_EVENT_A_BUSY);
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Encode and queue an CCBSStatusRequest message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode CCBSStatusRequest.
+ * \param cc_record Call completion record to process event.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int rose_ccbs_status_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;
+
+	end =
+		enc_etsi_ptmp_ccbs_status_request(ctrl, buffer, buffer + sizeof(buffer),
+			cc_record);
+	if (!end) {
+		return -1;
+	}
+
+	memset(&response, 0, sizeof(response));
+	response.invoke_id = ctrl->last_invoke;
+	response.timeout_time = ctrl->timers[PRI_TIMER_T_CCBS1];
+	response.callback = pri_cc_ccbs_status_response;
+	response.user.ptr = cc_record;
+	return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, &response);
+}
+
+/*!
+ * \internal
+ * \brief Encode and send an CCBSStatusRequest message.
+ *
+ * \param ctrl D channel controller for diagnostic messages or global options.
+ * \param call Call leg from which to encode CCBSStatusRequest.
+ * \param cc_record Call completion record to process event.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int send_ccbs_status_request(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
+{
+	if (rose_ccbs_status_request(ctrl, call, cc_record)
+		|| q931_facility(ctrl, call)) {
+		pri_message(ctrl,
+			"Could not schedule facility message for CCBSStatusRequest.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief FSM action to send the CCBSStatusRequest message.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_send_ccbs_status_request(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	send_ccbs_status_request(ctrl, cc_record->signaling, cc_record);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to send the CCBSBFree message.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_send_ccbs_b_free(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	send_ccbs_b_free(ctrl, cc_record->signaling, cc_record);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to send the remote user free message.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_send_remote_user_free(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	send_remote_user_free(ctrl, cc_record->signaling, cc_record);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to send the CCBSStopAlerting message.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_send_ccbs_stop_alerting(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	send_ccbs_stop_alerting(ctrl, cc_record->signaling, cc_record);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to release the call linkage id.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_release_link_id(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	cc_record->call_linkage_id = CC_PTMP_INVALID_ID;
+}
+
+/*!
+ * \internal
+ * \brief FSM action to reset raw A status.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_reset_raw_a_status(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	cc_record->fsm.ptmp.party_a_status_acc = CC_PARTY_A_AVAILABILITY_INVALID;
+}
+
+/*!
+ * \internal
+ * \brief FSM action to add raw A status with busy.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_add_raw_a_status_busy(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	if (cc_record->fsm.ptmp.party_a_status_acc != CC_PARTY_A_AVAILABILITY_FREE) {
+		cc_record->fsm.ptmp.party_a_status_acc = CC_PARTY_A_AVAILABILITY_BUSY;
+	}
+}
+
+/*!
+ * \internal
+ * \brief FSM action to set raw A status to free.
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ */
+static void pri_cc_act_set_raw_a_status_free(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	PRI_CC_ACT_DEBUG_OUTPUT(ctrl);
+	cc_record->fsm.ptmp.party_a_status_acc = CC_PARTY_A_AVAILABILITY_FREE;
+}
+
+/*!
+ * \internal
+ * \brief Fill in the status response party A status update event.
+ *
+ * \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_fill_status_rsp_a(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
+{
+	struct pri_subcommand *subcmd;
+
+	if (cc_record->fsm.ptmp.party_a_status_acc == CC_PARTY_A_AVAILABILITY_INVALID) {
+		/* Accumulated party A status is invalid so don't pass it up. */
+		return;
+	}
+
+	subcmd = q931_alloc_subcommand(ctrl);
+	if (!subcmd) {
+		pri_error(ctrl, "ERROR: Too many facility subcommands\n");
+		return;
+	}
+
+	subcmd->cmd = PRI_SUBCMD_CC_STATUS_REQ_RSP;
+	subcmd->u.cc_status_req_rsp.cc_id =  cc_record->record_id;
+	subcmd->u.cc_status_req_rsp.status =
+		(cc_record->fsm.ptmp.party_a_status_acc == CC_PARTY_A_AVAILABILITY_FREE)
+		? 0 /* free */ : 1 /* busy */;
+}
+
+/*!
+ * \internal
+ * \brief Pass up party A status to upper layer (indirectly).
+ *
+ * \param data CC record pointer.
+ *
+ * \return Nothing
+ */
+static void pri_cc_indirect_status_rsp_a(void *data)
+{
+	struct pri_cc_record *cc_record = data;
+
+	q931_cc_indirect(cc_record->signaling->pri, cc_record->signaling, cc_record,
+		pri_cc_fill_status_rsp_a);
+}
+
+/*!
+ * \internal
+ * \brief FSM action to pass up party A status to upper layer (indirectly).
+ *
+ * \param ctrl D channel controller.
+ * \param cc_record Call completion record to process event.
+ *
+ * \return Nothing
+ *
+ * \note
+ * Warning:  Must not use this action with pri_cc_act_set_self_destruct() in the
+ * same event.
+ */
+static void pri_cc_act_pass_up_status_rsp_a_indirect(struct pri *ctrl, struct pri_cc_record *cc_record)
+{
+	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);
+	}
+}
+
+/*!
+ * \internal
+ * \brief FSM action to pass up party A status 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_status_rsp_a(struct pri *ctrl, struct pri_cc_record *cc_record)
+{

[... 2195 lines stripped ...]



More information about the libpri-commits mailing list