[asterisk-commits] rmudgett: trunk r189735 - in /trunk: ./ channels/ channels/misdn/ configs/
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Tue Apr 21 12:44:05 CDT 2009
Author: rmudgett
Date: Tue Apr 21 12:44:01 2009
New Revision: 189735
URL: http://svn.digium.com/svn-view/asterisk?view=rev&rev=189735
Log:
Added CCBS/CCNR Party A support and enhanced COLP support.
This change adds the following features to chan_misdn:
* CCBS/CCNR Party A support for PTMP and PTP modes.
* Enhances COLP support for call diversion and explicit call transfer.
These enhanced features require a modified version of mISDN.
The latest modified mISDN v1.1.x based version is available at:
http://svn.digium.com/svn/thirdparty/mISDN/trunk
http://svn.digium.com/svn/thirdparty/mISDNuser/trunk
Taged versions of the modified mISDN code are available under:
http://svn.digium.com/svn/thirdparty/mISDN/tags
http://svn.digium.com/svn/thirdparty/mISDNuser/tags
Merged from team/rmudgett/misdn_facility branch.
Modified:
trunk/CHANGES
trunk/channels/chan_misdn.c
trunk/channels/misdn/chan_misdn_config.h
trunk/channels/misdn/ie.c
trunk/channels/misdn/isdn_lib.c
trunk/channels/misdn/isdn_lib.h
trunk/channels/misdn/isdn_lib_intern.h
trunk/channels/misdn/isdn_msg_parser.c
trunk/channels/misdn_config.c
trunk/configs/misdn.conf.sample
Modified: trunk/CHANGES
URL: http://svn.digium.com/svn-view/asterisk/trunk/CHANGES?view=diff&rev=189735&r1=189734&r2=189735
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Tue Apr 21 12:44:01 2009
@@ -61,7 +61,25 @@
subscriberprefix, and abbreviatedprefix in misdn.conf to prefix any
received number from the ISDN link if that number has the corresponding
Type-Of-Number.
-
+ * Added new dialplan application misdn_command which permits controlling
+ the CCBS/CCNR functionality.
+ * Added new dialplan function mISDN_CC which permits retrieval of various
+ values from an active call completion record.
+
+thirdparty mISDN enhancements
+-----------------------------
+mISDN has been modified by Digium, Inc. to greatly expand facility message
+support to allow:
+ * Enhanced COLP support for call diversion and transfer.
+ * CCBS/CCNR support.
+
+The latest modified mISDN v1.1.x based version is available at:
+http://svn.digium.com/svn/thirdparty/mISDN/trunk
+http://svn.digium.com/svn/thirdparty/mISDNuser/trunk
+
+Taged versions of the modified mISDN code are available under:
+http://svn.digium.com/svn/thirdparty/mISDN/tags
+http://svn.digium.com/svn/thirdparty/mISDNuser/tags
SIP channel driver (chan_sip) changes
-------------------------------------------
Modified: trunk/channels/chan_misdn.c
URL: http://svn.digium.com/svn-view/asterisk/trunk/channels/chan_misdn.c?view=diff&rev=189735&r1=189734&r2=189735
==============================================================================
--- trunk/channels/chan_misdn.c (original)
+++ trunk/channels/chan_misdn.c Tue Apr 21 12:44:01 2009
@@ -28,6 +28,26 @@
*
* \ingroup channel_drivers
*/
+
+/*!
+ * \note
+ * To use the CCBS/CCNR supplementary service feature and other
+ * supplementary services using FACILITY messages requires a
+ * modified version of mISDN.
+ *
+ * \note
+ * The latest modified mISDN v1.1.x based version is available at:
+ * http://svn.digium.com/svn/thirdparty/mISDN/trunk
+ * http://svn.digium.com/svn/thirdparty/mISDNuser/trunk
+ *
+ * \note
+ * Taged versions of the modified mISDN code are available under:
+ * http://svn.digium.com/svn/thirdparty/mISDN/tags
+ * http://svn.digium.com/svn/thirdparty/mISDNuser/tags
+ */
+
+/* Define to enable cli commands to generate canned CCBS messages. */
+// #define CCBS_TEST_MESSAGES 1
/*** MODULEINFO
<depend>isdnnet</depend>
@@ -48,6 +68,7 @@
#include <sys/file.h>
#include <semaphore.h>
#include <ctype.h>
+#include <time.h>
#include "asterisk/channel.h"
#include "asterisk/config.h"
@@ -109,6 +130,165 @@
static char *complete_show_config(struct ast_cli_args *a);
/* BEGIN: chan_misdn.h */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*
+ * 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 */
+
+/*!
+ * \brief Caller that initialized call completion services
+ *
+ * \details
+ * This data is the payload for a datastore that is put on the channel that
+ * initializes call completion services. This datastore is set to be inherited
+ * by the outbound mISDN channel. When one of these channels hangs up, the
+ * channel pointer will be set to NULL. That way, we can ensure that we do not
+ * touch this channel after it gets destroyed.
+ */
+struct misdn_cc_caller {
+ /*! \brief The channel that initialized call completion services */
+ struct ast_channel *chan;
+};
+
+struct misdn_cc_notify {
+ /*! \brief Dialplan: Notify extension priority */
+ int priority;
+
+ /*! \brief Dialplan: Notify extension context */
+ char context[AST_MAX_CONTEXT];
+
+ /*! \brief Dialplan: Notify extension number (User-A) */
+ char exten[AST_MAX_EXTENSION];
+};
+
+/*! \brief mISDN call completion record */
+struct misdn_cc_record {
+ /*! \brief Call completion record linked list */
+ AST_LIST_ENTRY(misdn_cc_record) list;
+
+ /*! \brief Time the record was created. */
+ time_t time_created;
+
+ /*! \brief MISDN_CC_RECORD_ID value */
+ long record_id;
+
+ /*!
+ * \brief Logical Layer 1 port associated with this
+ * call completion record
+ */
+ int port;
+
+ /*! \brief TRUE if point-to-point mode (CCBS-T/CCNR-T mode) */
+ int ptp;
+
+ /*! \brief Mode specific parameters */
+ union {
+ /*! \brief point-to-point specific parameters. */
+ struct {
+ /*!
+ * \brief Call-completion signaling link.
+ * NULL if signaling link not established.
+ */
+ struct misdn_bchannel *bc;
+
+ /*!
+ * \brief TRUE if we requested the request retention option
+ * to be enabled.
+ */
+ int requested_retention;
+
+ /*!
+ * \brief TRUE if the request retention option is enabled.
+ */
+ int retention_enabled;
+ } ptp;
+
+ /*! \brief point-to-multi-point specific parameters. */
+ struct {
+ /*! \brief CallLinkageID (valid when port determined) */
+ int linkage_id;
+
+ /*! \breif CCBSReference (valid when activated is TRUE) */
+ int reference_id;
+
+ /*! \brief globalRecall(0), specificRecall(1) */
+ int recall_mode;
+ } ptmp;
+ } mode;
+
+ /*! \brief TRUE if call completion activated */
+ int activated;
+
+ /*! \brief Outstanding message ID (valid when 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 TRUE if User-A is free
+ * \note PTMP - Used to answer CCBSStatusRequest.
+ * PTP - Determines how to respond to CCBS_T_RemoteUserFree.
+ */
+ 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;
+
+ /*!
+ * \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 The BC, HLC (optional) and LLC (optional) contents from the SETUP message. */
+ struct Q931_Bc_Hlc_Llc setup_bc_hlc_llc;
+
+ /*! \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 mISDN call completion record database */
+static AST_LIST_HEAD_STATIC(misdn_cc_records_db, misdn_cc_record);
+/*! \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_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;
@@ -308,6 +488,16 @@
*/
struct misdn_bchannel *bc;
+#if defined(AST_MISDN_ENHANCEMENTS)
+ /*!
+ * \brief Peer channel for which call completion was initialized.
+ */
+ struct misdn_cc_caller *peer;
+
+ /*! \brief Associated call completion record ID (-1 if not associated) */
+ long record_id;
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
+
/*!
* \brief HOLDED channel information
*/
@@ -472,7 +662,6 @@
static int pbx_start_chan(struct chan_list *ch);
#define MISDN_ASTERISK_TECH_PVT(ast) ast->tech_pvt
-#define MISDN_ASTERISK_PVT(ast) 1
#include "asterisk/strings.h"
@@ -518,6 +707,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);
@@ -565,6 +758,1027 @@
return NULL;
}
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Destroy the misdn_cc_ds_info datastore payload
+ *
+ * \param[in] data the datastore payload, a reference to an misdn_cc_caller
+ *
+ * \details
+ * Since the payload is a reference to an astobj2 object, we just decrement its
+ * reference count. Before doing so, we NULL out the channel pointer inside of
+ * the misdn_cc_caller instance. This function will be called in one of two
+ * cases. In both cases, we no longer need the channel pointer:
+ *
+ * - The original channel that initialized call completion services, the same
+ * channel that is stored here, has been destroyed early. This could happen
+ * if it transferred the mISDN channel, for example.
+ *
+ * - The mISDN channel that had this datastore inherited on to it is now being
+ * destroyed. If this is the case, then the call completion events have
+ * already occurred and the appropriate channel variables have already been
+ * set on the original channel that requested call completion services.
+ *
+ * \return Nothing
+ */
+static void misdn_cc_ds_destroy(void *data)
+{
+ struct misdn_cc_caller *cc_caller = data;
+
+ ao2_lock(cc_caller);
+ cc_caller->chan = NULL;
+ ao2_unlock(cc_caller);
+
+ ao2_ref(cc_caller, -1);
+}
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Duplicate the misdn_cc_ds_info datastore payload
+ *
+ * \param[in] data the datastore payload, a reference to an misdn_cc_caller
+ *
+ * \details
+ * All we need to do is bump the reference count and return the same instance.
+ *
+ * \return A reference to an instance of a misdn_cc_caller
+ */
+static void *misdn_cc_ds_duplicate(void *data)
+{
+ struct misdn_cc_caller *cc_caller = data;
+
+ ao2_ref(cc_caller, +1);
+
+ return cc_caller;
+}
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+static const struct ast_datastore_info misdn_cc_ds_info = {
+ .type = "misdn_cc",
+ .destroy = misdn_cc_ds_destroy,
+ .duplicate = misdn_cc_ds_duplicate,
+};
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Set a channel var on the peer channel for call completion services
+ *
+ * \param[in] peer The peer that initialized call completion services
+ * \param[in] var The variable name to set
+ * \param[in] value The variable value to set
+ *
+ * This function may be called from outside of the channel thread. It handles
+ * the fact that the peer channel may be hung up and destroyed at any time.
+ *
+ * \return nothing
+ */
+static void misdn_cc_set_peer_var(struct misdn_cc_caller *peer, const char *var,
+ const char *value)
+{
+ ao2_lock(peer);
+
+ /*! \todo XXX This nastiness can go away once ast_channel is ref counted! */
+ while (peer->chan && ast_channel_trylock(peer->chan)) {
+ ao2_unlock(peer);
+ sched_yield();
+ ao2_lock(peer);
+ }
+
+ if (peer->chan) {
+ pbx_builtin_setvar_helper(peer->chan, var, value);
+ ast_channel_unlock(peer->chan);
+ }
+
+ ao2_unlock(peer);
+}
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Get a reference to the CC caller if it exists
+ */
+static struct misdn_cc_caller *misdn_cc_caller_get(struct ast_channel *chan)
+{
+ struct ast_datastore *datastore;
+ struct misdn_cc_caller *cc_caller;
+
+ ast_channel_lock(chan);
+
+ if (!(datastore = ast_channel_datastore_find(chan, &misdn_cc_ds_info, NULL))) {
+ ast_channel_unlock(chan);
+ return NULL;
+ }
+
+ ao2_ref(datastore->data, +1);
+ cc_caller = datastore->data;
+
+ ast_channel_unlock(chan);
+
+ return cc_caller;
+}
+#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_records_db lock is already obtained.
+ */
+static struct misdn_cc_record *misdn_cc_find_by_id(long record_id)
+{
+ struct misdn_cc_record *current;
+
+ AST_LIST_TRAVERSE(&misdn_cc_records_db, current, list) {
+ if (current->record_id == record_id) {
+ /* Found the record */
+ break;
+ }
+ }
+
+ return current;
+}
+#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_records_db lock is already obtained.
+ */
+static struct misdn_cc_record *misdn_cc_find_by_linkage(int port, int linkage_id)
+{
+ struct misdn_cc_record *current;
+
+ AST_LIST_TRAVERSE(&misdn_cc_records_db, current, list) {
+ if (current->port == port
+ && !current->ptp
+ && current->mode.ptmp.linkage_id == linkage_id) {
+ /* Found the record */
+ break;
+ }
+ }
+
+ return current;
+}
+#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_records_db lock is already obtained.
+ */
+static struct misdn_cc_record *misdn_cc_find_by_invoke(int port, int invoke_id)
+{
+ struct misdn_cc_record *current;
+
+ AST_LIST_TRAVERSE(&misdn_cc_records_db, current, list) {
+ if (current->outstanding_message
+ && current->invoke_id == invoke_id
+ && current->port == port) {
+ /* Found the record */
+ break;
+ }
+ }
+
+ return current;
+}
+#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_records_db lock is already obtained.
+ */
+static struct misdn_cc_record *misdn_cc_find_by_reference(int port, int reference_id)
+{
+ struct misdn_cc_record *current;
+
+ AST_LIST_TRAVERSE(&misdn_cc_records_db, current, list) {
+ if (current->activated
+ && current->port == port
+ && !current->ptp
+ && current->mode.ptmp.reference_id == reference_id) {
+ /* Found the record */
+ break;
+ }
+ }
+
+ return current;
+}
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Find the call completion record given the B channel pointer
+ *
+ * \param bc B channel control structure pointer.
+ *
+ * \retval pointer to found call completion record
+ * \retval NULL if not found
+ *
+ * \note Assumes the misdn_cc_records_db lock is already obtained.
+ */
+static struct misdn_cc_record *misdn_cc_find_by_bc(const struct misdn_bchannel *bc)
+{
+ struct misdn_cc_record *current;
+
+ if (bc) {
+ AST_LIST_TRAVERSE(&misdn_cc_records_db, current, list) {
+ if (current->ptp
+ && current->mode.ptp.bc == bc) {
+ /* Found the record */
+ break;
+ }
+ }
+ } else {
+ current = NULL;
+ }
+
+ return current;
+}
+#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_records_db lock is already obtained.
+ */
+static void misdn_cc_delete(struct misdn_cc_record *doomed)
+{
+ struct misdn_cc_record *current;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&misdn_cc_records_db, current, list) {
+ if (current == doomed) {
+ AST_LIST_REMOVE_CURRENT(list);
+ ast_free(current);
+ return;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ /* The doomed node is not in the call completion database */
+}
+#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_records_db lock is already obtained.
+ */
+static void misdn_cc_remove_old(void)
+{
+ struct misdn_cc_record *current;
+ time_t now;
+
+ now = time(NULL);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&misdn_cc_records_db, current, list) {
+ if (MISDN_CC_RECORD_AGE_MAX < now - current->time_created) {
+ if (current->ptp && current->mode.ptp.bc) {
+ /* Close the old call-completion signaling link */
+ current->mode.ptp.bc->fac_out.Function = Fac_None;
+ current->mode.ptp.bc->out_cause = AST_CAUSE_NORMAL_CLEARING;
+ misdn_lib_send_event(current->mode.ptp.bc, EVENT_RELEASE_COMPLETE);
+ }
+
+ /* Remove the old call completion record */
+ AST_LIST_REMOVE_CURRENT(list);
+ ast_free(current);
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+}
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Allocate the next record id.
+ *
+ * \retval New record id on success.
+ * \retval -1 on error.
+ *
+ * \note Assumes the misdn_cc_records_db lock is already obtained.
+ */
+static long misdn_cc_record_id_new(void)
+{
+ long record_id;
+ long first_id;
+
+ 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");
+ record_id = -1;
+ break;
+ }
+ }
+
+ return record_id;
+}
+#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_records_db lock is already obtained.
+ */
+static struct misdn_cc_record *misdn_cc_new(void)
+{
+ struct misdn_cc_record *cc_record;
+ long record_id;
+
+ misdn_cc_remove_old();
+
+ cc_record = ast_calloc(1, sizeof(*cc_record));
+ if (cc_record) {
+ record_id = misdn_cc_record_id_new();
+ if (record_id < 0) {
+ ast_free(cc_record);
+ return NULL;
+ }
+
+ /* 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->invoke_id = ++misdn_invoke_id;
+ 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 */
+ AST_LIST_INSERT_HEAD(&misdn_cc_records_db, cc_record, list);
+ }
+ return cc_record;
+}
+#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)
+{
+ struct misdn_cc_record *current;
+
+ while ((current = AST_LIST_REMOVE_HEAD(&misdn_cc_records_db, list))) {
+ /* Do a misdn_cc_delete(current) inline */
+ ast_free(current);
+ }
+}
+#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_record_id = 0;
+}
+#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_LIST_LOCK(&misdn_cc_records_db);
+ cc_record = misdn_cc_find_by_id(*(long *) 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_LIST_UNLOCK(&misdn_cc_records_db);
+
+ return not_responded;
+}
+#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, long 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;
+ }
+ }
+}
+#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;
+ }
+ }
+
+ return "unknown";
+}
+#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" },
+
+ { FacError_CCBS_T_LongTermDenial, "CCBS-T: Long Term Denial" },
+ { FacError_CCBS_T_ShortTermDenial, "CCBS-T: Short Term Denial" },
+
+ { FacError_ECT_LinkIdNotAssignedByNetwork, "ECT: Link ID Not Assigned By Network" },
+/* *INDENT-ON* */
+ };
+
+ unsigned index;
+
+ for (index = 0; index < ARRAY_LEN(arr); ++index) {
+ if (arr[index].code == code) {
+ return arr[index].name;
+ }
+ }
+
+ return "unknown";
+}
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert mISDN redirecting reason to diversion reason.
+ *
+ * \param reason mISDN redirecting reason code.
+ *
+ * \return Supported diversion reason code.
+ */
+static unsigned misdn_to_diversion_reason(enum mISDN_REDIRECTING_REASON reason)
+{
+ unsigned diversion_reason;
+
+ switch (reason) {
+ case mISDN_REDIRECTING_REASON_CALL_FWD:
+ diversion_reason = 1;/* cfu */
+ break;
+ case mISDN_REDIRECTING_REASON_CALL_FWD_BUSY:
+ diversion_reason = 2;/* cfb */
+ break;
+ case mISDN_REDIRECTING_REASON_NO_REPLY:
+ diversion_reason = 3;/* cfnr */
+ break;
+ default:
+ diversion_reason = 0;/* unknown */
+ break;
+ }
+
+ return diversion_reason;
+}
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert diversion reason to mISDN redirecting reason
+ *
+ * \param diversion_reason Diversion reason to convert
+ *
+ * \return Supported redirecting reason code.
+ */
+static enum mISDN_REDIRECTING_REASON diversion_reason_to_misdn(unsigned diversion_reason)
+{
+ enum mISDN_REDIRECTING_REASON reason;
+
+ switch (diversion_reason) {
+ case 1:/* cfu */
+ reason = mISDN_REDIRECTING_REASON_CALL_FWD;
+ break;
+ case 2:/* cfb */
+ reason = mISDN_REDIRECTING_REASON_CALL_FWD_BUSY;
+ break;
+ case 3:/* cfnr */
+ reason = mISDN_REDIRECTING_REASON_NO_REPLY;
+ break;
+ default:
+ reason = mISDN_REDIRECTING_REASON_UNKNOWN;
+ break;
+ }
+
+ return reason;
+}
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert the mISDN presentation to PresentedNumberUnscreened type
+ *
+ * \param presentation mISDN presentation to convert
+ * \param number_present TRUE if the number is present
+ *
+ * \return PresentedNumberUnscreened type
+ */
+static unsigned misdn_to_PresentedNumberUnscreened_type(int presentation, int number_present)
+{
+ unsigned type;
+
+ switch (presentation) {
+ case 0:/* allowed */
+ if (number_present) {
+ type = 0;/* presentationAllowedNumber */
+ } else {
+ type = 2;/* numberNotAvailableDueToInterworking */
+ }
+ break;
+ case 1:/* restricted */
+ if (number_present) {
+ type = 3;/* presentationRestrictedNumber */
+ } else {
+ type = 1;/* presentationRestricted */
+ }
+ break;
+ default:
+ type = 2;/* numberNotAvailableDueToInterworking */
+ break;
+ }
+
+ return type;
+}
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert the PresentedNumberUnscreened type to mISDN presentation
+ *
+ * \param type PresentedNumberUnscreened type
+ *
+ * \return mISDN presentation
+ */
+static int PresentedNumberUnscreened_to_misdn_pres(unsigned type)
+{
+ int presentation;
+
+ switch (type) {
+ default:
+ case 0:/* presentationAllowedNumber */
+ presentation = 0;/* allowed */
+ break;
+
+ case 1:/* presentationRestricted */
+ case 3:/* presentationRestrictedNumber */
+ presentation = 1;/* restricted */
+ break;
+
+ case 2:/* numberNotAvailableDueToInterworking */
+ presentation = 2;/* unavailable */
+ break;
+ }
+
+ return presentation;
+}
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert the mISDN numbering plan to PartyNumber numbering plan
+ *
+ * \param number_plan mISDN numbering plan
+ *
+ * \return PartyNumber numbering plan
+ */
+static unsigned misdn_to_PartyNumber_plan(enum mISDN_NUMBER_PLAN number_plan)
+{
+ unsigned party_plan;
+
+ switch (number_plan) {
+ default:
+ case NUMPLAN_UNKNOWN:
+ party_plan = 0;/* unknown */
+ break;
+
+ case NUMPLAN_ISDN:
+ party_plan = 1;/* public */
+ break;
+
+ case NUMPLAN_DATA:
+ party_plan = 3;/* data */
+ break;
+
+ case NUMPLAN_TELEX:
+ party_plan = 4;/* telex */
+ break;
+
+ case NUMPLAN_NATIONAL:
+ party_plan = 8;/* nationalStandard */
+ break;
+
+ case NUMPLAN_PRIVATE:
+ party_plan = 5;/* private */
+ break;
+ }
+
+ return party_plan;
+}
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert PartyNumber numbering plan to mISDN numbering plan
+ *
+ * \param party_plan PartyNumber numbering plan
+ *
+ * \return mISDN numbering plan
+ */
+static enum mISDN_NUMBER_PLAN PartyNumber_to_misdn_plan(unsigned party_plan)
+{
+ enum mISDN_NUMBER_PLAN number_plan;
+
+ switch (party_plan) {
+ default:
+ case 0:/* unknown */
+ number_plan = NUMPLAN_UNKNOWN;
+ break;
+ case 1:/* public */
+ number_plan = NUMPLAN_ISDN;
+ break;
+ case 3:/* data */
+ number_plan = NUMPLAN_DATA;
+ break;
+ case 4:/* telex */
+ number_plan = NUMPLAN_TELEX;
+ break;
+ case 8:/* nationalStandard */
+ number_plan = NUMPLAN_NATIONAL;
+ break;
+ case 5:/* private */
+ number_plan = NUMPLAN_PRIVATE;
+ break;
+ }
+
+ return number_plan;
+}
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert mISDN type-of-number to PartyNumber public type-of-number
+ *
+ * \param ton mISDN type-of-number
+ *
+ * \return PartyNumber public type-of-number
+ */
+static unsigned misdn_to_PartyNumber_ton_public(enum mISDN_NUMBER_TYPE ton)
+{
+ unsigned party_ton;
+
+ switch (ton) {
+ default:
+ case NUMTYPE_UNKNOWN:
+ party_ton = 0;/* unknown */
+ break;
+
+ case NUMTYPE_INTERNATIONAL:
+ party_ton = 1;/* internationalNumber */
+ break;
+
+ case NUMTYPE_NATIONAL:
+ party_ton = 2;/* nationalNumber */
+ break;
+
+ case NUMTYPE_NETWORK_SPECIFIC:
+ party_ton = 3;/* networkSpecificNumber */
+ break;
+
+ case NUMTYPE_SUBSCRIBER:
+ party_ton = 4;/* subscriberNumber */
+ break;
+
+ case NUMTYPE_ABBREVIATED:
+ party_ton = 6;/* abbreviatedNumber */
+ break;
+ }
+
+ return party_ton;
+}
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert the PartyNumber public type-of-number to mISDN type-of-number
+ *
+ * \param party_ton PartyNumber public type-of-number
+ *
+ * \return mISDN type-of-number
+ */
+static enum mISDN_NUMBER_TYPE PartyNumber_to_misdn_ton_public(unsigned party_ton)
+{
+ enum mISDN_NUMBER_TYPE ton;
+
+ switch (party_ton) {
+ default:
+ case 0:/* unknown */
+ ton = NUMTYPE_UNKNOWN;
+ break;
+
+ case 1:/* internationalNumber */
+ ton = NUMTYPE_INTERNATIONAL;
+ break;
+
+ case 2:/* nationalNumber */
+ ton = NUMTYPE_NATIONAL;
+ break;
+
+ case 3:/* networkSpecificNumber */
+ ton = NUMTYPE_NETWORK_SPECIFIC;
+ break;
+
+ case 4:/* subscriberNumber */
+ ton = NUMTYPE_SUBSCRIBER;
+ break;
+
+ case 6:/* abbreviatedNumber */
+ ton = NUMTYPE_ABBREVIATED;
+ break;
+ }
+
+ return ton;
+}
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert mISDN type-of-number to PartyNumber private type-of-number
+ *
+ * \param ton mISDN type-of-number
+ *
+ * \return PartyNumber private type-of-number
+ */
+static unsigned misdn_to_PartyNumber_ton_private(enum mISDN_NUMBER_TYPE ton)
+{
+ unsigned party_ton;
+
+ switch (ton) {
+ default:
+ case NUMTYPE_UNKNOWN:
+ party_ton = 0;/* unknown */
+ break;
+
+ case NUMTYPE_INTERNATIONAL:
+ party_ton = 1;/* level2RegionalNumber */
+ break;
+
+ case NUMTYPE_NATIONAL:
+ party_ton = 2;/* level1RegionalNumber */
+ break;
+
+ case NUMTYPE_NETWORK_SPECIFIC:
+ party_ton = 3;/* pTNSpecificNumber */
+ break;
+
+ case NUMTYPE_SUBSCRIBER:
+ party_ton = 4;/* localNumber */
+ break;
+
+ case NUMTYPE_ABBREVIATED:
+ party_ton = 6;/* abbreviatedNumber */
+ break;
+ }
+
+ return party_ton;
+}
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Convert the PartyNumber private type-of-number to mISDN type-of-number
+ *
+ * \param party_ton PartyNumber private type-of-number
+ *
+ * \return mISDN type-of-number
+ */
+static enum mISDN_NUMBER_TYPE PartyNumber_to_misdn_ton_private(unsigned party_ton)
+{
+ enum mISDN_NUMBER_TYPE ton;
+
+ switch (party_ton) {
+ default:
+ case 0:/* unknown */
+ ton = NUMTYPE_UNKNOWN;
+ break;
+
+ case 1:/* level2RegionalNumber */
+ ton = NUMTYPE_INTERNATIONAL;
+ break;
+
+ case 2:/* level1RegionalNumber */
+ ton = NUMTYPE_NATIONAL;
+ break;
+
+ case 3:/* pTNSpecificNumber */
+ ton = NUMTYPE_NETWORK_SPECIFIC;
+ break;
+
+ case 4:/* localNumber */
+ ton = NUMTYPE_SUBSCRIBER;
+ break;
+
+ case 6:/* abbreviatedNumber */
+ ton = NUMTYPE_ABBREVIATED;
+ break;
+ }
+
+ return ton;
+}
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
/*!
* \internal
@@ -1128,25 +2342,589 @@
return "Unknown Bearer";
}
-
-static void print_facility(struct FacParm *fac, struct misdn_bchannel *bc)
-{
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Fill in facility PartyNumber information
+ *
+ * \param party PartyNumber structure to fill in.
+ * \param id Information to put in PartyNumber structure.
+ *
+ * \return Nothing
+ */
+static void misdn_PartyNumber_fill(struct FacPartyNumber *party, const struct misdn_party_id *id)
+{
+ ast_copy_string((char *) party->Number, id->number, sizeof(party->Number));
+ party->LengthOfNumber = strlen((char *) party->Number);
+ party->Type = misdn_to_PartyNumber_plan(id->number_plan);
+ switch (party->Type) {
+ case 1:/* public */
+ party->TypeOfNumber = misdn_to_PartyNumber_ton_public(id->number_type);
+ break;
+ case 5:/* private */
+ party->TypeOfNumber = misdn_to_PartyNumber_ton_private(id->number_type);
+ break;
+ default:
+ party->TypeOfNumber = 0;/* Dont't care */
+ break;
+ }
+}
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Extract the information from PartyNumber
+ *
+ * \param id Where to put extracted PartyNumber information
+ * \param party PartyNumber information to extract
+ *
+ * \return Nothing
+ */
+static void misdn_PartyNumber_extract(struct misdn_party_id *id, const struct FacPartyNumber *party)
+{
+ if (party->LengthOfNumber) {
+ ast_copy_string(id->number, (char *) party->Number, sizeof(id->number));
+ id->number_plan = PartyNumber_to_misdn_plan(party->Type);
+ switch (party->Type) {
+ case 1:/* public */
+ id->number_type = PartyNumber_to_misdn_ton_public(party->TypeOfNumber);
+ break;
+ case 5:/* private */
+ id->number_type = PartyNumber_to_misdn_ton_private(party->TypeOfNumber);
+ break;
+ default:
+ id->number_type = NUMTYPE_UNKNOWN;
+ break;
+ }
+ } else {
+ /* Number not present */
+ id->number_type = NUMTYPE_UNKNOWN;
+ id->number_plan = NUMPLAN_ISDN;
+ id->number[0] = 0;
+ }
+}
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
+ * \internal
+ * \brief Fill in facility Address information
+ *
+ * \param Address Address structure to fill in.
+ * \param id Information to put in Address structure.
+ *
+ * \return Nothing
+ */
+static void misdn_Address_fill(struct FacAddress *Address, const struct misdn_party_id *id)
+{
+ misdn_PartyNumber_fill(&Address->Party, id);
+
+ /* Subaddresses are not supported yet */
+ Address->Subaddress.Length = 0;
+}
+#endif /* defined(AST_MISDN_ENHANCEMENTS) */
+
+#if defined(AST_MISDN_ENHANCEMENTS)
+/*!
[... 7892 lines stripped ...]
More information about the asterisk-commits
mailing list