[asterisk-commits] rmudgett: branch rmudgett/misdn_facility r154780 - in /team/rmudgett/misdn_fa...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Nov 5 11:42:23 CST 2008


Author: rmudgett
Date: Wed Nov  5 11:42:22 2008
New Revision: 154780

URL: http://svn.digium.com/view/asterisk?view=rev&rev=154780
Log:
Merged revision 153295 from
https://origsvn.digium.com/svn/asterisk/be/branches/C.2-datus

..........
r153295 | rmudgett | 2008-10-31 20:13:02 -0500 (Fri, 31 Oct 2008) | 1 line

Initial mISDN CCBS/CCNR implementation.  Needs testing

Modified:
    team/rmudgett/misdn_facility/channels/chan_misdn.c
    team/rmudgett/misdn_facility/channels/misdn/isdn_lib.c
    team/rmudgett/misdn_facility/channels/misdn/isdn_lib.h
    team/rmudgett/misdn_facility/channels/misdn/isdn_msg_parser.c

Modified: team/rmudgett/misdn_facility/channels/chan_misdn.c
URL: http://svn.digium.com/view/asterisk/team/rmudgett/misdn_facility/channels/chan_misdn.c?view=diff&rev=154780&r1=154779&r2=154780
==============================================================================
--- team/rmudgett/misdn_facility/channels/chan_misdn.c (original)
+++ team/rmudgett/misdn_facility/channels/chan_misdn.c Wed Nov  5 11:42:22 2008
@@ -28,6 +28,9 @@
  *
  * \ingroup channel_drivers
  */
+
+/* Define to enable cli commands to generate canned CCBS messages. */
+// #define CCBS_TEST_MESSAGES	1
 
 /*** MODULEINFO
 	<depend>isdnnet</depend>
@@ -48,6 +51,7 @@
 #include <sys/file.h>
 #include <semaphore.h>
 #include <ctype.h>
+#include <time.h>
 
 #include "asterisk/channel.h"
 #include "asterisk/config.h"
@@ -111,6 +115,137 @@
 static char *complete_show_config(struct ast_cli_args *a);
 
 /* BEGIN: chan_misdn.h */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+#define MISDN_PEER_LINKS_MAX		64
+#define MISDN_PEER_AGE_MAX			20	/* seconds */
+
+/*
+ * This timeout duration is to clean up any call completion records that
+ * are forgotten about by the switch.
+ */
+#define MISDN_CC_RECORD_AGE_MAX		(6UL * 60 * 60)	/* seconds */
+
+#define MISDN_CC_REQUEST_WAIT_MAX	5	/* seconds */
+
+struct misdn_peer_link {
+	/*! \brief Asterisk channel that is a potential peer (NULL if not allocated) */
+	struct ast_channel *peer;
+
+	/*! \brief Time the record was allocated. */
+	time_t time_created;
+};
+
+/*! \brief Peer link guardian */
+ast_mutex_t misdn_peer_link_lock;
+/*! \brief mISDN peer link database */
+static struct misdn_peer_link misdn_peers[MISDN_PEER_LINKS_MAX];
+
+struct misdn_cc_notify {
+	/*! \brief Dialplan: Notify extension priority */
+	int priority;
+
+	/*! \brief Dialplan: Notify extension context */
+	char context[AST_MAX_CONTEXT];
+
+	/*! \brief Dialplan: Notify caller-extension number (User-A) */
+	char exten[AST_MAX_EXTENSION];
+};
+
+/*! \brief mISDN call completion record */
+struct misdn_cc_record {
+	/*! \brief Next record in linked list */
+	struct misdn_cc_record *next;
+
+	/*! \brief Time the record was created. */
+	time_t time_created;
+
+	/*! \brief MISDN_CC_RECORD_ID value */
+	int record_id;
+
+	/*!
+	 * \brief Logical Layer 1 port associated with this
+	 * call completion record
+	 */
+	int port;
+
+	/*! \brief CallLinkageID (valid when port determined) */
+	int linkage_id;
+
+	/*! \breif CCBSReference (valid when activated) */
+	int reference_id;
+
+	/*! \brief TRUE if call completion activated (reference_id is valid) */
+	int activated;
+
+	/*! \brief Outstanding message ID (valid while outstanding_message) */
+	int invoke_id;
+
+	/*! \brief TRUE if waiting for a response from a message (invoke_id is valid) */
+	int outstanding_message;
+
+	/*! \brief TRUE if activation has been requested */
+	int activation_requested;
+
+	/*! \brief globalRecall(0),	specificRecall(1) */
+	int recall_mode;
+
+	/*! \brief TRUE if User-A is free (Used to answer CCBSStatusRequest) */
+	int party_a_free;
+
+	/*! \brief Error code received from last outstanding message. */
+	enum FacErrorCode error_code;
+
+	/*! \brief Reject code received from last outstanding message. */
+	enum FacRejectCode reject_code;
+
+	/* Information obtained from CCBSRemoteUserFree or CCBSBFree messages */
+	//struct FacAddress Address_Of_B;
+	//struct Q931_Bc_Hlc_Llc Q931ie_Of_B;
+
+	/*!
+	 * \brief Saved struct misdn_bchannel call information when
+	 * attempted to call User-B
+	 */
+	struct {
+		/*! \brief User-A caller id information */
+		struct misdn_party_id caller;
+
+		/*! \brief User-B number information */
+		struct misdn_party_dialing dialed;
+
+		/*! \brief SETUP message bearer capability field code value */
+		int capability;
+
+		/*! \brief TRUE if call made in digital HDLC mode */
+		int hdlc;
+	} redial;
+
+	/*! \brief Dialplan location to indicate User-B free and User-A is free */
+	struct misdn_cc_notify remote_user_free;
+
+	/*! \brief Dialplan location to indicate User-B free and User-A is busy */
+	struct misdn_cc_notify b_free;
+};
+
+/*! \brief Call completion record guardian */
+ast_mutex_t misdn_cc_record_lock;
+/*! \brief mISDN call completion record database */
+static struct misdn_cc_record *misdn_cc_records;
+/*! \brief Next call completion record ID to use */
+static __u16 misdn_cc_record_id;
+/*! \brief Next invoke ID to use */
+static __s16 misdn_invoke_id;
+
+static const char misdn_no_response_from_network[] = "No response from network";
+static const char misdn_cc_record_not_found[] = "Call completion record not found";
+
+/* mISDN channel variable names */
+#define MISDN_CC_PEER_ID	"MISDN_CC_PEER_ID"
+#define MISDN_CC_RECORD_ID	"MISDN_CC_RECORD_ID"
+#define MISDN_CC_STATUS		"MISDN_CC_STATUS"
+#define MISDN_ERROR_MSG		"MISDN_ERROR_MSG"
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
 
 ast_mutex_t release_lock;
 
@@ -312,6 +447,14 @@
 	 */
 	struct misdn_bchannel *bc;
 
+#if defined(AST_MISDN_ENHANCEMENTS)
+	/*! \brief Asterisk channel that initiated this call (may be NULL) */
+	struct ast_channel *peer;
+
+	/*! \brief Associated call completion record ID (-1 if not associated) */
+	int record_id;
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
 	/*!
 	 * \brief HOLDED channel information
 	 */
@@ -525,6 +668,10 @@
 static int stop_bc_tones(struct chan_list *cl);
 static void release_chan(struct misdn_bchannel *bc);
 
+#if defined(AST_MISDN_ENHANCEMENTS)
+static const char misdn_command_name[] = "misdn_command";
+static int misdn_command_exec(struct ast_channel *chan, void *data);
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
 static int misdn_check_l2l1(struct ast_channel *chan, void *data);
 static int misdn_set_opt_exec(struct ast_channel *chan, void *data);
 static int misdn_facility_exec(struct ast_channel *chan, void *data);
@@ -570,6 +717,571 @@
   
 	return NULL;
 }
+
+
+
+
+/* ******************************************************************* */
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Get the requested peer link.
+ *
+ * \param peer_id Peer link id to obtain
+ *
+ * \retval peer_channel on success.
+ * \retval NULL on error.
+ */
+static struct ast_channel *misdn_peer_link_get(unsigned peer_id)
+{
+	struct ast_channel *peer;
+
+	if (peer_id < ARRAY_LEN(misdn_peers)) {
+		ast_mutex_lock(&misdn_peer_link_lock);
+		peer = misdn_peers[peer_id].peer;
+		ast_mutex_unlock(&misdn_peer_link_lock);
+	} else {
+		peer = NULL;
+	}
+
+	return peer;
+}	/* end misdn_peer_link_get() */
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+
+
+
+/* ******************************************************************* */
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Allocate a new peer link association id.
+ *
+ * \param chan Asterisk channel that is a potential peer.
+ *
+ * \retval peer_link_id on success.
+ * \retval -1 on error.
+ */
+static int misdn_peer_link_new(struct ast_channel *chan)
+{
+	int index;
+	int peer_id;
+	time_t now;
+
+	ast_mutex_lock(&misdn_peer_link_lock);
+
+	/* Remove all old entries */
+	now = time(NULL);
+	for (index = 0; index < ARRAY_LEN(misdn_peers); ++index) {
+		if (misdn_peers[index].peer
+			&& MISDN_PEER_AGE_MAX < now - misdn_peers[index].time_created) {
+			misdn_peers[index].peer = NULL;
+		}
+	}	/* end for */
+
+	/* Allocate a peer_id */
+	peer_id = -1;
+	for (index = 0; index < ARRAY_LEN(misdn_peers); ++index) {
+		if (!misdn_peers[index].peer) {
+			misdn_peers[index].peer = chan;
+			misdn_peers[index].time_created = time(NULL);
+			peer_id = index;
+			break;
+		}
+	}	/* end for */
+	ast_mutex_unlock(&misdn_peer_link_lock);
+
+	return peer_id;
+}	/* end misdn_peer_link_new() */
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+
+
+
+/* ******************************************************************* */
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Find the call completion record given the record id.
+ *
+ * \param record_id
+ *
+ * \retval pointer to found call completion record
+ * \retval NULL if not found
+ *
+ * \note Assumes the misdn_cc_record_lock is already obtained.
+ */
+static struct misdn_cc_record *misdn_cc_find_by_id(int record_id)
+{
+	struct misdn_cc_record *current;
+
+	for (current = misdn_cc_records; current; current = current->next) {
+		if (current->record_id == record_id) {
+			/* Found the record */
+			break;
+		}
+	}	/* end for */
+
+	return current;
+}	/* end misdn_cc_find_by_id() */
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+
+
+
+/* ******************************************************************* */
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Find the call completion record given the port and call linkage id.
+ *
+ * \param port Logical port number
+ * \param linkage_id Call linkage ID number from switch.
+ *
+ * \retval pointer to found call completion record
+ * \retval NULL if not found
+ *
+ * \note Assumes the misdn_cc_record_lock is already obtained.
+ */
+static struct misdn_cc_record *misdn_cc_find_by_linkage(int port, int linkage_id)
+{
+	struct misdn_cc_record *current;
+
+	for (current = misdn_cc_records; current; current = current->next) {
+		if (current->port == port && current->linkage_id == linkage_id) {
+			/* Found the record */
+			break;
+		}
+	}	/* end for */
+
+	return current;
+}	/* end misdn_cc_find_by_linkage() */
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+
+
+
+/* ******************************************************************* */
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Find the call completion record given the port and outstanding invocation id.
+ *
+ * \param port Logical port number
+ * \param invoke_id Outstanding message invocation ID number.
+ *
+ * \retval pointer to found call completion record
+ * \retval NULL if not found
+ *
+ * \note Assumes the misdn_cc_record_lock is already obtained.
+ */
+static struct misdn_cc_record *misdn_cc_find_by_invoke(int port, int invoke_id)
+{
+	struct misdn_cc_record *current;
+
+	for (current = misdn_cc_records; current; current = current->next) {
+		if (current->outstanding_message
+			&& current->invoke_id == invoke_id
+			&& current->port == port) {
+			/* Found the record */
+			break;
+		}
+	}	/* end for */
+
+	return current;
+}	/* end misdn_cc_find_by_invoke() */
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+
+
+
+/* ******************************************************************* */
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Find the call completion record given the port and CCBS reference id.
+ *
+ * \param port Logical port number
+ * \param reference_id CCBS reference ID number from switch.
+ *
+ * \retval pointer to found call completion record
+ * \retval NULL if not found
+ *
+ * \note Assumes the misdn_cc_record_lock is already obtained.
+ */
+static struct misdn_cc_record *misdn_cc_find_by_reference(int port, int reference_id)
+{
+	struct misdn_cc_record *current;
+
+	for (current = misdn_cc_records; current; current = current->next) {
+		if (current->activated
+			&& current->port == port
+			&& current->reference_id == reference_id) {
+			/* Found the record */
+			break;
+		}
+	}	/* end for */
+
+	return current;
+}	/* end misdn_cc_find_by_reference() */
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+
+
+
+/* ******************************************************************* */
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Delete the given call completion record
+ *
+ * \param doomed Call completion record to destroy
+ *
+ * \return Nothing
+ *
+ * \note Assumes the misdn_cc_record_lock is already obtained.
+ */
+static void misdn_cc_delete(struct misdn_cc_record *doomed)
+{
+	struct misdn_cc_record *current;
+	struct misdn_cc_record **prev;
+
+	prev = &misdn_cc_records;
+	for (current = misdn_cc_records; current; current = current->next) {
+		if (current == doomed) {
+			*prev = doomed->next;
+			ast_free(doomed);
+			return;
+		}
+		prev = &current->next;
+	}	/* end for */
+
+	/* The doomed node is not in the call completion database */
+}	/* end misdn_cc_delete() */
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+
+
+
+/* ******************************************************************* */
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Delete all old call completion records
+ *
+ * \return Nothing
+ *
+ * \note Assumes the misdn_cc_record_lock is already obtained.
+ */
+static void misdn_cc_remove_old(void)
+{
+	struct misdn_cc_record *current;
+	struct misdn_cc_record **prev;
+	time_t now;
+
+	now = time(NULL);
+	prev = &misdn_cc_records;
+	for (current = misdn_cc_records; current;) {
+		if (MISDN_CC_RECORD_AGE_MAX < now - current->time_created) {
+			/* Remove the old call completion record */
+			*prev = current->next;
+			ast_free(current);
+			current = *prev;
+		} else {
+			prev = &current->next;
+			current = current->next;
+		}
+	}	/* end for */
+}	/* end misdn_cc_remove_old() */
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+
+
+
+/* ******************************************************************* */
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Create a new call completion record
+ *
+ * \retval pointer to new call completion record
+ * \retval NULL if failed
+ *
+ * \note Assumes the misdn_cc_record_lock is already obtained.
+ */
+static struct misdn_cc_record *misdn_cc_new(void)
+{
+	struct misdn_cc_record *cc_record;
+	int record_id;
+	int first_id;
+
+	misdn_cc_remove_old();
+
+	cc_record = ast_calloc(1, sizeof(*cc_record));
+	if (cc_record) {
+		record_id = ++misdn_cc_record_id;
+		first_id = record_id;
+		while (misdn_cc_find_by_id(record_id)) {
+			record_id = ++misdn_cc_record_id;
+			if (record_id == first_id) {
+				/*
+				 * We have a resource leak.
+				 * We should never need to allocate 64k records.
+				 */
+				chan_misdn_log(0, 0, " --> ERROR Too many call completion records!\n");
+				ast_free(cc_record);
+				return NULL;
+			}
+		}	/* end while */
+
+		/* Initialize the new record */
+		cc_record->record_id = record_id;
+		cc_record->port = -1;/* Invalid port so it will never be found this way */
+		cc_record->party_a_free = 1;/* Default User-A as free */
+		cc_record->error_code = FacError_None;
+		cc_record->reject_code = FacReject_None;
+		cc_record->time_created = time(NULL);
+
+		/* Insert the new record into the database */
+		cc_record->next = misdn_cc_records;
+		misdn_cc_records = cc_record;
+	}
+	return cc_record;
+}	/* end misdn_cc_new() */
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+
+
+
+/* ******************************************************************* */
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Destroy the call completion record database
+ *
+ * \return Nothing
+ */
+static void misdn_cc_destroy(void)
+{
+	ast_mutex_destroy(&misdn_cc_record_lock);
+	while (misdn_cc_records) {
+		misdn_cc_delete(misdn_cc_records);
+	}	/* end while */
+}	/* end misdn_cc_destroy() */
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+
+
+
+/* ******************************************************************* */
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Initialize the call completion record database
+ *
+ * \return Nothing
+ */
+static void misdn_cc_init(void)
+{
+	misdn_cc_records = NULL;
+	misdn_cc_record_id = 0;
+	ast_mutex_init(&misdn_cc_record_lock);
+}	/* end misdn_cc_init() */
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+
+
+
+/* ******************************************************************* */
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Check the status of an outstanding invocation request.
+ *
+ * \param data Points to an integer containing the call completion record id.
+ *
+ * \retval 0 if got a response.
+ * \retval -1 if no response yet.
+ */
+static int misdn_cc_response_check(void *data)
+{
+	int not_responded;
+	struct misdn_cc_record *cc_record;
+
+	ast_mutex_lock(&misdn_cc_record_lock);
+	cc_record = misdn_cc_find_by_id(*(int *) data);
+	if (cc_record) {
+		if (cc_record->outstanding_message) {
+			not_responded = -1;
+		} else {
+			not_responded = 0;
+		}
+	} else {
+		/* No record so there is no response to check. */
+		not_responded = 0;
+	}
+	ast_mutex_unlock(&misdn_cc_record_lock);
+
+	return not_responded;
+}	/* end misdn_cc_response_check() */
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+
+
+
+/* ******************************************************************* */
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Wait for a response from the switch for an outstanding
+ * invocation request.
+ *
+ * \param chan Asterisk channel to operate upon.
+ * \param wait_seconds Number of seconds to wait
+ * \param record_id Call completion record ID.
+ *
+ * \return Nothing
+ */
+static void misdn_cc_response_wait(struct ast_channel *chan, int wait_seconds, int record_id)
+{
+	unsigned count;
+
+	for (count = 2 * MISDN_CC_REQUEST_WAIT_MAX; count--;) {
+		/* Sleep in 500 ms increments */
+		if (ast_safe_sleep_conditional(chan, 500, misdn_cc_response_check, &record_id) != 0) {
+			/* We got hung up or our response came in. */
+			break;
+		}
+	}	/* end for */
+}	/* end misdn_cc_response_wait() */
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+
+
+
+/* ******************************************************************* */
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert the mISDN reject code to a string
+ *
+ * \param code mISDN reject code.
+ *
+ * \return The mISDN reject code as a string
+ */
+static const char *misdn_to_str_reject_code(enum FacRejectCode code)
+{
+	static const struct {
+		enum FacRejectCode code;
+		char *name;
+	} arr[] = {
+/* *INDENT-OFF* */
+		{ FacReject_None,                           "No reject occurred" },
+		{ FacReject_Unknown,                        "Unknown reject code" },
+
+		{ FacReject_Gen_UnrecognizedComponent,      "General: Unrecognized Component" },
+		{ FacReject_Gen_MistypedComponent,          "General: Mistyped Component" },
+		{ FacReject_Gen_BadlyStructuredComponent,   "General: Badly Structured Component" },
+
+		{ FacReject_Inv_DuplicateInvocation,        "Invoke: Duplicate Invocation" },
+		{ FacReject_Inv_UnrecognizedOperation,      "Invoke: Unrecognized Operation" },
+		{ FacReject_Inv_MistypedArgument,           "Invoke: Mistyped Argument" },
+		{ FacReject_Inv_ResourceLimitation,         "Invoke: Resource Limitation" },
+		{ FacReject_Inv_InitiatorReleasing,         "Invoke: Initiator Releasing" },
+		{ FacReject_Inv_UnrecognizedLinkedID,       "Invoke: Unrecognized Linked ID" },
+		{ FacReject_Inv_LinkedResponseUnexpected,   "Invoke: Linked Response Unexpected" },
+		{ FacReject_Inv_UnexpectedChildOperation,   "Invoke: Unexpected Child Operation" },
+
+		{ FacReject_Res_UnrecognizedInvocation,     "Result: Unrecognized Invocation" },
+		{ FacReject_Res_ResultResponseUnexpected,   "Result: Result Response Unexpected" },
+		{ FacReject_Res_MistypedResult,             "Result: Mistyped Result" },
+
+		{ FacReject_Err_UnrecognizedInvocation,     "Error: Unrecognized Invocation" },
+		{ FacReject_Err_ErrorResponseUnexpected,    "Error: Error Response Unexpected" },
+		{ FacReject_Err_UnrecognizedError,          "Error: Unrecognized Error" },
+		{ FacReject_Err_UnexpectedError,            "Error: Unexpected Error" },
+		{ FacReject_Err_MistypedParameter,          "Error: Mistyped Parameter" },
+/* *INDENT-ON* */
+	};
+
+	unsigned index;
+
+	for (index = 0; index < ARRAY_LEN(arr); ++index) {
+		if (arr[index].code == code) {
+			return arr[index].name;
+		}
+	}	/* end for */
+
+	return "unknown";
+}	/* end misdn_to_str_reject_code() */
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+
+
+
+/* ******************************************************************* */
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert the mISDN error code to a string
+ *
+ * \param code mISDN error code.
+ *
+ * \return The mISDN error code as a string
+ */
+static const char *misdn_to_str_error_code(enum FacErrorCode code)
+{
+	static const struct {
+		enum FacErrorCode code;
+		char *name;
+	} arr[] = {
+/* *INDENT-OFF* */
+		{ FacError_None,                            "No error occurred" },
+		{ FacError_Unknown,                         "Unknown OID error code" },
+
+		{ FacError_Gen_NotSubscribed,               "General: Not Subscribed" },
+		{ FacError_Gen_NotAvailable,                "General: Not Available" },
+		{ FacError_Gen_NotImplemented,              "General: Not Implemented" },
+		{ FacError_Gen_InvalidServedUserNr,         "General: Invalid Served User Number" },
+		{ FacError_Gen_InvalidCallState,            "General: Invalid Call State" },
+		{ FacError_Gen_BasicServiceNotProvided,     "General: Basic Service Not Provided" },
+		{ FacError_Gen_NotIncomingCall,             "General: Not Incoming Call" },
+		{ FacError_Gen_SupplementaryServiceInteractionNotAllowed,"General: Supplementary Service Interaction Not Allowed" },
+		{ FacError_Gen_ResourceUnavailable,         "General: Resource Unavailable" },
+
+		{ FacError_Div_InvalidDivertedToNr,         "Diversion: Invalid Diverted To Number" },
+		{ FacError_Div_SpecialServiceNr,            "Diversion: Special Service Number" },
+		{ FacError_Div_DiversionToServedUserNr,     "Diversion: Diversion To Served User Number" },
+		{ FacError_Div_IncomingCallAccepted,        "Diversion: Incoming Call Accepted" },
+		{ FacError_Div_NumberOfDiversionsExceeded,  "Diversion: Number Of Diversions Exceeded" },
+		{ FacError_Div_NotActivated,                "Diversion: Not Activated" },
+		{ FacError_Div_RequestAlreadyAccepted,      "Diversion: Request Already Accepted" },
+
+		{ FacError_AOC_NoChargingInfoAvailable,     "AOC: No Charging Info Available" },
+
+		{ FacError_CCBS_InvalidCallLinkageID,       "CCBS: Invalid Call Linkage ID" },
+		{ FacError_CCBS_InvalidCCBSReference,       "CCBS: Invalid CCBS Reference" },
+		{ FacError_CCBS_LongTermDenial,             "CCBS: Long Term Denial" },
+		{ FacError_CCBS_ShortTermDenial,            "CCBS: Short Term Denial" },
+		{ FacError_CCBS_IsAlreadyActivated,         "CCBS: Is Already Activated" },
+		{ FacError_CCBS_AlreadyAccepted,            "CCBS: Already Accepted" },
+		{ FacError_CCBS_OutgoingCCBSQueueFull,      "CCBS: Outgoing CCBS Queue Full" },
+		{ FacError_CCBS_CallFailureReasonNotBusy,   "CCBS: Call Failure Reason Not Busy" },
+		{ FacError_CCBS_NotReadyForCall,            "CCBS: Not Ready For Call" },
+/* *INDENT-ON* */
+	};
+
+	unsigned index;
+
+	for (index = 0; index < ARRAY_LEN(arr); ++index) {
+		if (arr[index].code == code) {
+			return arr[index].name;
+		}
+	}	/* end for */
+
+	return "unknown";
+}	/* end misdn_to_str_error_code() */
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
 
 
 
@@ -1190,20 +1902,132 @@
 }
 
 
-static void print_facility(struct FacParm *fac, struct misdn_bchannel *bc)
-{
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+static const char Level_Spacing[] = "          ";/* Work for up to 10 levels */
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+/* ******************************************************************* */
+#if defined(AST_MISDN_ENHANCEMENTS)
+static void print_facility_partynumber(unsigned Level, const struct FacPartyNumber *Party, const struct misdn_bchannel *bc)
+{
+	if (Party->LengthOfNumber) {
+		const char *Spacing;
+
+		Spacing = &Level_Spacing[sizeof(Level_Spacing) - 1 - Level];
+		chan_misdn_log(1, bc->port, " -->%s PartyNumber: Type:%d\n",
+			Spacing, Party->Type);
+		switch (Party->Type) {
+		case 0: /* Unknown PartyNumber */
+			chan_misdn_log(1, bc->port, " -->%s  Unknown %s\n",
+				Spacing, Party->Number);
+			break;
+		case 1: /* Public PartyNumber */
+			chan_misdn_log(1, bc->port, " -->%s  Public TON:%d %s\n",
+				Spacing, Party->TypeOfNumber, Party->Number);
+			break;
+		case 2: /* NSAP encoded PartyNumber */
+			chan_misdn_log(1, bc->port, " -->%s  NSAP %s\n",
+				Spacing, Party->Number);
+			break;
+		case 3: /* Data PartyNumber (Not used) */
+			chan_misdn_log(1, bc->port, " -->%s  Data %s\n",
+				Spacing, Party->Number);
+			break;
+		case 4: /* Telex PartyNumber (Not used) */
+			chan_misdn_log(1, bc->port, " -->%s  Telex %s\n",
+				Spacing, Party->Number);
+			break;
+		case 5: /* Private PartyNumber */
+			chan_misdn_log(1, bc->port, " -->%s  Private TON:%d %s\n",
+				Spacing, Party->TypeOfNumber, Party->Number);
+			break;
+		case 8: /* National Standard PartyNumber (Not used) */
+			chan_misdn_log(1, bc->port, " -->%s  National %s\n",
+				Spacing, Party->Number);
+			break;
+		default:
+			break;
+		}	/* end switch */
+	}
+}	/* end print_facility_partynumber() */
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+
+
+
+/* ******************************************************************* */
+#if defined(AST_MISDN_ENHANCEMENTS)
+static void print_facility_subaddress(unsigned Level, const struct FacPartySubaddress *Subaddress, const struct misdn_bchannel *bc)
+{
+	if (Subaddress->Length) {
+		const char *Spacing;
+
+		Spacing = &Level_Spacing[sizeof(Level_Spacing) - 1 - Level];
+		chan_misdn_log(1, bc->port, " -->%s Subaddress: Type:%d\n",
+			Spacing, Subaddress->Type);
+		switch (Subaddress->Type) {
+		case 0: /* UserSpecified */
+			if (Subaddress->u.UserSpecified.OddCountPresent) {
+				chan_misdn_log(1, bc->port, " -->%s  User BCD OddCount:%d NumOctets:%d\n",
+					Spacing, Subaddress->u.UserSpecified.OddCount, Subaddress->Length);
+			} else {
+				chan_misdn_log(1, bc->port, " -->%s  User %s\n",
+					Spacing, Subaddress->u.UserSpecified.Information);
+			}
+			break;
+		case 1: /* NSAP */
+			chan_misdn_log(1, bc->port, " -->%s  NSAP %s\n",
+				Spacing, Subaddress->u.Nsap);
+			break;
+		default:
+			break;
+		}	/* end switch */
+	}
+}	/* end print_facility_subaddress() */
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+
+
+
+/* ******************************************************************* */
+#if defined(AST_MISDN_ENHANCEMENTS)
+static void print_facility_address(unsigned Level, const struct FacAddress *Address, const struct misdn_bchannel *bc)
+{
+	print_facility_partynumber(Level, &Address->Party, bc);
+	print_facility_subaddress(Level, &Address->Subaddress, bc);
+}	/* end print_facility_address() */
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+
+
+
+/* ******************************************************************* */
+#if defined(AST_MISDN_ENHANCEMENTS)
+static void print_facility_callinformation(unsigned Level, const struct FacCallInformation *CallInfo, const struct misdn_bchannel *bc)
+{
+	const char *Spacing;
+
+	Spacing = &Level_Spacing[sizeof(Level_Spacing) - 1 - Level];
+	chan_misdn_log(1, bc->port, " -->%s CCBSReference:%d\n",
+		Spacing, CallInfo->CCBSReference);
+	chan_misdn_log(1, bc->port, " -->%s AddressOfB\n", Spacing);
+	print_facility_address(Level + 1, &CallInfo->AddressOfB, bc);
+	if (CallInfo->SubaddressOfA.Length) {
+		chan_misdn_log(1, bc->port, " -->%s SubaddressOfA\n", Spacing);
+		print_facility_subaddress(Level + 1, &CallInfo->SubaddressOfA, bc);
+	}
+}	/* end print_facility_callinformation() */
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
+
+static void print_facility(const struct FacParm *fac, const const struct misdn_bchannel *bc)
+{
+#if defined(AST_MISDN_ENHANCEMENTS)
+	unsigned Index;
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
+
 	switch (fac->Function) {
-#ifdef HAVE_MISDN_FAC_RESULT
-	case Fac_RESULT:
-		chan_misdn_log(0, bc->port," --> Received RESULT Operation\n");
-		break;
-#endif
-#ifdef HAVE_MISDN_FAC_ERROR
-	case Fac_ERROR:
-		chan_misdn_log(0, bc->port," --> Received Error Operation\n");
-		chan_misdn_log(0, bc->port," --> Value:%d Error:%s\n",fac->u.ERROR.errorValue, fac->u.ERROR.error);
-		break;
-#endif
 	case Fac_CD:
 		chan_misdn_log(1,bc->port," --> calldeflect to: %s, presentable: %s\n", fac->u.CDeflection.DeflectedToNumber,
 			fac->u.CDeflection.PresentationAllowed ? "yes" : "no");
@@ -1234,11 +2058,207 @@
 			chan_misdn_log(1,bc->port," --> AOCD charging unit: recordedUnits:%d typeOfChargingInfo:%s\n",
 				fac->u.AOCDchu.recordedUnits, (fac->u.AOCDchu.typeOfChargingInfo == 0) ? "subTotal" : "total");
 		break;
+#if defined(AST_MISDN_ENHANCEMENTS)
+	case Fac_ERROR:
+		chan_misdn_log(1, bc->port, " --> ERROR: InvokeID:%d, Code:0x%02x\n",
+			fac->u.ERROR.invokeId, fac->u.ERROR.errorValue);
+		break;
+	case Fac_RESULT:
+		chan_misdn_log(1, bc->port, " --> RESULT: InvokeID:%d\n",
+			fac->u.RESULT.InvokeID);
+		break;
+	case Fac_REJECT:
+		if (fac->u.REJECT.InvokeIDPresent) {
+			chan_misdn_log(1, bc->port, " --> REJECT: InvokeID:%d, Code:0x%02x\n",
+				fac->u.REJECT.InvokeID, fac->u.REJECT.Code);
+		} else {
+			chan_misdn_log(1, bc->port, " --> REJECT: Code:0x%02x\n",
+				fac->u.REJECT.Code);
+		}
+		break;
+	case Fac_StatusRequest:
+		chan_misdn_log(1, bc->port, " --> StatusRequest: InvokeID:%d\n",
+			fac->u.StatusRequest.InvokeID);
+		switch (fac->u.StatusRequest.ComponentType) {
+		case FacComponent_Invoke:
+			chan_misdn_log(1, bc->port, " -->  Invoke: Compatibility:%d\n",
+				fac->u.StatusRequest.Component.Invoke.CompatibilityMode);
+			break;
+		case FacComponent_Result:
+			chan_misdn_log(1, bc->port, " -->  Result: Status:%d\n",
+				fac->u.StatusRequest.Component.Result.Status);
+			break;
+		default:
+			break;
+		}	/* end switch */
+		break;
+	case Fac_CallInfoRetain:
+		chan_misdn_log(1, bc->port, " --> CallInfoRetain: InvokeID:%d, LinkageID:%d\n",
+			fac->u.CallInfoRetain.InvokeID, fac->u.CallInfoRetain.CallLinkageID);
+		break;
+	case Fac_CCBSDeactivate:
+		chan_misdn_log(1, bc->port, " --> CCBSDeactivate: InvokeID:%d\n",
+			fac->u.CCBSDeactivate.InvokeID);
+		switch (fac->u.CCBSDeactivate.ComponentType) {
+		case FacComponent_Invoke:
+			chan_misdn_log(1, bc->port, " -->  Invoke: CCBSReference:%d\n",
+				fac->u.CCBSDeactivate.Component.Invoke.CCBSReference);
+			break;
+		case FacComponent_Result:
+			chan_misdn_log(1, bc->port, " -->  Result\n");
+			break;
+		default:
+			break;
+		}	/* end switch */
+		break;
+	case Fac_CCBSErase:
+		chan_misdn_log(1, bc->port, " --> CCBSErase: InvokeID:%d, CCBSReference:%d RecallMode:%d, Reason:%d\n",
+			fac->u.CCBSErase.InvokeID, fac->u.CCBSErase.CCBSReference,
+			fac->u.CCBSErase.RecallMode, fac->u.CCBSErase.Reason);
+		chan_misdn_log(1, bc->port, " -->  AddressOfB\n");
+		print_facility_address(2, &fac->u.CCBSErase.AddressOfB, bc);
+		break;
+	case Fac_CCBSRemoteUserFree:
+		chan_misdn_log(1, bc->port, " --> CCBSRemoteUserFree: InvokeID:%d, CCBSReference:%d RecallMode:%d\n",
+			fac->u.CCBSRemoteUserFree.InvokeID, fac->u.CCBSRemoteUserFree.CCBSReference,
+			fac->u.CCBSRemoteUserFree.RecallMode);
+		chan_misdn_log(1, bc->port, " -->  AddressOfB\n");
+		print_facility_address(2, &fac->u.CCBSRemoteUserFree.AddressOfB, bc);
+		break;
+	case Fac_CCBSCall:
+		chan_misdn_log(1, bc->port, " --> CCBSCall: InvokeID:%d, CCBSReference:%d\n",
+			fac->u.CCBSCall.InvokeID, fac->u.CCBSCall.CCBSReference);
+		break;
+	case Fac_CCBSStatusRequest:
+		chan_misdn_log(1, bc->port, " --> CCBSStatusRequest: InvokeID:%d\n",
+			fac->u.CCBSStatusRequest.InvokeID);
+		switch (fac->u.CCBSStatusRequest.ComponentType) {
+		case FacComponent_Invoke:
+			chan_misdn_log(1, bc->port, " -->  Invoke: CCBSReference:%d RecallMode:%d\n",
+				fac->u.CCBSStatusRequest.Component.Invoke.CCBSReference,
+				fac->u.CCBSStatusRequest.Component.Invoke.RecallMode);
+			break;
+		case FacComponent_Result:
+			chan_misdn_log(1, bc->port, " -->  Result: Free:%d\n",
+				fac->u.CCBSStatusRequest.Component.Result.Free);
+			break;
+		default:
+			break;
+		}	/* end switch */
+		break;
+	case Fac_CCBSBFree:
+		chan_misdn_log(1, bc->port, " --> CCBSBFree: InvokeID:%d, CCBSReference:%d RecallMode:%d\n",
+			fac->u.CCBSBFree.InvokeID, fac->u.CCBSBFree.CCBSReference,
+			fac->u.CCBSBFree.RecallMode);
+		chan_misdn_log(1, bc->port, " -->  AddressOfB\n");
+		print_facility_address(2, &fac->u.CCBSBFree.AddressOfB, bc);
+		break;
+	case Fac_EraseCallLinkageID:
+		chan_misdn_log(1, bc->port, " --> EraseCallLinkageID: InvokeID:%d, LinkageID:%d\n",
+			fac->u.EraseCallLinkageID.InvokeID, fac->u.EraseCallLinkageID.CallLinkageID);
+		break;
+	case Fac_CCBSStopAlerting:
+		chan_misdn_log(1, bc->port, " --> CCBSStopAlerting: InvokeID:%d, CCBSReference:%d\n",
+			fac->u.CCBSStopAlerting.InvokeID, fac->u.CCBSStopAlerting.CCBSReference);
+		break;
+	case Fac_CCBSRequest:
+		chan_misdn_log(1, bc->port, " --> CCBSRequest: InvokeID:%d\n",
+			fac->u.CCBSRequest.InvokeID);
+		switch (fac->u.CCBSRequest.ComponentType) {
+		case FacComponent_Invoke:
+			chan_misdn_log(1, bc->port, " -->  Invoke: LinkageID:%d\n",
+				fac->u.CCBSRequest.Component.Invoke.CallLinkageID);
+			break;
+		case FacComponent_Result:
+			chan_misdn_log(1, bc->port, " -->  Result: CCBSReference:%d RecallMode:%d\n",
+				fac->u.CCBSRequest.Component.Result.CCBSReference,
+				fac->u.CCBSRequest.Component.Result.RecallMode);
+			break;
+		default:
+			break;
+		}	/* end switch */
+		break;
+	case Fac_CCBSInterrogate:
+		chan_misdn_log(1, bc->port, " --> CCBSInterrogate: InvokeID:%d\n",
+			fac->u.CCBSInterrogate.InvokeID);
+		switch (fac->u.CCBSInterrogate.ComponentType) {
+		case FacComponent_Invoke:
+			chan_misdn_log(1, bc->port, " -->  Invoke\n");
+			if (fac->u.CCBSInterrogate.Component.Invoke.CCBSReferencePresent) {
+				chan_misdn_log(1, bc->port, " -->   CCBSReference:%d\n",
+					fac->u.CCBSInterrogate.Component.Invoke.CCBSReference);
+			}
+			if (fac->u.CCBSInterrogate.Component.Invoke.AParty.LengthOfNumber) {
+				chan_misdn_log(1, bc->port, " -->   AParty\n");
+				print_facility_partynumber(3, &fac->u.CCBSInterrogate.Component.Invoke.AParty, bc);
+			}
+			break;
+		case FacComponent_Result:
+			chan_misdn_log(1, bc->port, " -->  Result: RecallMode:%d\n",
+				fac->u.CCBSInterrogate.Component.Result.RecallMode);
+			if (fac->u.CCBSInterrogate.Component.Result.NumRecords) {
+				for (Index = 0; Index < fac->u.CCBSInterrogate.Component.Result.NumRecords; ++Index) {
+					chan_misdn_log(1, bc->port, " -->   CallDetails[%d]:\n", Index);
+					print_facility_callinformation(3, &fac->u.CCBSInterrogate.Component.Result.CallDetails[Index], bc);
+				}	/* end for */
+			}
+			break;
+		default:
+			break;
+		}	/* end switch */
+		break;
+	case Fac_CCNRRequest:
+		chan_misdn_log(1, bc->port, " --> CCNRRequest: InvokeID:%d\n",
+			fac->u.CCNRRequest.InvokeID);
+		switch (fac->u.CCNRRequest.ComponentType) {
+		case FacComponent_Invoke:
+			chan_misdn_log(1, bc->port, " -->  Invoke: LinkageID:%d\n",
+				fac->u.CCNRRequest.Component.Invoke.CallLinkageID);
+			break;
+		case FacComponent_Result:
+			chan_misdn_log(1, bc->port, " -->  Result: CCBSReference:%d RecallMode:%d\n",
+				fac->u.CCNRRequest.Component.Result.CCBSReference,
+				fac->u.CCNRRequest.Component.Result.RecallMode);
+			break;
+		default:
+			break;
+		}	/* end switch */
+		break;
+	case Fac_CCNRInterrogate:
+		chan_misdn_log(1, bc->port, " --> CCNRInterrogate: InvokeID:%d\n",
+			fac->u.CCNRInterrogate.InvokeID);
+		switch (fac->u.CCNRInterrogate.ComponentType) {
+		case FacComponent_Invoke:
+			chan_misdn_log(1, bc->port, " -->  Invoke\n");
+			if (fac->u.CCNRInterrogate.Component.Invoke.CCBSReferencePresent) {
+				chan_misdn_log(1, bc->port, " -->   CCBSReference:%d\n",
+					fac->u.CCNRInterrogate.Component.Invoke.CCBSReference);
+			}
+			if (fac->u.CCNRInterrogate.Component.Invoke.AParty.LengthOfNumber) {
+				chan_misdn_log(1, bc->port, " -->   AParty\n");
+				print_facility_partynumber(3, &fac->u.CCNRInterrogate.Component.Invoke.AParty, bc);
+			}
+			break;
+		case FacComponent_Result:
+			chan_misdn_log(1, bc->port, " -->  Result: RecallMode:%d\n",
+				fac->u.CCNRInterrogate.Component.Result.RecallMode);
+			if (fac->u.CCNRInterrogate.Component.Result.NumRecords) {
+				for (Index = 0; Index < fac->u.CCNRInterrogate.Component.Result.NumRecords; ++Index) {
+					chan_misdn_log(1, bc->port, " -->   CallDetails[%d]:\n", Index);
+					print_facility_callinformation(3, &fac->u.CCNRInterrogate.Component.Result.CallDetails[Index], bc);
+				}	/* end for */
+			}
+			break;
+		default:
+			break;
+		}	/* end switch */
+		break;
+#endif	/* defined(AST_MISDN_ENHANCEMENTS) */
 	case Fac_None:
 	default:
 		chan_misdn_log(1,bc->port," --> unknown facility\n");
 		break;
-	}
+	}	/* end switch */
 }
 
 static void print_bearer(struct misdn_bchannel *bc) 
@@ -2197,6 +3217,300 @@
 	return CLI_SUCCESS;
 }
 
+#if defined(AST_MISDN_ENHANCEMENTS) && defined(CCBS_TEST_MESSAGES)
+static const struct FacParm Fac_Msgs[] = {
+	[0].Function = Fac_ERROR,
+	[0].u.ERROR.invokeId = 8,
+	[0].u.ERROR.errorValue = FacError_CCBS_AlreadyAccepted,
+
+	[1].Function = Fac_RESULT,
+	[1].u.RESULT.InvokeID = 9,
+
+	[2].Function = Fac_REJECT,
+	[2].u.REJECT.Code = FacReject_Gen_BadlyStructuredComponent,
+
+	[3].Function = Fac_REJECT,
+	[3].u.REJECT.InvokeIDPresent = 1,
+	[3].u.REJECT.InvokeID = 10,
+	[3].u.REJECT.Code = FacReject_Inv_InitiatorReleasing,
+
+	[4].Function = Fac_REJECT,
+	[4].u.REJECT.InvokeIDPresent = 1,
+	[4].u.REJECT.InvokeID = 11,
+	[4].u.REJECT.Code = FacReject_Res_MistypedResult,
+
+	[5].Function = Fac_REJECT,
+	[5].u.REJECT.InvokeIDPresent = 1,
+	[5].u.REJECT.InvokeID = 12,
+	[5].u.REJECT.Code = FacReject_Err_ErrorResponseUnexpected,
+
+	[6].Function = Fac_StatusRequest,
+	[6].u.StatusRequest.InvokeID = 13,
+	[6].u.StatusRequest.ComponentType = FacComponent_Invoke,
+	[6].u.StatusRequest.Component.Invoke.Q931ie.Bc.Length = 2,
+	[6].u.StatusRequest.Component.Invoke.Q931ie.Bc.Contents = "AB",
+	[6].u.StatusRequest.Component.Invoke.Q931ie.Llc.Length = 3,
+	[6].u.StatusRequest.Component.Invoke.Q931ie.Llc.Contents = "CDE",
+	[6].u.StatusRequest.Component.Invoke.Q931ie.Hlc.Length = 4,
+	[6].u.StatusRequest.Component.Invoke.Q931ie.Hlc.Contents = "FGHI",
+	[6].u.StatusRequest.Component.Invoke.CompatibilityMode = 1,
+
+	[7].Function = Fac_StatusRequest,
+	[7].u.StatusRequest.InvokeID = 14,
+	[7].u.StatusRequest.ComponentType = FacComponent_Result,
+	[7].u.StatusRequest.Component.Result.Status = 2,
+
+	[8].Function = Fac_CallInfoRetain,
+	[8].u.CallInfoRetain.InvokeID = 15,
+	[8].u.CallInfoRetain.CallLinkageID = 115,
+
+	[9].Function = Fac_EraseCallLinkageID,
+	[9].u.EraseCallLinkageID.InvokeID = 16,
+	[9].u.EraseCallLinkageID.CallLinkageID = 105,
+
+	[10].Function = Fac_CCBSDeactivate,
+	[10].u.CCBSDeactivate.InvokeID = 17,
+	[10].u.CCBSDeactivate.ComponentType = FacComponent_Invoke,
+	[10].u.CCBSDeactivate.Component.Invoke.CCBSReference = 2,
+
+	[11].Function = Fac_CCBSDeactivate,
+	[11].u.CCBSDeactivate.InvokeID = 18,
+	[11].u.CCBSDeactivate.ComponentType = FacComponent_Result,
+
+	[12].Function = Fac_CCBSErase,
+	[12].u.CCBSErase.InvokeID = 19,
+	[12].u.CCBSErase.Q931ie.Bc.Length = 2,
+	[12].u.CCBSErase.Q931ie.Bc.Contents = "JK",
+	[12].u.CCBSErase.AddressOfB.Party.Type = 0,
+	[12].u.CCBSErase.AddressOfB.Party.LengthOfNumber = 5,
+	[12].u.CCBSErase.AddressOfB.Party.Number = "33403",
+	[12].u.CCBSErase.AddressOfB.Subaddress.Type = 0,
+	[12].u.CCBSErase.AddressOfB.Subaddress.Length = 4,
+	[12].u.CCBSErase.AddressOfB.Subaddress.u.UserSpecified.Information = "3748",
+	[12].u.CCBSErase.RecallMode = 1,
+	[12].u.CCBSErase.CCBSReference = 102,
+	[12].u.CCBSErase.Reason = 3,
+
+	[13].Function = Fac_CCBSErase,
+	[13].u.CCBSErase.InvokeID = 20,

[... 2339 lines stripped ...]



More information about the asterisk-commits mailing list