diff options
author | Richard Mudgett <rmudgett@digium.com> | 2009-04-21 17:44:01 +0000 |
---|---|---|
committer | Richard Mudgett <rmudgett@digium.com> | 2009-04-21 17:44:01 +0000 |
commit | 6bb2b6c096cc8c56d0a01fb17d9723b8c5b92d0e (patch) | |
tree | 274e051566d5d31050dc6c64f1ae79bc83bf38a5 | |
parent | cf95f3a1180b6e3078ca3ceaf1a28570ba340ace (diff) |
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
Review: http://reviewboard.digium.com/r/218/
Merged from team/rmudgett/misdn_facility branch.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@189735 65c4cc65-6c06-0410-ace0-fbb531ad65f3
-rw-r--r-- | CHANGES | 20 | ||||
-rw-r--r-- | channels/chan_misdn.c | 5508 | ||||
-rw-r--r-- | channels/misdn/chan_misdn_config.h | 1 | ||||
-rw-r--r-- | channels/misdn/ie.c | 100 | ||||
-rw-r--r-- | channels/misdn/isdn_lib.c | 1256 | ||||
-rw-r--r-- | channels/misdn/isdn_lib.h | 86 | ||||
-rw-r--r-- | channels/misdn/isdn_lib_intern.h | 30 | ||||
-rw-r--r-- | channels/misdn/isdn_msg_parser.c | 394 | ||||
-rw-r--r-- | channels/misdn_config.c | 2 | ||||
-rw-r--r-- | configs/misdn.conf.sample | 20 |
10 files changed, 6524 insertions, 893 deletions
@@ -61,7 +61,25 @@ mISDN channel driver (chan_misdn) changes 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 ------------------------------------------- diff --git a/channels/chan_misdn.c b/channels/chan_misdn.c index 0c691a184..c246b9b93 100644 --- a/channels/chan_misdn.c +++ b/channels/chan_misdn.c @@ -29,6 +29,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> <depend>misdn</depend> @@ -48,6 +68,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include <sys/file.h> #include <semaphore.h> #include <ctype.h> +#include <time.h> #include "asterisk/channel.h" #include "asterisk/config.h" @@ -110,6 +131,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; enum misdn_chan_state { @@ -308,6 +488,16 @@ struct chan_list { */ 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 void hangup_chan(struct chan_list *ch); 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 start_bc_tones(struct chan_list *cl); 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); @@ -566,6 +759,1027 @@ static struct chan_list *get_chan_by_ast_name(char *name) 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 * \brief Convert the mISDN type of number code to a string @@ -1128,25 +2342,589 @@ static const char *bearer2str(int cap) return "Unknown Bearer"; } +#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); -static void print_facility(struct FacParm *fac, struct misdn_bchannel *bc) + /* Subaddresses are not supported yet */ + Address->Subaddress.Length = 0; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Fill in facility PresentedNumberUnscreened information + * + * \param presented PresentedNumberUnscreened structure to fill in. + * \param id Information to put in PresentedNumberUnscreened structure. + * + * \return Nothing + */ +static void misdn_PresentedNumberUnscreened_fill(struct FacPresentedNumberUnscreened *presented, const struct misdn_party_id *id) { + presented->Type = misdn_to_PresentedNumberUnscreened_type(id->presentation, id->number[0] ? 1 : 0); + misdn_PartyNumber_fill(&presented->Unscreened, id); +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Extract the information from PartyNumber + * + * \param id Where to put extracted PresentedNumberUnscreened information + * \param presented PresentedNumberUnscreened information to extract + * + * \return Nothing + */ +static void misdn_PresentedNumberUnscreened_extract(struct misdn_party_id *id, const struct FacPresentedNumberUnscreened *presented) +{ + id->presentation = PresentedNumberUnscreened_to_misdn_pres(presented->Type); + id->screening = 0;/* unscreened */ + misdn_PartyNumber_extract(id, &presented->Unscreened); +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#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; + } + } +} +#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; + } + } +} +#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); +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static void print_facility_PresentedNumberUnscreened(unsigned Level, const struct FacPresentedNumberUnscreened *Presented, const struct misdn_bchannel *bc) +{ + const char *Spacing; + + Spacing = &Level_Spacing[sizeof(Level_Spacing) - 1 - Level]; + chan_misdn_log(1, bc->port, " -->%s Unscreened Type:%d\n", Spacing, Presented->Type); + switch (Presented->Type) { + case 0: /* presentationAllowedNumber */ + chan_misdn_log(1, bc->port, " -->%s Allowed:\n", Spacing); + print_facility_PartyNumber(Level + 2, &Presented->Unscreened, bc); + break; + case 1: /* presentationRestricted */ + chan_misdn_log(1, bc->port, " -->%s Restricted\n", Spacing); + break; + case 2: /* numberNotAvailableDueToInterworking */ + chan_misdn_log(1, bc->port, " -->%s Not Available\n", Spacing); + break; + case 3: /* presentationRestrictedNumber */ + chan_misdn_log(1, bc->port, " -->%s Restricted:\n", Spacing); + print_facility_PartyNumber(Level + 2, &Presented->Unscreened, bc); + break; + default: + break; + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static void print_facility_AddressScreened(unsigned Level, const struct FacAddressScreened *Address, const struct misdn_bchannel *bc) +{ + const char *Spacing; + + Spacing = &Level_Spacing[sizeof(Level_Spacing) - 1 - Level]; + chan_misdn_log(1, bc->port, " -->%s ScreeningIndicator:%d\n", Spacing, Address->ScreeningIndicator); + print_facility_PartyNumber(Level, &Address->Party, bc); + print_facility_Subaddress(Level, &Address->Subaddress, bc); +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static void print_facility_PresentedAddressScreened(unsigned Level, const struct FacPresentedAddressScreened *Presented, const struct misdn_bchannel *bc) +{ + const char *Spacing; + + Spacing = &Level_Spacing[sizeof(Level_Spacing) - 1 - Level]; + chan_misdn_log(1, bc->port, " -->%s Screened Type:%d\n", Spacing, Presented->Type); + switch (Presented->Type) { + case 0: /* presentationAllowedAddress */ + chan_misdn_log(1, bc->port, " -->%s Allowed:\n", Spacing); + print_facility_AddressScreened(Level + 2, &Presented->Address, bc); + break; + case 1: /* presentationRestricted */ + chan_misdn_log(1, bc->port, " -->%s Restricted\n", Spacing); + break; + case 2: /* numberNotAvailableDueToInterworking */ + chan_misdn_log(1, bc->port, " -->%s Not Available\n", Spacing); + break; + case 3: /* presentationRestrictedAddress */ + chan_misdn_log(1, bc->port, " -->%s Restricted:\n", Spacing); + print_facility_AddressScreened(Level + 2, &Presented->Address, bc); + break; + default: + break; + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static void print_facility_Q931_Bc_Hlc_Llc(unsigned Level, const struct Q931_Bc_Hlc_Llc *Q931ie, const struct misdn_bchannel *bc) +{ + const char *Spacing; + + Spacing = &Level_Spacing[sizeof(Level_Spacing) - 1 - Level]; + chan_misdn_log(1, bc->port, " -->%s Q931ie:\n", Spacing); + if (Q931ie->Bc.Length) { + chan_misdn_log(1, bc->port, " -->%s Bc Len:%d\n", Spacing, Q931ie->Bc.Length); + } + if (Q931ie->Hlc.Length) { + chan_misdn_log(1, bc->port, " -->%s Hlc Len:%d\n", Spacing, Q931ie->Hlc.Length); + } + if (Q931ie->Llc.Length) { + chan_misdn_log(1, bc->port, " -->%s Llc Len:%d\n", Spacing, Q931ie->Llc.Length); + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static void print_facility_Q931_Bc_Hlc_Llc_Uu(unsigned Level, const struct Q931_Bc_Hlc_Llc_Uu *Q931ie, const struct misdn_bchannel *bc) +{ + const char *Spacing; + + Spacing = &Level_Spacing[sizeof(Level_Spacing) - 1 - Level]; + chan_misdn_log(1, bc->port, " -->%s Q931ie:\n", Spacing); + if (Q931ie->Bc.Length) { + chan_misdn_log(1, bc->port, " -->%s Bc Len:%d\n", Spacing, Q931ie->Bc.Length); + } + if (Q931ie->Hlc.Length) { + chan_misdn_log(1, bc->port, " -->%s Hlc Len:%d\n", Spacing, Q931ie->Hlc.Length); + } + if (Q931ie->Llc.Length) { + chan_misdn_log(1, bc->port, " -->%s Llc Len:%d\n", Spacing, Q931ie->Llc.Length); + } + if (Q931ie->UserInfo.Length) { + chan_misdn_log(1, bc->port, " -->%s UserInfo Len:%d\n", Spacing, Q931ie->UserInfo.Length); + } +} +#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); + print_facility_Q931_Bc_Hlc_Llc(Level, &CallInfo->Q931ie, bc); + if (CallInfo->SubaddressOfA.Length) { + chan_misdn_log(1, bc->port, " -->%s SubaddressOfA:\n", Spacing); + print_facility_Subaddress(Level + 1, &CallInfo->SubaddressOfA, bc); + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static void print_facility_ServedUserNr(unsigned Level, const struct FacPartyNumber *Party, const struct misdn_bchannel *bc) +{ + const char *Spacing; + + Spacing = &Level_Spacing[sizeof(Level_Spacing) - 1 - Level]; + if (Party->LengthOfNumber) { + print_facility_PartyNumber(Level, Party, bc); + } else { + chan_misdn_log(1, bc->port, " -->%s All Numbers\n", Spacing); + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static void print_facility_IntResult(unsigned Level, const struct FacForwardingRecord *ForwardingRecord, const struct misdn_bchannel *bc) +{ + const char *Spacing; + + Spacing = &Level_Spacing[sizeof(Level_Spacing) - 1 - Level]; + chan_misdn_log(1, bc->port, " -->%s Procedure:%d BasicService:%d\n", + Spacing, + ForwardingRecord->Procedure, + ForwardingRecord->BasicService); + chan_misdn_log(1, bc->port, " -->%s ForwardedTo:\n", Spacing); + print_facility_Address(Level + 1, &ForwardingRecord->ForwardedTo, bc); + chan_misdn_log(1, bc->port, " -->%s ServedUserNr:\n", Spacing); + print_facility_ServedUserNr(Level + 1, &ForwardingRecord->ServedUser, bc); +} +#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"); +#if defined(AST_MISDN_ENHANCEMENTS) + case Fac_ActivationDiversion: + chan_misdn_log(1, bc->port, " --> ActivationDiversion: InvokeID:%d\n", + fac->u.ActivationDiversion.InvokeID); + switch (fac->u.ActivationDiversion.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke: Procedure:%d BasicService:%d\n", + fac->u.ActivationDiversion.Component.Invoke.Procedure, + fac->u.ActivationDiversion.Component.Invoke.BasicService); + chan_misdn_log(1, bc->port, " --> ForwardedTo:\n"); + print_facility_Address(3, &fac->u.ActivationDiversion.Component.Invoke.ForwardedTo, bc); + chan_misdn_log(1, bc->port, " --> ServedUserNr:\n"); + print_facility_ServedUserNr(3, &fac->u.ActivationDiversion.Component.Invoke.ServedUser, bc); + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result\n"); + break; + default: + break; + } 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); + case Fac_DeactivationDiversion: + chan_misdn_log(1, bc->port, " --> DeactivationDiversion: InvokeID:%d\n", + fac->u.DeactivationDiversion.InvokeID); + switch (fac->u.DeactivationDiversion.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke: Procedure:%d BasicService:%d\n", + fac->u.DeactivationDiversion.Component.Invoke.Procedure, + fac->u.DeactivationDiversion.Component.Invoke.BasicService); + chan_misdn_log(1, bc->port, " --> ServedUserNr:\n"); + print_facility_ServedUserNr(3, &fac->u.DeactivationDiversion.Component.Invoke.ServedUser, bc); + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result\n"); + break; + default: + break; + } break; -#endif + case Fac_ActivationStatusNotificationDiv: + chan_misdn_log(1, bc->port, " --> ActivationStatusNotificationDiv: InvokeID:%d Procedure:%d BasicService:%d\n", + fac->u.ActivationStatusNotificationDiv.InvokeID, + fac->u.ActivationStatusNotificationDiv.Procedure, + fac->u.ActivationStatusNotificationDiv.BasicService); + chan_misdn_log(1, bc->port, " --> ForwardedTo:\n"); + print_facility_Address(2, &fac->u.ActivationStatusNotificationDiv.ForwardedTo, bc); + chan_misdn_log(1, bc->port, " --> ServedUserNr:\n"); + print_facility_ServedUserNr(2, &fac->u.ActivationStatusNotificationDiv.ServedUser, bc); + break; + case Fac_DeactivationStatusNotificationDiv: + chan_misdn_log(1, bc->port, " --> DeactivationStatusNotificationDiv: InvokeID:%d Procedure:%d BasicService:%d\n", + fac->u.DeactivationStatusNotificationDiv.InvokeID, + fac->u.DeactivationStatusNotificationDiv.Procedure, + fac->u.DeactivationStatusNotificationDiv.BasicService); + chan_misdn_log(1, bc->port, " --> ServedUserNr:\n"); + print_facility_ServedUserNr(2, &fac->u.DeactivationStatusNotificationDiv.ServedUser, bc); + break; + case Fac_InterrogationDiversion: + chan_misdn_log(1, bc->port, " --> InterrogationDiversion: InvokeID:%d\n", + fac->u.InterrogationDiversion.InvokeID); + switch (fac->u.InterrogationDiversion.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke: Procedure:%d BasicService:%d\n", + fac->u.InterrogationDiversion.Component.Invoke.Procedure, + fac->u.InterrogationDiversion.Component.Invoke.BasicService); + chan_misdn_log(1, bc->port, " --> ServedUserNr:\n"); + print_facility_ServedUserNr(3, &fac->u.InterrogationDiversion.Component.Invoke.ServedUser, bc); + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result:\n"); + if (fac->u.InterrogationDiversion.Component.Result.NumRecords) { + for (Index = 0; Index < fac->u.InterrogationDiversion.Component.Result.NumRecords; ++Index) { + chan_misdn_log(1, bc->port, " --> IntResult[%d]:\n", Index); + print_facility_IntResult(3, &fac->u.InterrogationDiversion.Component.Result.List[Index], bc); + } + } + break; + default: + break; + } + break; + case Fac_DiversionInformation: + chan_misdn_log(1, bc->port, " --> DiversionInformation: InvokeID:%d Reason:%d BasicService:%d\n", + fac->u.DiversionInformation.InvokeID, + fac->u.DiversionInformation.DiversionReason, + fac->u.DiversionInformation.BasicService); + if (fac->u.DiversionInformation.ServedUserSubaddress.Length) { + chan_misdn_log(1, bc->port, " --> ServedUserSubaddress:\n"); + print_facility_Subaddress(2, &fac->u.DiversionInformation.ServedUserSubaddress, bc); + } + if (fac->u.DiversionInformation.CallingAddressPresent) { + chan_misdn_log(1, bc->port, " --> CallingAddress:\n"); + print_facility_PresentedAddressScreened(2, &fac->u.DiversionInformation.CallingAddress, bc); + } + if (fac->u.DiversionInformation.OriginalCalledPresent) { + chan_misdn_log(1, bc->port, " --> OriginalCalledNr:\n"); + print_facility_PresentedNumberUnscreened(2, &fac->u.DiversionInformation.OriginalCalled, bc); + } + if (fac->u.DiversionInformation.LastDivertingPresent) { + chan_misdn_log(1, bc->port, " --> LastDivertingNr:\n"); + print_facility_PresentedNumberUnscreened(2, &fac->u.DiversionInformation.LastDiverting, bc); + } + if (fac->u.DiversionInformation.LastDivertingReasonPresent) { + chan_misdn_log(1, bc->port, " --> LastDivertingReason:%d\n", fac->u.DiversionInformation.LastDivertingReason); + } + if (fac->u.DiversionInformation.UserInfo.Length) { + chan_misdn_log(1, bc->port, " --> UserInfo Length:%d\n", fac->u.DiversionInformation.UserInfo.Length); + } + break; + case Fac_CallDeflection: + chan_misdn_log(1, bc->port, " --> CallDeflection: InvokeID:%d\n", + fac->u.CallDeflection.InvokeID); + switch (fac->u.CallDeflection.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke:\n"); + if (fac->u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUserPresent) { + chan_misdn_log(1, bc->port, " --> PresentationAllowed:%d\n", + fac->u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUser); + } + chan_misdn_log(1, bc->port, " --> DeflectionAddress:\n"); + print_facility_Address(3, &fac->u.CallDeflection.Component.Invoke.Deflection, bc); + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result\n"); + break; + default: + break; + } + break; + case Fac_CallRerouteing: + chan_misdn_log(1, bc->port, " --> CallRerouteing: InvokeID:%d\n", + fac->u.CallRerouteing.InvokeID); + switch (fac->u.CallRerouteing.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke: Reason:%d Counter:%d\n", + fac->u.CallRerouteing.Component.Invoke.ReroutingReason, + fac->u.CallRerouteing.Component.Invoke.ReroutingCounter); + chan_misdn_log(1, bc->port, " --> CalledAddress:\n"); + print_facility_Address(3, &fac->u.CallRerouteing.Component.Invoke.CalledAddress, bc); + print_facility_Q931_Bc_Hlc_Llc_Uu(2, &fac->u.CallRerouteing.Component.Invoke.Q931ie, bc); + chan_misdn_log(1, bc->port, " --> LastReroutingNr:\n"); + print_facility_PresentedNumberUnscreened(3, &fac->u.CallRerouteing.Component.Invoke.LastRerouting, bc); + chan_misdn_log(1, bc->port, " --> SubscriptionOption:%d\n", + fac->u.CallRerouteing.Component.Invoke.SubscriptionOption); + if (fac->u.CallRerouteing.Component.Invoke.CallingPartySubaddress.Length) { + chan_misdn_log(1, bc->port, " --> CallingParty:\n"); + print_facility_Subaddress(3, &fac->u.CallRerouteing.Component.Invoke.CallingPartySubaddress, bc); + } + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result\n"); + break; + default: + break; + } + break; + case Fac_InterrogateServedUserNumbers: + chan_misdn_log(1, bc->port, " --> InterrogateServedUserNumbers: InvokeID:%d\n", + fac->u.InterrogateServedUserNumbers.InvokeID); + switch (fac->u.InterrogateServedUserNumbers.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke\n"); + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result:\n"); + if (fac->u.InterrogateServedUserNumbers.Component.Result.NumRecords) { + for (Index = 0; Index < fac->u.InterrogateServedUserNumbers.Component.Result.NumRecords; ++Index) { + chan_misdn_log(1, bc->port, " --> ServedUserNr[%d]:\n", Index); + print_facility_PartyNumber(3, &fac->u.InterrogateServedUserNumbers.Component.Result.List[Index], bc); + } + } + break; + default: + break; + } + break; + case Fac_DivertingLegInformation1: + chan_misdn_log(1, bc->port, " --> DivertingLegInformation1: InvokeID:%d Reason:%d SubscriptionOption:%d\n", + fac->u.DivertingLegInformation1.InvokeID, + fac->u.DivertingLegInformation1.DiversionReason, + fac->u.DivertingLegInformation1.SubscriptionOption); + if (fac->u.DivertingLegInformation1.DivertedToPresent) { + chan_misdn_log(1, bc->port, " --> DivertedToNr:\n"); + print_facility_PresentedNumberUnscreened(2, &fac->u.DivertingLegInformation1.DivertedTo, bc); + } + break; + case Fac_DivertingLegInformation2: + chan_misdn_log(1, bc->port, " --> DivertingLegInformation2: InvokeID:%d Reason:%d Count:%d\n", + fac->u.DivertingLegInformation2.InvokeID, + fac->u.DivertingLegInformation2.DiversionReason, + fac->u.DivertingLegInformation2.DiversionCounter); + if (fac->u.DivertingLegInformation2.DivertingPresent) { + chan_misdn_log(1, bc->port, " --> DivertingNr:\n"); + print_facility_PresentedNumberUnscreened(2, &fac->u.DivertingLegInformation2.Diverting, bc); + } + if (fac->u.DivertingLegInformation2.OriginalCalledPresent) { + chan_misdn_log(1, bc->port, " --> OriginalCalledNr:\n"); + print_facility_PresentedNumberUnscreened(2, &fac->u.DivertingLegInformation2.OriginalCalled, bc); + } + break; + case Fac_DivertingLegInformation3: + chan_misdn_log(1, bc->port, " --> DivertingLegInformation3: InvokeID:%d PresentationAllowed:%d\n", + fac->u.DivertingLegInformation3.InvokeID, + fac->u.DivertingLegInformation3.PresentationAllowedIndicator); + break; + +#else /* !defined(AST_MISDN_ENHANCEMENTS) */ + 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"); break; +#endif /* !defined(AST_MISDN_ENHANCEMENTS) */ case Fac_AOCDCurrency: if (fac->u.AOCDcur.chargeNotAvailable) { chan_misdn_log(1, bc->port, " --> AOCD currency: charge not available\n"); @@ -1175,7 +2953,345 @@ static void print_facility(struct FacParm *fac, struct misdn_bchannel *bc) 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_EctExecute: + chan_misdn_log(1, bc->port, " --> EctExecute: InvokeID:%d\n", + fac->u.EctExecute.InvokeID); + break; + case Fac_ExplicitEctExecute: + chan_misdn_log(1, bc->port, " --> ExplicitEctExecute: InvokeID:%d LinkID:%d\n", + fac->u.ExplicitEctExecute.InvokeID, + fac->u.ExplicitEctExecute.LinkID); + break; + case Fac_RequestSubaddress: + chan_misdn_log(1, bc->port, " --> RequestSubaddress: InvokeID:%d\n", + fac->u.RequestSubaddress.InvokeID); + break; + case Fac_SubaddressTransfer: + chan_misdn_log(1, bc->port, " --> SubaddressTransfer: InvokeID:%d\n", + fac->u.SubaddressTransfer.InvokeID); + print_facility_Subaddress(1, &fac->u.SubaddressTransfer.Subaddress, bc); + break; + case Fac_EctLinkIdRequest: + chan_misdn_log(1, bc->port, " --> EctLinkIdRequest: InvokeID:%d\n", + fac->u.EctLinkIdRequest.InvokeID); + switch (fac->u.EctLinkIdRequest.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke\n"); + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result: LinkID:%d\n", + fac->u.EctLinkIdRequest.Component.Result.LinkID); + break; + default: + break; + } + break; + case Fac_EctInform: + chan_misdn_log(1, bc->port, " --> EctInform: InvokeID:%d Status:%d\n", + fac->u.EctInform.InvokeID, + fac->u.EctInform.Status); + if (fac->u.EctInform.RedirectionPresent) { + chan_misdn_log(1, bc->port, " --> Redirection Number\n"); + print_facility_PresentedNumberUnscreened(2, &fac->u.EctInform.Redirection, bc); + } + break; + case Fac_EctLoopTest: + chan_misdn_log(1, bc->port, " --> EctLoopTest: InvokeID:%d\n", + fac->u.EctLoopTest.InvokeID); + switch (fac->u.EctLoopTest.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke: CallTransferID:%d\n", + fac->u.EctLoopTest.Component.Invoke.CallTransferID); + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result: LoopResult:%d\n", + fac->u.EctLoopTest.Component.Result.LoopResult); + break; + default: + break; + } + 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; + } + 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; + } + 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); + print_facility_Q931_Bc_Hlc_Llc(1, &fac->u.CCBSErase.Q931ie, 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); + print_facility_Q931_Bc_Hlc_Llc(1, &fac->u.CCBSRemoteUserFree.Q931ie, 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); + print_facility_Q931_Bc_Hlc_Llc(2, &fac->u.CCBSStatusRequest.Component.Invoke.Q931ie, bc); + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result: Free:%d\n", + fac->u.CCBSStatusRequest.Component.Result.Free); + break; + default: + break; + } + 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); + print_facility_Q931_Bc_Hlc_Llc(1, &fac->u.CCBSBFree.Q931ie, 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; + } + 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); + } + } + break; + default: + break; + } + 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; + } + 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); + } + } + break; + default: + break; + } + break; + case Fac_CCBS_T_Call: + chan_misdn_log(1, bc->port, " --> CCBS_T_Call: InvokeID:%d\n", + fac->u.CCBS_T_Call.InvokeID); + break; + case Fac_CCBS_T_Suspend: + chan_misdn_log(1, bc->port, " --> CCBS_T_Suspend: InvokeID:%d\n", + fac->u.CCBS_T_Suspend.InvokeID); + break; + case Fac_CCBS_T_Resume: + chan_misdn_log(1, bc->port, " --> CCBS_T_Resume: InvokeID:%d\n", + fac->u.CCBS_T_Resume.InvokeID); + break; + case Fac_CCBS_T_RemoteUserFree: + chan_misdn_log(1, bc->port, " --> CCBS_T_RemoteUserFree: InvokeID:%d\n", + fac->u.CCBS_T_RemoteUserFree.InvokeID); + break; + case Fac_CCBS_T_Available: + chan_misdn_log(1, bc->port, " --> CCBS_T_Available: InvokeID:%d\n", + fac->u.CCBS_T_Available.InvokeID); + break; + case Fac_CCBS_T_Request: + chan_misdn_log(1, bc->port, " --> CCBS_T_Request: InvokeID:%d\n", + fac->u.CCBS_T_Request.InvokeID); + switch (fac->u.CCBS_T_Request.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke\n"); + chan_misdn_log(1, bc->port, " --> DestinationAddress:\n"); + print_facility_Address(3, &fac->u.CCBS_T_Request.Component.Invoke.Destination, bc); + print_facility_Q931_Bc_Hlc_Llc(2, &fac->u.CCBS_T_Request.Component.Invoke.Q931ie, bc); + if (fac->u.CCBS_T_Request.Component.Invoke.RetentionSupported) { + chan_misdn_log(1, bc->port, " --> RetentionSupported:1\n"); + } + if (fac->u.CCBS_T_Request.Component.Invoke.PresentationAllowedIndicatorPresent) { + chan_misdn_log(1, bc->port, " --> PresentationAllowed:%d\n", + fac->u.CCBS_T_Request.Component.Invoke.PresentationAllowedIndicator); + } + if (fac->u.CCBS_T_Request.Component.Invoke.Originating.Party.LengthOfNumber) { + chan_misdn_log(1, bc->port, " --> OriginatingAddress:\n"); + print_facility_Address(3, &fac->u.CCBS_T_Request.Component.Invoke.Originating, bc); + } + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result: RetentionSupported:%d\n", + fac->u.CCBS_T_Request.Component.Result.RetentionSupported); + break; + default: + break; + } + break; + case Fac_CCNR_T_Request: + chan_misdn_log(1, bc->port, " --> CCNR_T_Request: InvokeID:%d\n", + fac->u.CCNR_T_Request.InvokeID); + switch (fac->u.CCNR_T_Request.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke\n"); + chan_misdn_log(1, bc->port, " --> DestinationAddress:\n"); + print_facility_Address(3, &fac->u.CCNR_T_Request.Component.Invoke.Destination, bc); + print_facility_Q931_Bc_Hlc_Llc(2, &fac->u.CCNR_T_Request.Component.Invoke.Q931ie, bc); + if (fac->u.CCNR_T_Request.Component.Invoke.RetentionSupported) { + chan_misdn_log(1, bc->port, " --> RetentionSupported:1\n"); + } + if (fac->u.CCNR_T_Request.Component.Invoke.PresentationAllowedIndicatorPresent) { + chan_misdn_log(1, bc->port, " --> PresentationAllowed:%d\n", + fac->u.CCNR_T_Request.Component.Invoke.PresentationAllowedIndicator); + } + if (fac->u.CCNR_T_Request.Component.Invoke.Originating.Party.LengthOfNumber) { + chan_misdn_log(1, bc->port, " --> OriginatingAddress:\n"); + print_facility_Address(3, &fac->u.CCNR_T_Request.Component.Invoke.Originating, bc); + } + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result: RetentionSupported:%d\n", + fac->u.CCNR_T_Request.Component.Result.RetentionSupported); + break; + default: + break; + } + break; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ case Fac_None: + /* No facility so print nothing */ + break; default: chan_misdn_log(1, bc->port, " --> unknown facility\n"); break; @@ -1862,7 +3978,9 @@ static char *handle_cli_misdn_show_config(struct ast_cli_entry *e, int cmd, stru ast_cli(a->fd, "Unknown option: %s\n", a->argv[3]); return CLI_SHOWUSAGE; } - } else if (a->argc == 3 || onlyport == 0) { + } + + if (a->argc == 3 || onlyport == 0) { ast_cli(a->fd, "mISDN General-Config:\n"); for (elem = MISDN_GEN_FIRST + 1, linebreak = 1; elem < MISDN_GEN_LAST; elem++, linebreak++) { misdn_cfg_get_config_string(0, elem, buffer, sizeof(buffer)); @@ -2003,7 +4121,8 @@ static void print_bc_info(int fd, struct chan_list *help, struct misdn_bchannel ast_cli(fd, "* Pid:%d Port:%d Ch:%d Mode:%s Orig:%s dialed:%s\n" " --> caller:\"%s\" <%s>\n" - " --> redirecting:\"%s\" <%s>\n" + " --> redirecting-from:\"%s\" <%s>\n" + " --> redirecting-to:\"%s\" <%s>\n" " --> context:%s state:%s\n", bc->pid, bc->port, @@ -2015,6 +4134,8 @@ static void print_bc_info(int fd, struct chan_list *help, struct misdn_bchannel (ast && ast->cid.cid_num) ? ast->cid.cid_num : "", bc->redirecting.from.name, bc->redirecting.from.number, + bc->redirecting.to.name, + bc->redirecting.to.number, ast ? ast->context : "", misdn_get_ch_state(help)); if (misdn_debug[bc->port] > 0) { @@ -2267,6 +4388,739 @@ static char *handle_cli_misdn_show_port(struct ast_cli_entry *e, int cmd, struct return CLI_SUCCESS; } +#if defined(AST_MISDN_ENHANCEMENTS) && defined(CCBS_TEST_MESSAGES) +static const struct FacParm Fac_Msgs[] = { +/* *INDENT-OFF* */ + [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, + [13].u.CCBSErase.Q931ie.Bc.Length = 2, + [13].u.CCBSErase.Q931ie.Bc.Contents = "JK", + [13].u.CCBSErase.AddressOfB.Party.Type = 1, + [13].u.CCBSErase.AddressOfB.Party.LengthOfNumber = 11, + [13].u.CCBSErase.AddressOfB.Party.TypeOfNumber = 1, + [13].u.CCBSErase.AddressOfB.Party.Number = "18003020102", + [13].u.CCBSErase.AddressOfB.Subaddress.Type = 0, + [13].u.CCBSErase.AddressOfB.Subaddress.Length = 4, + [13].u.CCBSErase.AddressOfB.Subaddress.u.UserSpecified.OddCountPresent = 1, + [13].u.CCBSErase.AddressOfB.Subaddress.u.UserSpecified.OddCount = 1, + [13].u.CCBSErase.AddressOfB.Subaddress.u.UserSpecified.Information = "3748", + [13].u.CCBSErase.RecallMode = 1, + [13].u.CCBSErase.CCBSReference = 102, + [13].u.CCBSErase.Reason = 3, + + [14].Function = Fac_CCBSErase, + [14].u.CCBSErase.InvokeID = 21, + [14].u.CCBSErase.Q931ie.Bc.Length = 2, + [14].u.CCBSErase.Q931ie.Bc.Contents = "JK", + [14].u.CCBSErase.AddressOfB.Party.Type = 2, + [14].u.CCBSErase.AddressOfB.Party.LengthOfNumber = 4, + [14].u.CCBSErase.AddressOfB.Party.Number = "1803", + [14].u.CCBSErase.AddressOfB.Subaddress.Type = 1, + [14].u.CCBSErase.AddressOfB.Subaddress.Length = 4, + [14].u.CCBSErase.AddressOfB.Subaddress.u.Nsap = "6492", + [14].u.CCBSErase.RecallMode = 1, + [14].u.CCBSErase.CCBSReference = 102, + [14].u.CCBSErase.Reason = 3, + + [15].Function = Fac_CCBSErase, + [15].u.CCBSErase.InvokeID = 22, + [15].u.CCBSErase.Q931ie.Bc.Length = 2, + [15].u.CCBSErase.Q931ie.Bc.Contents = "JK", + [15].u.CCBSErase.AddressOfB.Party.Type = 3, + [15].u.CCBSErase.AddressOfB.Party.LengthOfNumber = 4, + [15].u.CCBSErase.AddressOfB.Party.Number = "1803", + [15].u.CCBSErase.RecallMode = 1, + [15].u.CCBSErase.CCBSReference = 102, + [15].u.CCBSErase.Reason = 3, + + [16].Function = Fac_CCBSErase, + [16].u.CCBSErase.InvokeID = 23, + [16].u.CCBSErase.Q931ie.Bc.Length = 2, + [16].u.CCBSErase.Q931ie.Bc.Contents = "JK", + [16].u.CCBSErase.AddressOfB.Party.Type = 4, + [16].u.CCBSErase.AddressOfB.Party.LengthOfNumber = 4, + [16].u.CCBSErase.AddressOfB.Party.Number = "1803", + [16].u.CCBSErase.RecallMode = 1, + [16].u.CCBSErase.CCBSReference = 102, + [16].u.CCBSErase.Reason = 3, + + [17].Function = Fac_CCBSErase, + [17].u.CCBSErase.InvokeID = 24, + [17].u.CCBSErase.Q931ie.Bc.Length = 2, + [17].u.CCBSErase.Q931ie.Bc.Contents = "JK", + [17].u.CCBSErase.AddressOfB.Party.Type = 5, + [17].u.CCBSErase.AddressOfB.Party.LengthOfNumber = 11, + [17].u.CCBSErase.AddressOfB.Party.TypeOfNumber = 4, + [17].u.CCBSErase.AddressOfB.Party.Number = "18003020102", + [17].u.CCBSErase.RecallMode = 1, + [17].u.CCBSErase.CCBSReference = 102, + [17].u.CCBSErase.Reason = 3, + + [18].Function = Fac_CCBSErase, + [18].u.CCBSErase.InvokeID = 25, + [18].u.CCBSErase.Q931ie.Bc.Length = 2, + [18].u.CCBSErase.Q931ie.Bc.Contents = "JK", + [18].u.CCBSErase.AddressOfB.Party.Type = 8, + [18].u.CCBSErase.AddressOfB.Party.LengthOfNumber = 4, + [18].u.CCBSErase.AddressOfB.Party.Number = "1803", + [18].u.CCBSErase.RecallMode = 1, + [18].u.CCBSErase.CCBSReference = 102, + [18].u.CCBSErase.Reason = 3, + + [19].Function = Fac_CCBSRemoteUserFree, + [19].u.CCBSRemoteUserFree.InvokeID = 26, + [19].u.CCBSRemoteUserFree.Q931ie.Bc.Length = 2, + [19].u.CCBSRemoteUserFree.Q931ie.Bc.Contents = "JK", + [19].u.CCBSRemoteUserFree.AddressOfB.Party.Type = 8, + [19].u.CCBSRemoteUserFree.AddressOfB.Party.LengthOfNumber = 4, + [19].u.CCBSRemoteUserFree.AddressOfB.Party.Number = "1803", + [19].u.CCBSRemoteUserFree.RecallMode = 1, + [19].u.CCBSRemoteUserFree.CCBSReference = 102, + + [20].Function = Fac_CCBSCall, + [20].u.CCBSCall.InvokeID = 27, + [20].u.CCBSCall.CCBSReference = 115, + + [21].Function = Fac_CCBSStatusRequest, + [21].u.CCBSStatusRequest.InvokeID = 28, + [21].u.CCBSStatusRequest.ComponentType = FacComponent_Invoke, + [21].u.CCBSStatusRequest.Component.Invoke.Q931ie.Bc.Length = 2, + [21].u.CCBSStatusRequest.Component.Invoke.Q931ie.Bc.Contents = "JK", + [21].u.CCBSStatusRequest.Component.Invoke.RecallMode = 1, + [21].u.CCBSStatusRequest.Component.Invoke.CCBSReference = 102, + + [22].Function = Fac_CCBSStatusRequest, + [22].u.CCBSStatusRequest.InvokeID = 29, + [22].u.CCBSStatusRequest.ComponentType = FacComponent_Result, + [22].u.CCBSStatusRequest.Component.Result.Free = 1, + + [23].Function = Fac_CCBSBFree, + [23].u.CCBSBFree.InvokeID = 30, + [23].u.CCBSBFree.Q931ie.Bc.Length = 2, + [23].u.CCBSBFree.Q931ie.Bc.Contents = "JK", + [23].u.CCBSBFree.AddressOfB.Party.Type = 8, + [23].u.CCBSBFree.AddressOfB.Party.LengthOfNumber = 4, + [23].u.CCBSBFree.AddressOfB.Party.Number = "1803", + [23].u.CCBSBFree.RecallMode = 1, + [23].u.CCBSBFree.CCBSReference = 14, + + [24].Function = Fac_CCBSStopAlerting, + [24].u.CCBSStopAlerting.InvokeID = 31, + [24].u.CCBSStopAlerting.CCBSReference = 37, + + [25].Function = Fac_CCBSRequest, + [25].u.CCBSRequest.InvokeID = 32, + [25].u.CCBSRequest.ComponentType = FacComponent_Invoke, + [25].u.CCBSRequest.Component.Invoke.CallLinkageID = 57, + + [26].Function = Fac_CCBSRequest, + [26].u.CCBSRequest.InvokeID = 33, + [26].u.CCBSRequest.ComponentType = FacComponent_Result, + [26].u.CCBSRequest.Component.Result.RecallMode = 1, + [26].u.CCBSRequest.Component.Result.CCBSReference = 102, + + [27].Function = Fac_CCBSInterrogate, + [27].u.CCBSInterrogate.InvokeID = 34, + [27].u.CCBSInterrogate.ComponentType = FacComponent_Invoke, + [27].u.CCBSInterrogate.Component.Invoke.AParty.Type = 8, + [27].u.CCBSInterrogate.Component.Invoke.AParty.LengthOfNumber = 4, + [27].u.CCBSInterrogate.Component.Invoke.AParty.Number = "1803", + [27].u.CCBSInterrogate.Component.Invoke.CCBSReferencePresent = 1, + [27].u.CCBSInterrogate.Component.Invoke.CCBSReference = 76, + + [28].Function = Fac_CCBSInterrogate, + [28].u.CCBSInterrogate.InvokeID = 35, + [28].u.CCBSInterrogate.ComponentType = FacComponent_Invoke, + [28].u.CCBSInterrogate.Component.Invoke.AParty.Type = 8, + [28].u.CCBSInterrogate.Component.Invoke.AParty.LengthOfNumber = 4, + [28].u.CCBSInterrogate.Component.Invoke.AParty.Number = "1803", + + [29].Function = Fac_CCBSInterrogate, + [29].u.CCBSInterrogate.InvokeID = 36, + [29].u.CCBSInterrogate.ComponentType = FacComponent_Invoke, + [29].u.CCBSInterrogate.Component.Invoke.CCBSReferencePresent = 1, + [29].u.CCBSInterrogate.Component.Invoke.CCBSReference = 76, + + [30].Function = Fac_CCBSInterrogate, + [30].u.CCBSInterrogate.InvokeID = 37, + [30].u.CCBSInterrogate.ComponentType = FacComponent_Invoke, + + [31].Function = Fac_CCBSInterrogate, + [31].u.CCBSInterrogate.InvokeID = 38, + [31].u.CCBSInterrogate.ComponentType = FacComponent_Result, + [31].u.CCBSInterrogate.Component.Result.RecallMode = 1, + + [32].Function = Fac_CCBSInterrogate, + [32].u.CCBSInterrogate.InvokeID = 39, + [32].u.CCBSInterrogate.ComponentType = FacComponent_Result, + [32].u.CCBSInterrogate.Component.Result.RecallMode = 1, + [32].u.CCBSInterrogate.Component.Result.NumRecords = 1, + [32].u.CCBSInterrogate.Component.Result.CallDetails[0].CCBSReference = 12, + [32].u.CCBSInterrogate.Component.Result.CallDetails[0].Q931ie.Bc.Length = 2, + [32].u.CCBSInterrogate.Component.Result.CallDetails[0].Q931ie.Bc.Contents = "JK", + [32].u.CCBSInterrogate.Component.Result.CallDetails[0].AddressOfB.Party.Type = 8, + [32].u.CCBSInterrogate.Component.Result.CallDetails[0].AddressOfB.Party.LengthOfNumber = 4, + [32].u.CCBSInterrogate.Component.Result.CallDetails[0].AddressOfB.Party.Number = "1803", + [32].u.CCBSInterrogate.Component.Result.CallDetails[0].SubaddressOfA.Type = 1, + [32].u.CCBSInterrogate.Component.Result.CallDetails[0].SubaddressOfA.Length = 4, + [32].u.CCBSInterrogate.Component.Result.CallDetails[0].SubaddressOfA.u.Nsap = "6492", + + [33].Function = Fac_CCBSInterrogate, + [33].u.CCBSInterrogate.InvokeID = 40, + [33].u.CCBSInterrogate.ComponentType = FacComponent_Result, + [33].u.CCBSInterrogate.Component.Result.RecallMode = 1, + [33].u.CCBSInterrogate.Component.Result.NumRecords = 2, + [33].u.CCBSInterrogate.Component.Result.CallDetails[0].CCBSReference = 12, + [33].u.CCBSInterrogate.Component.Result.CallDetails[0].Q931ie.Bc.Length = 2, + [33].u.CCBSInterrogate.Component.Result.CallDetails[0].Q931ie.Bc.Contents = "JK", + [33].u.CCBSInterrogate.Component.Result.CallDetails[0].AddressOfB.Party.Type = 8, + [33].u.CCBSInterrogate.Component.Result.CallDetails[0].AddressOfB.Party.LengthOfNumber = 4, + [33].u.CCBSInterrogate.Component.Result.CallDetails[0].AddressOfB.Party.Number = "1803", + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].CCBSReference = 102, + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].Q931ie.Bc.Length = 2, + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].Q931ie.Bc.Contents = "LM", + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].AddressOfB.Party.Type = 8, + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].AddressOfB.Party.LengthOfNumber = 4, + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].AddressOfB.Party.Number = "6229", + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].AddressOfB.Subaddress.Type = 1, + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].AddressOfB.Subaddress.Length = 4, + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].AddressOfB.Subaddress.u.Nsap = "8592", + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].SubaddressOfA.Type = 1, + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].SubaddressOfA.Length = 4, + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].SubaddressOfA.u.Nsap = "6492", + + [34].Function = Fac_CCNRRequest, + [34].u.CCNRRequest.InvokeID = 512, + [34].u.CCNRRequest.ComponentType = FacComponent_Invoke, + [34].u.CCNRRequest.Component.Invoke.CallLinkageID = 57, + + [35].Function = Fac_CCNRRequest, + [35].u.CCNRRequest.InvokeID = 150, + [35].u.CCNRRequest.ComponentType = FacComponent_Result, + [35].u.CCNRRequest.Component.Result.RecallMode = 1, + [35].u.CCNRRequest.Component.Result.CCBSReference = 102, + + [36].Function = Fac_CCNRInterrogate, + [36].u.CCNRInterrogate.InvokeID = -129, + [36].u.CCNRInterrogate.ComponentType = FacComponent_Invoke, + + [37].Function = Fac_CCNRInterrogate, + [37].u.CCNRInterrogate.InvokeID = -3, + [37].u.CCNRInterrogate.ComponentType = FacComponent_Result, + [37].u.CCNRInterrogate.Component.Result.RecallMode = 1, + + [38].Function = Fac_CCBS_T_Call, + [38].u.EctExecute.InvokeID = 41, + + [39].Function = Fac_CCBS_T_Suspend, + [39].u.EctExecute.InvokeID = 42, + + [40].Function = Fac_CCBS_T_Resume, + [40].u.EctExecute.InvokeID = 43, + + [41].Function = Fac_CCBS_T_RemoteUserFree, + [41].u.EctExecute.InvokeID = 44, + + [42].Function = Fac_CCBS_T_Available, + [42].u.EctExecute.InvokeID = 45, + + [43].Function = Fac_CCBS_T_Request, + [43].u.CCBS_T_Request.InvokeID = 46, + [43].u.CCBS_T_Request.ComponentType = FacComponent_Invoke, + [43].u.CCBS_T_Request.Component.Invoke.Destination.Party.Type = 8, + [43].u.CCBS_T_Request.Component.Invoke.Destination.Party.LengthOfNumber = 4, + [43].u.CCBS_T_Request.Component.Invoke.Destination.Party.Number = "6229", + [43].u.CCBS_T_Request.Component.Invoke.Q931ie.Bc.Length = 2, + [43].u.CCBS_T_Request.Component.Invoke.Q931ie.Bc.Contents = "LM", + [43].u.CCBS_T_Request.Component.Invoke.RetentionSupported = 1, + [43].u.CCBS_T_Request.Component.Invoke.PresentationAllowedIndicatorPresent = 1, + [43].u.CCBS_T_Request.Component.Invoke.PresentationAllowedIndicator = 1, + [43].u.CCBS_T_Request.Component.Invoke.Originating.Party.Type = 8, + [43].u.CCBS_T_Request.Component.Invoke.Originating.Party.LengthOfNumber = 4, + [43].u.CCBS_T_Request.Component.Invoke.Originating.Party.Number = "9864", + + [44].Function = Fac_CCBS_T_Request, + [44].u.CCBS_T_Request.InvokeID = 47, + [44].u.CCBS_T_Request.ComponentType = FacComponent_Invoke, + [44].u.CCBS_T_Request.Component.Invoke.Destination.Party.Type = 8, + [44].u.CCBS_T_Request.Component.Invoke.Destination.Party.LengthOfNumber = 4, + [44].u.CCBS_T_Request.Component.Invoke.Destination.Party.Number = "6229", + [44].u.CCBS_T_Request.Component.Invoke.Q931ie.Bc.Length = 2, + [44].u.CCBS_T_Request.Component.Invoke.Q931ie.Bc.Contents = "LM", + [44].u.CCBS_T_Request.Component.Invoke.PresentationAllowedIndicatorPresent = 1, + [44].u.CCBS_T_Request.Component.Invoke.PresentationAllowedIndicator = 1, + [44].u.CCBS_T_Request.Component.Invoke.Originating.Party.Type = 8, + [44].u.CCBS_T_Request.Component.Invoke.Originating.Party.LengthOfNumber = 4, + [44].u.CCBS_T_Request.Component.Invoke.Originating.Party.Number = "9864", + + [45].Function = Fac_CCBS_T_Request, + [45].u.CCBS_T_Request.InvokeID = 48, + [45].u.CCBS_T_Request.ComponentType = FacComponent_Invoke, + [45].u.CCBS_T_Request.Component.Invoke.Destination.Party.Type = 8, + [45].u.CCBS_T_Request.Component.Invoke.Destination.Party.LengthOfNumber = 4, + [45].u.CCBS_T_Request.Component.Invoke.Destination.Party.Number = "6229", + [45].u.CCBS_T_Request.Component.Invoke.Q931ie.Bc.Length = 2, + [45].u.CCBS_T_Request.Component.Invoke.Q931ie.Bc.Contents = "LM", + [45].u.CCBS_T_Request.Component.Invoke.Originating.Party.Type = 8, + [45].u.CCBS_T_Request.Component.Invoke.Originating.Party.LengthOfNumber = 4, + [45].u.CCBS_T_Request.Component.Invoke.Originating.Party.Number = "9864", + + [46].Function = Fac_CCBS_T_Request, + [46].u.CCBS_T_Request.InvokeID = 49, + [46].u.CCBS_T_Request.ComponentType = FacComponent_Invoke, + [46].u.CCBS_T_Request.Component.Invoke.Destination.Party.Type = 8, + [46].u.CCBS_T_Request.Component.Invoke.Destination.Party.LengthOfNumber = 4, + [46].u.CCBS_T_Request.Component.Invoke.Destination.Party.Number = "6229", + [46].u.CCBS_T_Request.Component.Invoke.Q931ie.Bc.Length = 2, + [46].u.CCBS_T_Request.Component.Invoke.Q931ie.Bc.Contents = "LM", + [46].u.CCBS_T_Request.Component.Invoke.PresentationAllowedIndicatorPresent = 1, + [46].u.CCBS_T_Request.Component.Invoke.PresentationAllowedIndicator = 1, + + [47].Function = Fac_CCBS_T_Request, + [47].u.CCBS_T_Request.InvokeID = 50, + [47].u.CCBS_T_Request.ComponentType = FacComponent_Invoke, + [47].u.CCBS_T_Request.Component.Invoke.Destination.Party.Type = 8, + [47].u.CCBS_T_Request.Component.Invoke.Destination.Party.LengthOfNumber = 4, + [47].u.CCBS_T_Request.Component.Invoke.Destination.Party.Number = "6229", + [47].u.CCBS_T_Request.Component.Invoke.Q931ie.Bc.Length = 2, + [47].u.CCBS_T_Request.Component.Invoke.Q931ie.Bc.Contents = "LM", + + [48].Function = Fac_CCBS_T_Request, + [48].u.CCBS_T_Request.InvokeID = 51, + [48].u.CCBS_T_Request.ComponentType = FacComponent_Result, + [48].u.CCBS_T_Request.Component.Result.RetentionSupported = 1, + + [49].Function = Fac_CCNR_T_Request, + [49].u.CCNR_T_Request.InvokeID = 52, + [49].u.CCNR_T_Request.ComponentType = FacComponent_Invoke, + [49].u.CCNR_T_Request.Component.Invoke.Destination.Party.Type = 8, + [49].u.CCNR_T_Request.Component.Invoke.Destination.Party.LengthOfNumber = 4, + [49].u.CCNR_T_Request.Component.Invoke.Destination.Party.Number = "6229", + [49].u.CCNR_T_Request.Component.Invoke.Q931ie.Bc.Length = 2, + [49].u.CCNR_T_Request.Component.Invoke.Q931ie.Bc.Contents = "LM", + + [50].Function = Fac_CCNR_T_Request, + [50].u.CCNR_T_Request.InvokeID = 53, + [50].u.CCNR_T_Request.ComponentType = FacComponent_Result, + [50].u.CCNR_T_Request.Component.Result.RetentionSupported = 1, + + [51].Function = Fac_EctExecute, + [51].u.EctExecute.InvokeID = 54, + + [52].Function = Fac_ExplicitEctExecute, + [52].u.ExplicitEctExecute.InvokeID = 55, + [52].u.ExplicitEctExecute.LinkID = 23, + + [53].Function = Fac_RequestSubaddress, + [53].u.RequestSubaddress.InvokeID = 56, + + [54].Function = Fac_SubaddressTransfer, + [54].u.SubaddressTransfer.InvokeID = 57, + [54].u.SubaddressTransfer.Subaddress.Type = 1, + [54].u.SubaddressTransfer.Subaddress.Length = 4, + [54].u.SubaddressTransfer.Subaddress.u.Nsap = "6492", + + [55].Function = Fac_EctLinkIdRequest, + [55].u.EctLinkIdRequest.InvokeID = 58, + [55].u.EctLinkIdRequest.ComponentType = FacComponent_Invoke, + + [56].Function = Fac_EctLinkIdRequest, + [56].u.EctLinkIdRequest.InvokeID = 59, + [56].u.EctLinkIdRequest.ComponentType = FacComponent_Result, + [56].u.EctLinkIdRequest.Component.Result.LinkID = 76, + + [57].Function = Fac_EctInform, + [57].u.EctInform.InvokeID = 60, + [57].u.EctInform.Status = 1, + [57].u.EctInform.RedirectionPresent = 1, + [57].u.EctInform.Redirection.Type = 0, + [57].u.EctInform.Redirection.Unscreened.Type = 8, + [57].u.EctInform.Redirection.Unscreened.LengthOfNumber = 4, + [57].u.EctInform.Redirection.Unscreened.Number = "6229", + + [58].Function = Fac_EctInform, + [58].u.EctInform.InvokeID = 61, + [58].u.EctInform.Status = 1, + [58].u.EctInform.RedirectionPresent = 1, + [58].u.EctInform.Redirection.Type = 1, + + [59].Function = Fac_EctInform, + [59].u.EctInform.InvokeID = 62, + [59].u.EctInform.Status = 1, + [59].u.EctInform.RedirectionPresent = 1, + [59].u.EctInform.Redirection.Type = 2, + + [60].Function = Fac_EctInform, + [60].u.EctInform.InvokeID = 63, + [60].u.EctInform.Status = 1, + [60].u.EctInform.RedirectionPresent = 1, + [60].u.EctInform.Redirection.Type = 3, + [60].u.EctInform.Redirection.Unscreened.Type = 8, + [60].u.EctInform.Redirection.Unscreened.LengthOfNumber = 4, + [60].u.EctInform.Redirection.Unscreened.Number = "3340", + + [61].Function = Fac_EctInform, + [61].u.EctInform.InvokeID = 64, + [61].u.EctInform.Status = 1, + [61].u.EctInform.RedirectionPresent = 0, + + [62].Function = Fac_EctLoopTest, + [62].u.EctLoopTest.InvokeID = 65, + [62].u.EctLoopTest.ComponentType = FacComponent_Invoke, + [62].u.EctLoopTest.Component.Invoke.CallTransferID = 7, + + [63].Function = Fac_EctLoopTest, + [63].u.EctLoopTest.InvokeID = 66, + [63].u.EctLoopTest.ComponentType = FacComponent_Result, + [63].u.EctLoopTest.Component.Result.LoopResult = 2, + + [64].Function = Fac_ActivationDiversion, + [64].u.ActivationDiversion.InvokeID = 67, + [64].u.ActivationDiversion.ComponentType = FacComponent_Invoke, + [64].u.ActivationDiversion.Component.Invoke.Procedure = 2, + [64].u.ActivationDiversion.Component.Invoke.BasicService = 3, + [64].u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.Type = 4, + [64].u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.LengthOfNumber = 4, + [64].u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.Number = "1803", + [64].u.ActivationDiversion.Component.Invoke.ServedUser.Type = 4, + [64].u.ActivationDiversion.Component.Invoke.ServedUser.LengthOfNumber = 4, + [64].u.ActivationDiversion.Component.Invoke.ServedUser.Number = "5398", + + [65].Function = Fac_ActivationDiversion, + [65].u.ActivationDiversion.InvokeID = 68, + [65].u.ActivationDiversion.ComponentType = FacComponent_Invoke, + [65].u.ActivationDiversion.Component.Invoke.Procedure = 1, + [65].u.ActivationDiversion.Component.Invoke.BasicService = 5, + [65].u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.Type = 4, + [65].u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.LengthOfNumber = 4, + [65].u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.Number = "1803", + + [66].Function = Fac_ActivationDiversion, + [66].u.ActivationDiversion.InvokeID = 69, + [66].u.ActivationDiversion.ComponentType = FacComponent_Result, + + [67].Function = Fac_DeactivationDiversion, + [67].u.DeactivationDiversion.InvokeID = 70, + [67].u.DeactivationDiversion.ComponentType = FacComponent_Invoke, + [67].u.DeactivationDiversion.Component.Invoke.Procedure = 1, + [67].u.DeactivationDiversion.Component.Invoke.BasicService = 5, + + [68].Function = Fac_DeactivationDiversion, + [68].u.DeactivationDiversion.InvokeID = 71, + [68].u.DeactivationDiversion.ComponentType = FacComponent_Result, + + [69].Function = Fac_ActivationStatusNotificationDiv, + [69].u.ActivationStatusNotificationDiv.InvokeID = 72, + [69].u.ActivationStatusNotificationDiv.Procedure = 1, + [69].u.ActivationStatusNotificationDiv.BasicService = 5, + [69].u.ActivationStatusNotificationDiv.ForwardedTo.Party.Type = 4, + [69].u.ActivationStatusNotificationDiv.ForwardedTo.Party.LengthOfNumber = 4, + [69].u.ActivationStatusNotificationDiv.ForwardedTo.Party.Number = "1803", + + [70].Function = Fac_DeactivationStatusNotificationDiv, + [70].u.DeactivationStatusNotificationDiv.InvokeID = 73, + [70].u.DeactivationStatusNotificationDiv.Procedure = 1, + [70].u.DeactivationStatusNotificationDiv.BasicService = 5, + + [71].Function = Fac_InterrogationDiversion, + [71].u.InterrogationDiversion.InvokeID = 74, + [71].u.InterrogationDiversion.ComponentType = FacComponent_Invoke, + [71].u.InterrogationDiversion.Component.Invoke.Procedure = 1, + [71].u.InterrogationDiversion.Component.Invoke.BasicService = 5, + + [72].Function = Fac_InterrogationDiversion, + [72].u.InterrogationDiversion.InvokeID = 75, + [72].u.InterrogationDiversion.ComponentType = FacComponent_Invoke, + [72].u.InterrogationDiversion.Component.Invoke.Procedure = 1, + + [73].Function = Fac_InterrogationDiversion, + [73].u.InterrogationDiversion.InvokeID = 76, + [73].u.InterrogationDiversion.ComponentType = FacComponent_Result, + [73].u.InterrogationDiversion.Component.Result.NumRecords = 2, + [73].u.InterrogationDiversion.Component.Result.List[0].Procedure = 2, + [73].u.InterrogationDiversion.Component.Result.List[0].BasicService = 5, + [73].u.InterrogationDiversion.Component.Result.List[0].ForwardedTo.Party.Type = 4, + [73].u.InterrogationDiversion.Component.Result.List[0].ForwardedTo.Party.LengthOfNumber = 4, + [73].u.InterrogationDiversion.Component.Result.List[0].ForwardedTo.Party.Number = "1803", + [73].u.InterrogationDiversion.Component.Result.List[1].Procedure = 1, + [73].u.InterrogationDiversion.Component.Result.List[1].BasicService = 3, + [73].u.InterrogationDiversion.Component.Result.List[1].ForwardedTo.Party.Type = 4, + [73].u.InterrogationDiversion.Component.Result.List[1].ForwardedTo.Party.LengthOfNumber = 4, + [73].u.InterrogationDiversion.Component.Result.List[1].ForwardedTo.Party.Number = "1903", + [73].u.InterrogationDiversion.Component.Result.List[1].ServedUser.Type = 4, + [73].u.InterrogationDiversion.Component.Result.List[1].ServedUser.LengthOfNumber = 4, + [73].u.InterrogationDiversion.Component.Result.List[1].ServedUser.Number = "5398", + + [74].Function = Fac_DiversionInformation, + [74].u.DiversionInformation.InvokeID = 77, + [74].u.DiversionInformation.DiversionReason = 3, + [74].u.DiversionInformation.BasicService = 5, + [74].u.DiversionInformation.ServedUserSubaddress.Type = 1, + [74].u.DiversionInformation.ServedUserSubaddress.Length = 4, + [74].u.DiversionInformation.ServedUserSubaddress.u.Nsap = "6492", + [74].u.DiversionInformation.CallingAddressPresent = 1, + [74].u.DiversionInformation.CallingAddress.Type = 0, + [74].u.DiversionInformation.CallingAddress.Address.ScreeningIndicator = 3, + [74].u.DiversionInformation.CallingAddress.Address.Party.Type = 4, + [74].u.DiversionInformation.CallingAddress.Address.Party.LengthOfNumber = 4, + [74].u.DiversionInformation.CallingAddress.Address.Party.Number = "1803", + [74].u.DiversionInformation.OriginalCalledPresent = 1, + [74].u.DiversionInformation.OriginalCalled.Type = 1, + [74].u.DiversionInformation.LastDivertingPresent = 1, + [74].u.DiversionInformation.LastDiverting.Type = 2, + [74].u.DiversionInformation.LastDivertingReasonPresent = 1, + [74].u.DiversionInformation.LastDivertingReason = 3, + [74].u.DiversionInformation.UserInfo.Length = 5, + [74].u.DiversionInformation.UserInfo.Contents = "79828", + + [75].Function = Fac_DiversionInformation, + [75].u.DiversionInformation.InvokeID = 78, + [75].u.DiversionInformation.DiversionReason = 3, + [75].u.DiversionInformation.BasicService = 5, + [75].u.DiversionInformation.CallingAddressPresent = 1, + [75].u.DiversionInformation.CallingAddress.Type = 1, + [75].u.DiversionInformation.OriginalCalledPresent = 1, + [75].u.DiversionInformation.OriginalCalled.Type = 2, + [75].u.DiversionInformation.LastDivertingPresent = 1, + [75].u.DiversionInformation.LastDiverting.Type = 1, + + [76].Function = Fac_DiversionInformation, + [76].u.DiversionInformation.InvokeID = 79, + [76].u.DiversionInformation.DiversionReason = 2, + [76].u.DiversionInformation.BasicService = 3, + [76].u.DiversionInformation.CallingAddressPresent = 1, + [76].u.DiversionInformation.CallingAddress.Type = 2, + + [77].Function = Fac_DiversionInformation, + [77].u.DiversionInformation.InvokeID = 80, + [77].u.DiversionInformation.DiversionReason = 3, + [77].u.DiversionInformation.BasicService = 5, + [77].u.DiversionInformation.CallingAddressPresent = 1, + [77].u.DiversionInformation.CallingAddress.Type = 3, + [77].u.DiversionInformation.CallingAddress.Address.ScreeningIndicator = 2, + [77].u.DiversionInformation.CallingAddress.Address.Party.Type = 4, + [77].u.DiversionInformation.CallingAddress.Address.Party.LengthOfNumber = 4, + [77].u.DiversionInformation.CallingAddress.Address.Party.Number = "1803", + + [78].Function = Fac_DiversionInformation, + [78].u.DiversionInformation.InvokeID = 81, + [78].u.DiversionInformation.DiversionReason = 2, + [78].u.DiversionInformation.BasicService = 4, + [78].u.DiversionInformation.UserInfo.Length = 5, + [78].u.DiversionInformation.UserInfo.Contents = "79828", + + [79].Function = Fac_DiversionInformation, + [79].u.DiversionInformation.InvokeID = 82, + [79].u.DiversionInformation.DiversionReason = 2, + [79].u.DiversionInformation.BasicService = 4, + + [80].Function = Fac_CallDeflection, + [80].u.CallDeflection.InvokeID = 83, + [80].u.CallDeflection.ComponentType = FacComponent_Invoke, + [80].u.CallDeflection.Component.Invoke.Deflection.Party.Type = 4, + [80].u.CallDeflection.Component.Invoke.Deflection.Party.LengthOfNumber = 4, + [80].u.CallDeflection.Component.Invoke.Deflection.Party.Number = "1803", + [80].u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUserPresent = 1, + [80].u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUser = 1, + + [81].Function = Fac_CallDeflection, + [81].u.CallDeflection.InvokeID = 84, + [81].u.CallDeflection.ComponentType = FacComponent_Invoke, + [81].u.CallDeflection.Component.Invoke.Deflection.Party.Type = 4, + [81].u.CallDeflection.Component.Invoke.Deflection.Party.LengthOfNumber = 4, + [81].u.CallDeflection.Component.Invoke.Deflection.Party.Number = "1803", + [81].u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUserPresent = 1, + [81].u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUser = 0, + + [82].Function = Fac_CallDeflection, + [82].u.CallDeflection.InvokeID = 85, + [82].u.CallDeflection.ComponentType = FacComponent_Invoke, + [82].u.CallDeflection.Component.Invoke.Deflection.Party.Type = 4, + [82].u.CallDeflection.Component.Invoke.Deflection.Party.LengthOfNumber = 4, + [82].u.CallDeflection.Component.Invoke.Deflection.Party.Number = "1803", + + [83].Function = Fac_CallDeflection, + [83].u.CallDeflection.InvokeID = 86, + [83].u.CallDeflection.ComponentType = FacComponent_Result, + + [84].Function = Fac_CallRerouteing, + [84].u.CallRerouteing.InvokeID = 87, + [84].u.CallRerouteing.ComponentType = FacComponent_Invoke, + [84].u.CallRerouteing.Component.Invoke.ReroutingReason = 3, + [84].u.CallRerouteing.Component.Invoke.ReroutingCounter = 2, + [84].u.CallRerouteing.Component.Invoke.CalledAddress.Party.Type = 4, + [84].u.CallRerouteing.Component.Invoke.CalledAddress.Party.LengthOfNumber = 4, + [84].u.CallRerouteing.Component.Invoke.CalledAddress.Party.Number = "1803", + [84].u.CallRerouteing.Component.Invoke.Q931ie.Bc.Length = 2, + [84].u.CallRerouteing.Component.Invoke.Q931ie.Bc.Contents = "RT", + [84].u.CallRerouteing.Component.Invoke.Q931ie.Hlc.Length = 3, + [84].u.CallRerouteing.Component.Invoke.Q931ie.Hlc.Contents = "RTG", + [84].u.CallRerouteing.Component.Invoke.Q931ie.Llc.Length = 2, + [84].u.CallRerouteing.Component.Invoke.Q931ie.Llc.Contents = "MY", + [84].u.CallRerouteing.Component.Invoke.Q931ie.UserInfo.Length = 5, + [84].u.CallRerouteing.Component.Invoke.Q931ie.UserInfo.Contents = "YEHAW", + [84].u.CallRerouteing.Component.Invoke.LastRerouting.Type = 1, + [84].u.CallRerouteing.Component.Invoke.SubscriptionOption = 2, + [84].u.CallRerouteing.Component.Invoke.CallingPartySubaddress.Type = 1, + [84].u.CallRerouteing.Component.Invoke.CallingPartySubaddress.Length = 4, + [84].u.CallRerouteing.Component.Invoke.CallingPartySubaddress.u.Nsap = "6492", + + [85].Function = Fac_CallRerouteing, + [85].u.CallRerouteing.InvokeID = 88, + [85].u.CallRerouteing.ComponentType = FacComponent_Invoke, + [85].u.CallRerouteing.Component.Invoke.ReroutingReason = 3, + [85].u.CallRerouteing.Component.Invoke.ReroutingCounter = 2, + [85].u.CallRerouteing.Component.Invoke.CalledAddress.Party.Type = 4, + [85].u.CallRerouteing.Component.Invoke.CalledAddress.Party.LengthOfNumber = 4, + [85].u.CallRerouteing.Component.Invoke.CalledAddress.Party.Number = "1803", + [85].u.CallRerouteing.Component.Invoke.Q931ie.Bc.Length = 2, + [85].u.CallRerouteing.Component.Invoke.Q931ie.Bc.Contents = "RT", + [85].u.CallRerouteing.Component.Invoke.LastRerouting.Type = 1, + [85].u.CallRerouteing.Component.Invoke.SubscriptionOption = 2, + + [86].Function = Fac_CallRerouteing, + [86].u.CallRerouteing.InvokeID = 89, + [86].u.CallRerouteing.ComponentType = FacComponent_Invoke, + [86].u.CallRerouteing.Component.Invoke.ReroutingReason = 3, + [86].u.CallRerouteing.Component.Invoke.ReroutingCounter = 2, + [86].u.CallRerouteing.Component.Invoke.CalledAddress.Party.Type = 4, + [86].u.CallRerouteing.Component.Invoke.CalledAddress.Party.LengthOfNumber = 4, + [86].u.CallRerouteing.Component.Invoke.CalledAddress.Party.Number = "1803", + [86].u.CallRerouteing.Component.Invoke.Q931ie.Bc.Length = 2, + [86].u.CallRerouteing.Component.Invoke.Q931ie.Bc.Contents = "RT", + [86].u.CallRerouteing.Component.Invoke.LastRerouting.Type = 2, + + [87].Function = Fac_CallRerouteing, + [87].u.CallRerouteing.InvokeID = 90, + [87].u.CallRerouteing.ComponentType = FacComponent_Result, + + [88].Function = Fac_InterrogateServedUserNumbers, + [88].u.InterrogateServedUserNumbers.InvokeID = 91, + [88].u.InterrogateServedUserNumbers.ComponentType = FacComponent_Invoke, + + [89].Function = Fac_InterrogateServedUserNumbers, + [89].u.InterrogateServedUserNumbers.InvokeID = 92, + [89].u.InterrogateServedUserNumbers.ComponentType = FacComponent_Result, + [89].u.InterrogateServedUserNumbers.Component.Result.NumRecords = 2, + [89].u.InterrogateServedUserNumbers.Component.Result.List[0].Type = 4, + [89].u.InterrogateServedUserNumbers.Component.Result.List[0].LengthOfNumber = 4, + [89].u.InterrogateServedUserNumbers.Component.Result.List[0].Number = "1803", + [89].u.InterrogateServedUserNumbers.Component.Result.List[1].Type = 4, + [89].u.InterrogateServedUserNumbers.Component.Result.List[1].LengthOfNumber = 4, + [89].u.InterrogateServedUserNumbers.Component.Result.List[1].Number = "5786", + + [90].Function = Fac_DivertingLegInformation1, + [90].u.DivertingLegInformation1.InvokeID = 93, + [90].u.DivertingLegInformation1.DiversionReason = 4, + [90].u.DivertingLegInformation1.SubscriptionOption = 1, + [90].u.DivertingLegInformation1.DivertedToPresent = 1, + [90].u.DivertingLegInformation1.DivertedTo.Type = 2, + + [91].Function = Fac_DivertingLegInformation1, + [91].u.DivertingLegInformation1.InvokeID = 94, + [91].u.DivertingLegInformation1.DiversionReason = 4, + [91].u.DivertingLegInformation1.SubscriptionOption = 1, + + [92].Function = Fac_DivertingLegInformation2, + [92].u.DivertingLegInformation2.InvokeID = 95, + [92].u.DivertingLegInformation2.DiversionCounter = 3, + [92].u.DivertingLegInformation2.DiversionReason = 2, + [92].u.DivertingLegInformation2.DivertingPresent = 1, + [92].u.DivertingLegInformation2.Diverting.Type = 2, + [92].u.DivertingLegInformation2.OriginalCalledPresent = 1, + [92].u.DivertingLegInformation2.OriginalCalled.Type = 1, + + [93].Function = Fac_DivertingLegInformation2, + [93].u.DivertingLegInformation2.InvokeID = 96, + [93].u.DivertingLegInformation2.DiversionCounter = 3, + [93].u.DivertingLegInformation2.DiversionReason = 2, + [93].u.DivertingLegInformation2.OriginalCalledPresent = 1, + [93].u.DivertingLegInformation2.OriginalCalled.Type = 1, + + [94].Function = Fac_DivertingLegInformation2, + [94].u.DivertingLegInformation2.InvokeID = 97, + [94].u.DivertingLegInformation2.DiversionCounter = 1, + [94].u.DivertingLegInformation2.DiversionReason = 2, + + [95].Function = Fac_DivertingLegInformation3, + [95].u.DivertingLegInformation3.InvokeID = 98, + [95].u.DivertingLegInformation3.PresentationAllowedIndicator = 1, +/* *INDENT-ON* */ +}; +#endif /* defined(AST_MISDN_ENHANCEMENTS) && defined(CCBS_TEST_MESSAGES) */ + static char *handle_cli_misdn_send_facility(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { char *channame; @@ -2275,6 +5129,7 @@ static char *handle_cli_misdn_send_facility(struct ast_cli_entry *e, int cmd, st int port; char *served_nr; struct misdn_bchannel dummy, *bc=&dummy; + unsigned max_len; switch (cmd) { case CLI_INIT: @@ -2309,12 +5164,39 @@ static char *handle_cli_misdn_send_facility(struct ast_cli_entry *e, int cmd, st return 0; } - if (strlen(nr) >= 15) { - ast_verbose("Sending CD with nr %s to %s failed: Number too long (up to 15 digits are allowed).\n", nr, channame); +#if defined(AST_MISDN_ENHANCEMENTS) + max_len = sizeof(tmp->bc->fac_out.u.CallDeflection.Component.Invoke.Deflection.Party.Number) - 1; + if (max_len < strlen(nr)) { + ast_verbose("Sending CD with nr %s to %s failed: Number too long (up to %u digits are allowed).\n", + nr, channame, max_len); + return 0; + } + tmp->bc->fac_out.Function = Fac_CallDeflection; + tmp->bc->fac_out.u.CallDeflection.InvokeID = ++misdn_invoke_id; + tmp->bc->fac_out.u.CallDeflection.ComponentType = FacComponent_Invoke; + tmp->bc->fac_out.u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUserPresent = 1; + tmp->bc->fac_out.u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUser = 0; + tmp->bc->fac_out.u.CallDeflection.Component.Invoke.Deflection.Party.Type = 0;/* unknown */ + tmp->bc->fac_out.u.CallDeflection.Component.Invoke.Deflection.Party.LengthOfNumber = strlen(nr); + strcpy((char *) tmp->bc->fac_out.u.CallDeflection.Component.Invoke.Deflection.Party.Number, nr); + tmp->bc->fac_out.u.CallDeflection.Component.Invoke.Deflection.Subaddress.Length = 0; + +#else /* !defined(AST_MISDN_ENHANCEMENTS) */ + + max_len = sizeof(tmp->bc->fac_out.u.CDeflection.DeflectedToNumber) - 1; + if (max_len < strlen(nr)) { + ast_verbose("Sending CD with nr %s to %s failed: Number too long (up to %u digits are allowed).\n", + nr, channame, max_len); return 0; } tmp->bc->fac_out.Function = Fac_CD; - ast_copy_string((char *) tmp->bc->fac_out.u.CDeflection.DeflectedToNumber, nr, sizeof(tmp->bc->fac_out.u.CDeflection.DeflectedToNumber)); + tmp->bc->fac_out.u.CDeflection.PresentationAllowed = 0; + //tmp->bc->fac_out.u.CDeflection.DeflectedToSubaddress[0] = 0; + strcpy((char *) tmp->bc->fac_out.u.CDeflection.DeflectedToNumber, nr); +#endif /* !defined(AST_MISDN_ENHANCEMENTS) */ + + /* Send message */ + print_facility(&tmp->bc->fac_out, tmp->bc); misdn_lib_send_event(tmp->bc, EVENT_FACILITY); } else if (strstr(a->argv[3], "CFActivate")) { if (a->argc < 7) { @@ -2329,12 +5211,35 @@ static char *handle_cli_misdn_send_facility(struct ast_cli_entry *e, int cmd, st ast_verbose("Sending CFActivate Port:(%d) FromNr. (%s) to Nr. (%s)\n", port, served_nr, nr); +#if defined(AST_MISDN_ENHANCEMENTS) + bc->fac_out.Function = Fac_ActivationDiversion; + bc->fac_out.u.ActivationDiversion.InvokeID = ++misdn_invoke_id; + bc->fac_out.u.ActivationDiversion.ComponentType = FacComponent_Invoke; + bc->fac_out.u.ActivationDiversion.Component.Invoke.BasicService = 0;/* allServices */ + bc->fac_out.u.ActivationDiversion.Component.Invoke.Procedure = 0;/* cfu (Call Forward Unconditional) */ + ast_copy_string((char *) bc->fac_out.u.ActivationDiversion.Component.Invoke.ServedUser.Number, + served_nr, sizeof(bc->fac_out.u.ActivationDiversion.Component.Invoke.ServedUser.Number)); + bc->fac_out.u.ActivationDiversion.Component.Invoke.ServedUser.LengthOfNumber = + strlen((char *) bc->fac_out.u.ActivationDiversion.Component.Invoke.ServedUser.Number); + bc->fac_out.u.ActivationDiversion.Component.Invoke.ServedUser.Type = 0;/* unknown */ + ast_copy_string((char *) bc->fac_out.u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.Number, + nr, sizeof(bc->fac_out.u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.Number)); + bc->fac_out.u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.LengthOfNumber = + strlen((char *) bc->fac_out.u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.Number); + bc->fac_out.u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.Type = 0;/* unknown */ + bc->fac_out.u.ActivationDiversion.Component.Invoke.ForwardedTo.Subaddress.Length = 0; + +#else /* !defined(AST_MISDN_ENHANCEMENTS) */ + bc->fac_out.Function = Fac_CFActivate; bc->fac_out.u.CFActivate.BasicService = 0; /* All Services */ bc->fac_out.u.CFActivate.Procedure = 0; /* Unconditional */ ast_copy_string((char *) bc->fac_out.u.CFActivate.ServedUserNumber, served_nr, sizeof(bc->fac_out.u.CFActivate.ServedUserNumber)); ast_copy_string((char *) bc->fac_out.u.CFActivate.ForwardedToNumber, nr, sizeof(bc->fac_out.u.CFActivate.ForwardedToNumber)); +#endif /* !defined(AST_MISDN_ENHANCEMENTS) */ + /* Send message */ + print_facility(&bc->fac_out, bc); misdn_lib_send_event(bc, EVENT_FACILITY); } else if (strstr(a->argv[3], "CFDeactivate")) { if (a->argc < 6) { @@ -2347,12 +5252,94 @@ static char *handle_cli_misdn_send_facility(struct ast_cli_entry *e, int cmd, st misdn_make_dummy(bc, port, 0, misdn_lib_port_is_nt(port), 0); ast_verbose("Sending CFDeactivate Port:(%d) FromNr. (%s)\n", port, served_nr); +#if defined(AST_MISDN_ENHANCEMENTS) + bc->fac_out.Function = Fac_DeactivationDiversion; + bc->fac_out.u.DeactivationDiversion.InvokeID = ++misdn_invoke_id; + bc->fac_out.u.DeactivationDiversion.ComponentType = FacComponent_Invoke; + bc->fac_out.u.DeactivationDiversion.Component.Invoke.BasicService = 0;/* allServices */ + bc->fac_out.u.DeactivationDiversion.Component.Invoke.Procedure = 0;/* cfu (Call Forward Unconditional) */ + ast_copy_string((char *) bc->fac_out.u.DeactivationDiversion.Component.Invoke.ServedUser.Number, + served_nr, sizeof(bc->fac_out.u.DeactivationDiversion.Component.Invoke.ServedUser.Number)); + bc->fac_out.u.DeactivationDiversion.Component.Invoke.ServedUser.LengthOfNumber = + strlen((char *) bc->fac_out.u.DeactivationDiversion.Component.Invoke.ServedUser.Number); + bc->fac_out.u.DeactivationDiversion.Component.Invoke.ServedUser.Type = 0;/* unknown */ + +#else /* !defined(AST_MISDN_ENHANCEMENTS) */ + bc->fac_out.Function = Fac_CFDeactivate; bc->fac_out.u.CFDeactivate.BasicService = 0; /* All Services */ bc->fac_out.u.CFDeactivate.Procedure = 0; /* Unconditional */ ast_copy_string((char *) bc->fac_out.u.CFActivate.ServedUserNumber, served_nr, sizeof(bc->fac_out.u.CFActivate.ServedUserNumber)); +#endif /* !defined(AST_MISDN_ENHANCEMENTS) */ + /* Send message */ + print_facility(&bc->fac_out, bc); misdn_lib_send_event(bc, EVENT_FACILITY); +#if defined(AST_MISDN_ENHANCEMENTS) && defined(CCBS_TEST_MESSAGES) + } else if (strstr(a->argv[3], "test")) { + int msg_number; + + if (a->argc < 5) { + ast_verbose("test (<port> [<msg#>]) | (<channel-name> <msg#>)\n\n"); + return 0; + } + port = atoi(a->argv[4]); + + channame = argv[4]; + tmp = get_chan_by_ast_name(channame); + if (tmp) { + /* We are going to send this FACILITY message out on an existing connection */ + msg_number = atoi(argv[5]); + if (msg_number < ARRAY_LEN(Fac_Msgs)) { + tmp->bc->fac_out = Fac_Msgs[msg_number]; + + /* Send message */ + print_facility(&tmp->bc->fac_out, tmp->bc); + misdn_lib_send_event(tmp->bc, EVENT_FACILITY); + } else { + ast_verbose("test <channel-name> <msg#>\n\n"); + } + } else if (a->argc < 6) { + for (msg_number = 0; msg_number < ARRAY_LEN(Fac_Msgs); ++msg_number) { + misdn_make_dummy(bc, port, 0, misdn_lib_port_is_nt(port), 0); + bc->fac_out = Fac_Msgs[msg_number]; + + /* Send message */ + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_FACILITY); + sleep(1); + } + } else { + msg_number = atoi(a->argv[5]); + if (msg_number < ARRAY_LEN(Fac_Msgs)) { + misdn_make_dummy(bc, port, 0, misdn_lib_port_is_nt(port), 0); + bc->fac_out = Fac_Msgs[msg_number]; + + /* Send message */ + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_FACILITY); + } else { + ast_verbose("test <port> [<msg#>]\n\n"); + } + } + } else if (strstr(argv[3], "register")) { + if (argc < 5) { + ast_cli(fd, "register <port>\n\n"); + return 0; + } + port = atoi(argv[4]); + + bc = misdn_lib_get_register_bc(port); + if (!bc) { + ast_cli(fd, "Could not allocate REGISTER bc struct\n\n"); + return 0; + } + bc->fac_out = Fac_Msgs[45]; + + /* Send message */ + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_REGISTER); +#endif /* defined(AST_MISDN_ENHANCEMENTS) && defined(CCBS_TEST_MESSAGES) */ } return CLI_SUCCESS; @@ -2967,7 +5954,31 @@ static int read_config(struct chan_list *ch) /*! * \internal - * \brief Notify peer that the connected line has changed. + * \brief Send a connected line update to the other channel + * + * \param ast Current Asterisk channel + * \param id Party id information to send to the other side + * \param source Why are we sending this update + * + * \return Nothing + */ +static void misdn_queue_connected_line_update(struct ast_channel *ast, const struct misdn_party_id *id, enum AST_CONNECTED_LINE_UPDATE_SOURCE source) +{ + struct ast_party_connected_line connected; + + ast_party_connected_line_init(&connected); + connected.id.number = (char *) id->number; + connected.id.number_type = misdn_to_ast_ton(id->number_type) + | misdn_to_ast_plan(id->number_plan); + connected.id.number_presentation = misdn_to_ast_pres(id->presentation) + | misdn_to_ast_screen(id->screening); + connected.source = source; + ast_channel_queue_connected_line_update(ast, &connected); +} + +/*! + * \internal + * \brief Get the connected line information out of the Asterisk channel. * * \param ast Current Asterisk channel * \param bc Associated B channel @@ -2975,7 +5986,7 @@ static int read_config(struct chan_list *ch) * * \return Nothing */ -static void misdn_update_connected_line(struct ast_channel *ast, struct misdn_bchannel *bc, int originator) +static void misdn_get_connected_line(struct ast_channel *ast, struct misdn_bchannel *bc, int originator) { int number_type; @@ -3020,6 +6031,90 @@ static void misdn_update_connected_line(struct ast_channel *ast, struct misdn_bc /*! * \internal + * \brief Notify peer that the connected line has changed. + * + * \param ast Current Asterisk channel + * \param bc Associated B channel + * \param originator Who originally created this channel. ORG_AST or ORG_MISDN + * + * \return Nothing + */ +static void misdn_update_connected_line(struct ast_channel *ast, struct misdn_bchannel *bc, int originator) +{ + int Is_PTMP; + struct chan_list *ch; + + misdn_get_connected_line(ast, bc, originator); + if (originator == ORG_MISDN) { + bc->redirecting.to = bc->connected; + } else { + bc->redirecting.to = bc->caller; + } + + Is_PTMP = !misdn_lib_is_ptp(bc->port); + switch (ast->connected.source) { + case AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER: + case AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER: + ch = MISDN_ASTERISK_TECH_PVT(ast); + if (ch->state == MISDN_CONNECTED + || originator != ORG_MISDN) { + if (Is_PTMP) { + /* Send NOTIFY(transfer-active, redirecting.to data) */ + bc->redirecting.to_changed = 1; + bc->notify_description_code = mISDN_NOTIFY_CODE_CALL_TRANSFER_ACTIVE; + misdn_lib_send_event(bc, EVENT_NOTIFY); +#if defined(AST_MISDN_ENHANCEMENTS) + } else { + /* Send EctInform(transfer-active, redirecting.to data) */ + bc->fac_out.Function = Fac_EctInform; + bc->fac_out.u.EctInform.InvokeID = ++misdn_invoke_id; + bc->fac_out.u.EctInform.Status = 1;/* active */ + if (bc->redirecting.to.number[0]) { + misdn_PresentedNumberUnscreened_fill(&bc->fac_out.u.EctInform.Redirection, + &bc->redirecting.to); + bc->fac_out.u.EctInform.RedirectionPresent = 1; + } else { + bc->fac_out.u.EctInform.RedirectionPresent = 0; + } + + /* Send message */ + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_FACILITY); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + } + } + break; + case AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER_ALERTING: + if (Is_PTMP) { + /* Send NOTIFY(transfer-alerting, redirecting.to data) */ + bc->redirecting.to_changed = 1; + bc->notify_description_code = mISDN_NOTIFY_CODE_CALL_TRANSFER_ALERTING; + misdn_lib_send_event(bc, EVENT_NOTIFY); +#if defined(AST_MISDN_ENHANCEMENTS) + } else { + /* Send EctInform(transfer-alerting, redirecting.to data) */ + bc->fac_out.Function = Fac_EctInform; + bc->fac_out.u.EctInform.InvokeID = ++misdn_invoke_id; + bc->fac_out.u.EctInform.Status = 0;/* alerting */ + if (bc->redirecting.to.number[0]) { + misdn_PresentedNumberUnscreened_fill(&bc->fac_out.u.EctInform.Redirection, + &bc->redirecting.to); + bc->fac_out.u.EctInform.RedirectionPresent = 1; + } else { + bc->fac_out.u.EctInform.RedirectionPresent = 0; + } + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_FACILITY); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + } + break; + default: + break; + } +} + +/*! + * \internal * \brief Copy the redirecting information out of the Asterisk channel * * \param bc Associated B channel @@ -3035,7 +6130,98 @@ static void misdn_copy_redirecting_from_ast(struct misdn_bchannel *bc, struct as bc->redirecting.from.screening = ast_to_misdn_screen(ast->redirecting.from.number_presentation); bc->redirecting.from.number_type = ast_to_misdn_ton(ast->redirecting.from.number_type); bc->redirecting.from.number_plan = ast_to_misdn_plan(ast->redirecting.from.number_type); + + ast_copy_string(bc->redirecting.to.name, S_OR(ast->redirecting.to.name, ""), sizeof(bc->redirecting.to.name)); + ast_copy_string(bc->redirecting.to.number, S_OR(ast->redirecting.to.number, ""), sizeof(bc->redirecting.to.number)); + bc->redirecting.to.presentation = ast_to_misdn_pres(ast->redirecting.to.number_presentation); + bc->redirecting.to.screening = ast_to_misdn_screen(ast->redirecting.to.number_presentation); + bc->redirecting.to.number_type = ast_to_misdn_ton(ast->redirecting.to.number_type); + bc->redirecting.to.number_plan = ast_to_misdn_plan(ast->redirecting.to.number_type); + bc->redirecting.reason = ast_to_misdn_reason(ast->redirecting.reason); + bc->redirecting.count = ast->redirecting.count; +} + +/*! + * \internal + * \brief Copy the redirecting info into the Asterisk channel + * + * \param ast Current Asterisk channel + * \param redirect Associated B channel redirecting info + * + * \return Nothing + */ +static void misdn_copy_redirecting_to_ast(struct ast_channel *ast, const struct misdn_party_redirecting *redirect) +{ + struct ast_party_redirecting redirecting; + + ast_party_redirecting_set_init(&redirecting, &ast->redirecting); + + redirecting.from.number = (char *) redirect->from.number; + redirecting.from.number_type = + misdn_to_ast_ton(redirect->from.number_type) + | misdn_to_ast_plan(redirect->from.number_plan); + redirecting.from.number_presentation = + misdn_to_ast_pres(redirect->from.presentation) + | misdn_to_ast_screen(redirect->from.screening); + + redirecting.to.number = (char *) redirect->to.number; + redirecting.to.number_type = + misdn_to_ast_ton(redirect->to.number_type) + | misdn_to_ast_plan(redirect->to.number_plan); + redirecting.to.number_presentation = + misdn_to_ast_pres(redirect->to.presentation) + | misdn_to_ast_screen(redirect->to.screening); + + redirecting.reason = misdn_to_ast_reason(redirect->reason); + redirecting.count = redirect->count; + + ast_channel_set_redirecting(ast, &redirecting); +} + +/*! + * \internal + * \brief Notify peer that the redirecting information has changed. + * + * \param ast Current Asterisk channel + * \param bc Associated B channel + * + * \return Nothing + */ +static void misdn_update_redirecting(struct ast_channel *ast, struct misdn_bchannel *bc) +{ + int Is_PTMP; + + misdn_copy_redirecting_from_ast(bc, ast); + + Is_PTMP = !misdn_lib_is_ptp(bc->port); + if (Is_PTMP) { + /* Send NOTIFY(call-is-diverting, redirecting.to data) */ + bc->redirecting.to_changed = 1; + bc->notify_description_code = mISDN_NOTIFY_CODE_CALL_IS_DIVERTING; + misdn_lib_send_event(bc, EVENT_NOTIFY); +#if defined(AST_MISDN_ENHANCEMENTS) + } else { + /* Send DivertingLegInformation1 */ + bc->fac_out.Function = Fac_DivertingLegInformation1; + bc->fac_out.u.DivertingLegInformation1.InvokeID = ++misdn_invoke_id; + bc->fac_out.u.DivertingLegInformation1.DiversionReason = + misdn_to_diversion_reason(bc->redirecting.reason); + bc->fac_out.u.DivertingLegInformation1.SubscriptionOption = 2;/* notificationWithDivertedToNr */ + bc->fac_out.u.DivertingLegInformation1.DivertedToPresent = 1; + misdn_PresentedNumberUnscreened_fill(&bc->fac_out.u.DivertingLegInformation1.DivertedTo, &bc->redirecting.to); + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_FACILITY); + + /* Send DivertingLegInformation3 */ + bc->fac_out.Function = Fac_DivertingLegInformation3; + bc->fac_out.u.DivertingLegInformation3.InvokeID = ++misdn_invoke_id; + bc->fac_out.u.DivertingLegInformation3.PresentationAllowedIndicator = + bc->redirecting.to.presentation == 0 ? 1 : 0; + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_FACILITY); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + } } @@ -3088,98 +6274,164 @@ static int misdn_call(struct ast_channel *ast, char *dest, int timeout) return -1; } - /* - * dest is ---v - * Dial(mISDN/g:group_name[/extension[/options]]) - * Dial(mISDN/port[:preselected_channel][/extension[/options]]) - * - * The dial extension could be empty if you are using MISDN_KEYPAD - * to control ISDN provider features. - */ - dest_cp = ast_strdupa(dest); - AST_NONSTANDARD_APP_ARGS(args, dest_cp, '/'); - if (!args.ext) { - args.ext = ""; + port = newbc->port; + +#if defined(AST_MISDN_ENHANCEMENTS) + if ((ch->peer = misdn_cc_caller_get(ast))) { + chan_misdn_log(3, port, " --> Found CC caller data, peer:%s\n", + ch->peer->chan ? "available" : "NULL"); } - port = newbc->port; + if (ch->record_id != -1) { + struct misdn_cc_record *cc_record; - exceed = add_out_calls(port); - if (exceed != 0) { - char tmp[16]; + /* This is a call completion retry call */ + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(ch->record_id); + if (!cc_record) { + AST_LIST_UNLOCK(&misdn_cc_records_db); + ast_log(LOG_WARNING, " --> ! misdn_call called on %s, cc_record==NULL\n", ast->name); + ast->hangupcause = AST_CAUSE_NORMAL_TEMPORARY_FAILURE; + ast_setstate(ast, AST_STATE_DOWN); + return -1; + } - snprintf(tmp, sizeof(tmp), "%d", exceed); - pbx_builtin_setvar_helper(ast, "MAX_OVERFLOW", tmp); - ast->hangupcause = AST_CAUSE_NORMAL_TEMPORARY_FAILURE; - ast_setstate(ast, AST_STATE_DOWN); - return -1; - } + /* Setup calling parameters to retry the call. */ + newbc->dialed = cc_record->redial.dialed; + newbc->caller = cc_record->redial.caller; + memset(&newbc->redirecting, 0, sizeof(newbc->redirecting)); + newbc->capability = cc_record->redial.capability; + newbc->hdlc = cc_record->redial.hdlc; + newbc->sending_complete = 1; - chan_misdn_log(1, port, "* CALL: %s\n", dest); + if (cc_record->ptp) { + newbc->fac_out.Function = Fac_CCBS_T_Call; + newbc->fac_out.u.CCBS_T_Call.InvokeID = ++misdn_invoke_id; + } else { + newbc->fac_out.Function = Fac_CCBSCall; + newbc->fac_out.u.CCBSCall.InvokeID = ++misdn_invoke_id; + newbc->fac_out.u.CCBSCall.CCBSReference = cc_record->mode.ptmp.reference_id; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); - chan_misdn_log(2, port, " --> * dialed:%s tech:%s context:%s\n", args.ext, ast->name, ast->context); + ast_copy_string(ast->exten, newbc->dialed.number, sizeof(ast->exten)); - ast_copy_string(ast->exten, args.ext, sizeof(ast->exten)); - ast_copy_string(newbc->dialed.number, args.ext, sizeof(newbc->dialed.number)); + chan_misdn_log(1, port, "* Call completion to: %s\n", newbc->dialed.number); + chan_misdn_log(2, port, " --> * tech:%s context:%s\n", ast->name, ast->context); + } else +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + { + /* + * dest is ---v + * Dial(mISDN/g:group_name[/extension[/options]]) + * Dial(mISDN/port[:preselected_channel][/extension[/options]]) + * + * The dial extension could be empty if you are using MISDN_KEYPAD + * to control ISDN provider features. + */ + dest_cp = ast_strdupa(dest); + AST_NONSTANDARD_APP_ARGS(args, dest_cp, '/'); + if (!args.ext) { + args.ext = ""; + } - if (ast_strlen_zero(newbc->caller.name) && !ast_strlen_zero(ast->connected.id.name)) { - ast_copy_string(newbc->caller.name, ast->connected.id.name, sizeof(newbc->caller.name)); - chan_misdn_log(3, port, " --> * set caller:\"%s\" <%s>\n", newbc->caller.name, newbc->caller.number); - } - if (ast_strlen_zero(newbc->caller.number) && !ast_strlen_zero(ast->connected.id.number)) { - ast_copy_string(newbc->caller.number, ast->connected.id.number, sizeof(newbc->caller.number)); - chan_misdn_log(3, port, " --> * set caller:\"%s\" <%s>\n", newbc->caller.name, newbc->caller.number); - } + chan_misdn_log(1, port, "* CALL: %s\n", dest); + chan_misdn_log(2, port, " --> * dialed:%s tech:%s context:%s\n", args.ext, ast->name, ast->context); - misdn_cfg_get(port, MISDN_CFG_LOCALDIALPLAN, &number_type, sizeof(number_type)); - if (number_type < 0) { - newbc->caller.number_type = ast_to_misdn_ton(ast->connected.id.number_type); - newbc->caller.number_plan = ast_to_misdn_plan(ast->connected.id.number_type); - } else { - /* Force us to send in SETUP message */ - newbc->caller.number_type = number_type; - newbc->caller.number_plan = NUMPLAN_ISDN; - } - debug_numtype(port, newbc->caller.number_type, "LTON"); + ast_copy_string(ast->exten, args.ext, sizeof(ast->exten)); + ast_copy_string(newbc->dialed.number, args.ext, sizeof(newbc->dialed.number)); - newbc->capability = ast->transfercapability; - pbx_builtin_setvar_helper(ast, "TRANSFERCAPABILITY", ast_transfercapability2str(newbc->capability)); - if ( ast->transfercapability == INFO_CAPABILITY_DIGITAL_UNRESTRICTED) { - chan_misdn_log(2, port, " --> * Call with flag Digital\n"); - } + if (ast_strlen_zero(newbc->caller.name) && !ast_strlen_zero(ast->connected.id.name)) { + ast_copy_string(newbc->caller.name, ast->connected.id.name, sizeof(newbc->caller.name)); + chan_misdn_log(3, port, " --> * set caller:\"%s\" <%s>\n", newbc->caller.name, newbc->caller.number); + } + if (ast_strlen_zero(newbc->caller.number) && !ast_strlen_zero(ast->connected.id.number)) { + ast_copy_string(newbc->caller.number, ast->connected.id.number, sizeof(newbc->caller.number)); + chan_misdn_log(3, port, " --> * set caller:\"%s\" <%s>\n", newbc->caller.name, newbc->caller.number); + } - /* update caller screening and presentation */ - update_config(ch); + misdn_cfg_get(port, MISDN_CFG_LOCALDIALPLAN, &number_type, sizeof(number_type)); + if (number_type < 0) { + newbc->caller.number_type = ast_to_misdn_ton(ast->connected.id.number_type); + newbc->caller.number_plan = ast_to_misdn_plan(ast->connected.id.number_type); + } else { + /* Force us to send in SETUP message */ + newbc->caller.number_type = number_type; + newbc->caller.number_plan = NUMPLAN_ISDN; + } + debug_numtype(port, newbc->caller.number_type, "LTON"); - /* fill in some ies from channel dialplan variables */ - import_ch(ast, newbc, ch); + newbc->capability = ast->transfercapability; + pbx_builtin_setvar_helper(ast, "TRANSFERCAPABILITY", ast_transfercapability2str(newbc->capability)); + if (ast->transfercapability == INFO_CAPABILITY_DIGITAL_UNRESTRICTED) { + chan_misdn_log(2, port, " --> * Call with flag Digital\n"); + } - /* Finally The Options Override Everything */ - if (!ast_strlen_zero(args.opts)) { - misdn_set_opt_exec(ast, args.opts); - } else { - chan_misdn_log(2, port, "NO OPTS GIVEN\n"); - } - if (newbc->set_presentation) { - newbc->caller.presentation = newbc->presentation; - } + /* update caller screening and presentation */ + update_config(ch); - misdn_copy_redirecting_from_ast(newbc, ast); + /* fill in some ies from channel dialplan variables */ + import_ch(ast, newbc, ch); - /*check for bridging*/ - misdn_cfg_get(0, MISDN_GEN_BRIDGING, &bridging, sizeof(bridging)); - if (bridging && ch->other_ch) { + /* Finally The Options Override Everything */ + if (!ast_strlen_zero(args.opts)) { + misdn_set_opt_exec(ast, args.opts); + } else { + chan_misdn_log(2, port, "NO OPTS GIVEN\n"); + } + if (newbc->set_presentation) { + newbc->caller.presentation = newbc->presentation; + } + + misdn_copy_redirecting_from_ast(newbc, ast); +#if defined(AST_MISDN_ENHANCEMENTS) + if (newbc->redirecting.from.number[0] && misdn_lib_is_ptp(port)) { + /* Create DivertingLegInformation2 facility */ + newbc->fac_out.Function = Fac_DivertingLegInformation2; + newbc->fac_out.u.DivertingLegInformation2.InvokeID = ++misdn_invoke_id; + newbc->fac_out.u.DivertingLegInformation2.DiversionCounter = + newbc->redirecting.count; + newbc->fac_out.u.DivertingLegInformation2.DiversionReason = + misdn_to_diversion_reason(newbc->redirecting.reason); + newbc->fac_out.u.DivertingLegInformation2.DivertingPresent = 1; + misdn_PresentedNumberUnscreened_fill( + &newbc->fac_out.u.DivertingLegInformation2.Diverting, + &newbc->redirecting.from); + newbc->fac_out.u.DivertingLegInformation2.OriginalCalledPresent = 0; + } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + + /*check for bridging*/ + misdn_cfg_get(0, MISDN_GEN_BRIDGING, &bridging, sizeof(bridging)); + if (bridging && ch->other_ch) { #ifdef MISDN_1_2 - chan_misdn_log(1, port, "Disabling EC (aka Pipeline) on both Sides\n"); - *ch->bc->pipeline = 0; - *ch->other_ch->bc->pipeline = 0; + chan_misdn_log(1, port, "Disabling EC (aka Pipeline) on both Sides\n"); + *ch->bc->pipeline = 0; + *ch->other_ch->bc->pipeline = 0; #else - chan_misdn_log(1, port, "Disabling EC on both Sides\n"); - ch->bc->ec_enable = 0; - ch->other_ch->bc->ec_enable = 0; + chan_misdn_log(1, port, "Disabling EC on both Sides\n"); + ch->bc->ec_enable = 0; + ch->other_ch->bc->ec_enable = 0; #endif + } } + exceed = add_out_calls(port); + if (exceed != 0) { + char tmp[16]; + + snprintf(tmp, sizeof(tmp), "%d", exceed); + pbx_builtin_setvar_helper(ast, "MAX_OVERFLOW", tmp); + ast->hangupcause = AST_CAUSE_NORMAL_TEMPORARY_FAILURE; + ast_setstate(ast, AST_STATE_DOWN); + return -1; + } + +#if defined(AST_MISDN_ENHANCEMENTS) + if (newbc->fac_out.Function != Fac_None) { + print_facility(&newbc->fac_out, newbc); + } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ r = misdn_lib_send_event(newbc, EVENT_SETUP); /** we should have l3id after sending setup **/ @@ -3468,7 +6720,7 @@ static int misdn_indication(struct ast_channel *ast, int cond, const void *data, break; case AST_CONTROL_REDIRECTING: chan_misdn_log(1, p->bc->port, "* IND :\tredirecting info update pid:%d\n", p->bc->pid); - misdn_copy_redirecting_from_ast(p->bc, ast); + misdn_update_redirecting(ast, p->bc); break; default: chan_misdn_log(1, p->bc->port, " --> * Unknown Indication:%d pid:%d\n", cond, p->bc->pid); @@ -3518,7 +6770,7 @@ static int misdn_hangup(struct ast_channel *ast) p->state == MISDN_HOLDED || p->state == MISDN_HOLD_DISCONNECT) { - CLEAN_CH: +CLEAN_CH: /* between request and call */ ast_debug(1, "State Reserved (or nothing) => chanIsAvail\n"); MISDN_ASTERISK_TECH_PVT(ast) = NULL; @@ -3673,6 +6925,13 @@ static int misdn_hangup(struct ast_channel *ast) p->state = MISDN_CLEANING; +#if defined(AST_MISDN_ENHANCEMENTS) + if (p->peer) { + ao2_ref(p->peer, -1); + p->peer = NULL; + } +#endif /* AST_MISDN_ENHANCEMENTS */ + chan_misdn_log(3, bc->port, " --> Channel: %s hanguped new state:%s\n", ast->name, misdn_get_ch_state(p)); return 0; @@ -4148,13 +7407,16 @@ static struct chan_list *init_chan_list(int orig) cl->need_hangup = 1; cl->need_busy = 1; cl->overlap_dial_task = -1; +#if defined(AST_MISDN_ENHANCEMENTS) + cl->record_id = -1; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ return cl; } static struct ast_channel *misdn_request(const char *type, int format, void *data, int *cause) { - struct ast_channel *tmp = NULL; + struct ast_channel *ast; char group[BUFFERSIZE + 1] = ""; char dial_str[128]; char *dest_cp; @@ -4163,6 +7425,12 @@ static struct ast_channel *misdn_request(const char *type, int format, void *dat int port = 0; struct misdn_bchannel *newbc = NULL; int dec = 0; +#if defined(AST_MISDN_ENHANCEMENTS) + int cc_retry_call = 0; /* TRUE if this is a call completion retry call */ + long record_id = -1; + struct misdn_cc_record *cc_record; + const char *err_msg; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ struct chan_list *cl; AST_DECLARE_APP_ARGS(args, @@ -4177,6 +7445,7 @@ static struct ast_channel *misdn_request(const char *type, int format, void *dat * data is ---v * Dial(mISDN/g:group_name[/extension[/options]]) * Dial(mISDN/port[:preselected_channel][/extension[/options]]) + * Dial(mISDN/cc/cc-record-id) * * The dial extension could be empty if you are using MISDN_KEYPAD * to control ISDN provider features. @@ -4193,6 +7462,10 @@ static struct ast_channel *misdn_request(const char *type, int format, void *dat args.intf += 2; ast_copy_string(group, args.intf, sizeof(group)); chan_misdn_log(2, 0, " --> Group Call group: %s\n", group); +#if defined(AST_MISDN_ENHANCEMENTS) + } else if (strcmp(args.intf, "cc") == 0) { + cc_retry_call = 1; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ } else if ((p = strchr(args.intf, ':'))) { /* we have a preselected channel */ *p++ = 0; @@ -4207,6 +7480,37 @@ static struct ast_channel *misdn_request(const char *type, int format, void *dat return NULL; } +#if defined(AST_MISDN_ENHANCEMENTS) + if (cc_retry_call) { + if (ast_strlen_zero(args.ext)) { + ast_log(LOG_WARNING, " --> ! IND : Dial(%s) WITHOUT cc-record-id, check extensions.conf\n", dial_str); + return NULL; + } + if (!isdigit(*args.ext)) { + ast_log(LOG_WARNING, " --> ! IND : Dial(%s) cc-record-id must be a number.\n", dial_str); + return NULL; + } + record_id = atol(args.ext); + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(record_id); + if (!cc_record) { + AST_LIST_UNLOCK(&misdn_cc_records_db); + err_msg = misdn_cc_record_not_found; + ast_log(LOG_WARNING, " --> ! IND : Dial(%s) %s.\n", dial_str, err_msg); + return NULL; + } + if (!cc_record->activated) { + AST_LIST_UNLOCK(&misdn_cc_records_db); + err_msg = "Call completion has not been activated"; + ast_log(LOG_WARNING, " --> ! IND : Dial(%s) %s.\n", dial_str, err_msg); + return NULL; + } + port = cc_record->port; + AST_LIST_UNLOCK(&misdn_cc_records_db); + } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + if (misdn_cfg_is_group_method(group, METHOD_STANDARD_DEC)) { chan_misdn_log(4, port, " --> STARTING STANDARD DEC...\n"); dec = 1; @@ -4331,18 +7635,21 @@ static struct ast_channel *misdn_request(const char *type, int format, void *dat /* create ast_channel and link all the objects together */ cl = init_chan_list(ORG_AST); if (!cl) { - ast_log(LOG_WARNING, "Could not create Asterisk channel for Dial(%s)\n", dial_str); + ast_log(LOG_ERROR, "Could not create call record for Dial(%s)\n", dial_str); return NULL; } cl->bc = newbc; - tmp = misdn_new(cl, AST_STATE_RESERVED, args.ext, NULL, format, port, channel); - if (!tmp) { - ast_log(LOG_ERROR, "Could not create Asterisk object\n"); + ast = misdn_new(cl, AST_STATE_RESERVED, args.ext, NULL, format, port, channel); + if (!ast) { + ast_log(LOG_ERROR, "Could not create Asterisk channel for Dial(%s)\n", dial_str); return NULL; } + cl->ast = ast; - cl->ast = tmp; +#if defined(AST_MISDN_ENHANCEMENTS) + cl->record_id = record_id; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ /* register chan in local list */ cl_queue_chan(&cl_te, cl); @@ -4353,7 +7660,7 @@ static struct ast_channel *misdn_request(const char *type, int format, void *dat /* important */ cl->need_hangup = 0; - return tmp; + return ast; } @@ -4373,7 +7680,7 @@ static int misdn_send_text(struct ast_channel *chan, const char *text) } static struct ast_channel_tech misdn_tech = { - .type = "mISDN", + .type = misdn_type, .description = "Channel driver for mISDN Support (Bri/Pri)", .capabilities = AST_FORMAT_ALAW , .requester = misdn_request, @@ -4392,7 +7699,7 @@ static struct ast_channel_tech misdn_tech = { }; static struct ast_channel_tech misdn_tech_wo_bridge = { - .type = "mISDN", + .type = misdn_type, .description = "Channel driver for mISDN Support (Bri/Pri)", .capabilities = AST_FORMAT_ALAW , .requester = misdn_request, @@ -4688,10 +7995,11 @@ static void hangup_chan(struct chan_list *ch) /** Isdn asks us to release channel, pendant to misdn_hangup **/ static void release_chan(struct misdn_bchannel *bc) { - struct ast_channel *ast = NULL; struct chan_list *ch; + struct ast_channel *ast; ast_mutex_lock(&release_lock); + ch = find_chan_by_bc(cl_te, bc); if (!ch) { chan_misdn_log(1, bc->port, "release_chan: Ch not found!\n"); @@ -4699,10 +8007,6 @@ static void release_chan(struct misdn_bchannel *bc) return; } - if (ch->ast) { - ast = ch->ast; - } - chan_misdn_log(5, bc->port, "release_chan: bc with l3id: %x\n", bc->l3_id); /* releasing jitterbuffer */ @@ -4724,42 +8028,39 @@ static void release_chan(struct misdn_bchannel *bc) } if (ch->originator == ORG_AST) { - misdn_out_calls[bc->port]--; + --misdn_out_calls[bc->port]; } else { - misdn_in_calls[bc->port]--; + --misdn_in_calls[bc->port]; } - if (ch) { - close(ch->pipe[0]); - close(ch->pipe[1]); - - if (ast && MISDN_ASTERISK_TECH_PVT(ast)) { - chan_misdn_log(1, bc->port, - "* RELEASING CHANNEL pid:%d context:%s dialed:%s caller:\"%s\" <%s> state: %s\n", - bc->pid, - ast->context, - ast->exten, - ast->cid.cid_name ? ast->cid.cid_name : "", - ast->cid.cid_num ? ast->cid.cid_num : "", - misdn_get_ch_state(ch)); - chan_misdn_log(3, bc->port, " --> * State Down\n"); - MISDN_ASTERISK_TECH_PVT(ast) = NULL; - - if (ast->_state != AST_STATE_RESERVED) { - chan_misdn_log(3, bc->port, " --> Setting AST State to down\n"); - ast_setstate(ast, AST_STATE_DOWN); - } - } + close(ch->pipe[0]); + close(ch->pipe[1]); - ch->state = MISDN_CLEANING; - cl_dequeue_chan(&cl_te, ch); + ast = ch->ast; + if (ast && MISDN_ASTERISK_TECH_PVT(ast)) { + chan_misdn_log(1, bc->port, + "* RELEASING CHANNEL pid:%d context:%s dialed:%s caller:\"%s\" <%s> state: %s\n", + bc->pid, + ast->context, + ast->exten, + ast->cid.cid_name ? ast->cid.cid_name : "", + ast->cid.cid_num ? ast->cid.cid_num : "", + misdn_get_ch_state(ch)); + chan_misdn_log(3, bc->port, " --> * State Down\n"); + MISDN_ASTERISK_TECH_PVT(ast) = NULL; - ast_free(ch); - } else { - /* chan is already cleaned, so exiting */ + if (ast->_state != AST_STATE_RESERVED) { + chan_misdn_log(3, bc->port, " --> Setting AST State to down\n"); + ast_setstate(ast, AST_STATE_DOWN); + } } + + ch->state = MISDN_CLEANING; + cl_dequeue_chan(&cl_te, ch); + + ast_free(ch); + ast_mutex_unlock(&release_lock); -/*** release end **/ } static void misdn_transfer_bc(struct chan_list *tmp_ch, struct chan_list *holded_chan) @@ -4828,7 +8129,7 @@ static void do_immediate_setup(struct misdn_bchannel *bc, struct chan_list *ch, fr.offset = 0; fr.delivery = ast_tv(0,0); - if (ch->ast && MISDN_ASTERISK_PVT(ch->ast) && MISDN_ASTERISK_TECH_PVT(ch->ast)) { + if (ch->ast && MISDN_ASTERISK_TECH_PVT(ch->ast)) { ast_queue_frame(ch->ast, &fr); } predial++; @@ -5012,6 +8313,883 @@ static void wait_for_digits(struct chan_list *ch, struct misdn_bchannel *bc, str } } +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Handle the FACILITY CCBSStatusRequest message. + * + * \param port Logical port number. + * \param facility Facility ie contents. + * + * \return Nothing + */ +static void misdn_cc_handle_ccbs_status_request(int port, const struct FacParm *facility) +{ + struct misdn_cc_record *cc_record; + struct misdn_bchannel dummy; + + switch (facility->u.CCBSStatusRequest.ComponentType) { + case FacComponent_Invoke: + /* Build message */ + misdn_make_dummy(&dummy, port, 0, misdn_lib_port_is_nt(port), 0); + dummy.fac_out.Function = Fac_CCBSStatusRequest; + dummy.fac_out.u.CCBSStatusRequest.InvokeID = facility->u.CCBSStatusRequest.InvokeID; + dummy.fac_out.u.CCBSStatusRequest.ComponentType = FacComponent_Result; + + /* Answer User-A free question */ + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_reference(port, facility->u.CCBSStatusRequest.Component.Invoke.CCBSReference); + if (cc_record) { + dummy.fac_out.u.CCBSStatusRequest.Component.Result.Free = cc_record->party_a_free; + } else { + /* No record so say User-A is free */ + dummy.fac_out.u.CCBSStatusRequest.Component.Result.Free = 1; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + + /* Send message */ + print_facility(&dummy.fac_out, &dummy); + misdn_lib_send_event(&dummy, EVENT_FACILITY); + break; + + default: + chan_misdn_log(0, port, " --> not yet handled: facility type:0x%04X\n", facility->Function); + break; + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Start a PBX to notify that User-B is available. + * + * \param record_id Call completion record ID + * \param notify Dialplan location to start processing. + * + * \return Nothing + */ +static void misdn_cc_pbx_notify(long record_id, const struct misdn_cc_notify *notify) +{ + struct ast_channel *chan; + char id_str[32]; + + static unsigned short sequence = 0; + + /* Create a channel to notify with */ + snprintf(id_str, sizeof(id_str), "%ld", record_id); + chan = ast_channel_alloc(0, AST_STATE_DOWN, id_str, NULL, NULL, + notify->exten, notify->context, 0, + "mISDN-CC/%ld-%X", record_id, (unsigned) ++sequence); + if (!chan) { + ast_log(LOG_ERROR, "Unable to allocate channel!\n"); + return; + } + chan->priority = notify->priority; + if (chan->cid.cid_dnid) { + ast_free(chan->cid.cid_dnid); + } + chan->cid.cid_dnid = ast_strdup(notify->exten); + + if (ast_pbx_start(chan)) { + ast_log(LOG_WARNING, "Unable to start pbx channel %s!\n", chan->name); + ast_channel_free(chan); + } else { + ast_verb(1, "Started pbx for call completion notify channel %s\n", chan->name); + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Handle the FACILITY CCBS_T_RemoteUserFree message. + * + * \param bc B channel control structure message came in on + * + * \return Nothing + */ +static void misdn_cc_handle_T_remote_user_free(struct misdn_bchannel *bc) +{ + struct misdn_cc_record *cc_record; + struct misdn_cc_notify notify; + long record_id; + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_bc(bc); + if (cc_record) { + if (cc_record->party_a_free) { + notify = cc_record->remote_user_free; + } else { + /* Send CCBS_T_Suspend message */ + bc->fac_out.Function = Fac_CCBS_T_Suspend; + bc->fac_out.u.CCBS_T_Suspend.InvokeID = ++misdn_invoke_id; + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_FACILITY); + + notify = cc_record->b_free; + } + record_id = cc_record->record_id; + AST_LIST_UNLOCK(&misdn_cc_records_db); + if (notify.context[0]) { + /* Party A is free or B-Free notify has been setup. */ + misdn_cc_pbx_notify(record_id, ¬ify); + } + } else { + AST_LIST_UNLOCK(&misdn_cc_records_db); + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Handle the FACILITY CCBSRemoteUserFree message. + * + * \param port Logical port number. + * \param facility Facility ie contents. + * + * \return Nothing + */ +static void misdn_cc_handle_remote_user_free(int port, const struct FacParm *facility) +{ + struct misdn_cc_record *cc_record; + struct misdn_cc_notify notify; + long record_id; + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_reference(port, facility->u.CCBSRemoteUserFree.CCBSReference); + if (cc_record) { + notify = cc_record->remote_user_free; + record_id = cc_record->record_id; + AST_LIST_UNLOCK(&misdn_cc_records_db); + misdn_cc_pbx_notify(record_id, ¬ify); + } else { + AST_LIST_UNLOCK(&misdn_cc_records_db); + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Handle the FACILITY CCBSBFree message. + * + * \param port Logical port number. + * \param facility Facility ie contents. + * + * \return Nothing + */ +static void misdn_cc_handle_b_free(int port, const struct FacParm *facility) +{ + struct misdn_cc_record *cc_record; + struct misdn_cc_notify notify; + long record_id; + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_reference(port, facility->u.CCBSBFree.CCBSReference); + if (cc_record && cc_record->b_free.context[0]) { + /* B-Free notify has been setup. */ + notify = cc_record->b_free; + record_id = cc_record->record_id; + AST_LIST_UNLOCK(&misdn_cc_records_db); + misdn_cc_pbx_notify(record_id, ¬ify); + } else { + AST_LIST_UNLOCK(&misdn_cc_records_db); + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +/*! + * \internal + * \brief Handle the incoming facility ie contents + * + * \param event Message type facility ie came in on + * \param bc B channel control structure message came in on + * \param ch Associated channel call record + * + * \return Nothing + */ +static void misdn_facility_ie_handler(enum event_e event, struct misdn_bchannel *bc, struct chan_list *ch) +{ +#if defined(AST_MISDN_ENHANCEMENTS) + const char *diagnostic_msg; + struct misdn_cc_record *cc_record; + char buf[32]; + struct misdn_party_id party_id; + long new_record_id; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + + print_facility(&bc->fac_in, bc); + switch (bc->fac_in.Function) { +#if defined(AST_MISDN_ENHANCEMENTS) + case Fac_ActivationDiversion: + switch (bc->fac_in.u.ActivationDiversion.ComponentType) { + case FacComponent_Result: + /* Positive ACK to activation */ + /* We don't handle this yet */ + break; + default: + chan_misdn_log(0, bc->port," --> not yet handled: facility type:0x%04X\n", + bc->fac_in.Function); + break; + } + break; + case Fac_DeactivationDiversion: + switch (bc->fac_in.u.DeactivationDiversion.ComponentType) { + case FacComponent_Result: + /* Positive ACK to deactivation */ + /* We don't handle this yet */ + break; + default: + chan_misdn_log(0, bc->port," --> not yet handled: facility type:0x%04X\n", + bc->fac_in.Function); + break; + } + break; + case Fac_ActivationStatusNotificationDiv: + /* Sent to other MSN numbers on the line when a user activates call forwarding. */ + /* Sent in the first call control message of an outgoing call from the served user. */ + /* We do not have anything to do for this message. */ + break; + case Fac_DeactivationStatusNotificationDiv: + /* Sent to other MSN numbers on the line when a user deactivates call forwarding. */ + /* We do not have anything to do for this message. */ + break; +#if 0 /* We don't handle this yet */ + case Fac_InterrogationDiversion: + /* We don't handle this yet */ + break; + case Fac_InterrogateServedUserNumbers: + /* We don't handle this yet */ + break; +#endif /* We don't handle this yet */ + case Fac_DiversionInformation: + /* Sent to the served user when a call is forwarded. */ + /* We do not have anything to do for this message. */ + break; + case Fac_CallDeflection: + if (ch && ch->ast) { + switch (bc->fac_in.u.CallDeflection.ComponentType) { + case FacComponent_Invoke: + ast_copy_string(bc->redirecting.from.number, bc->dialed.number, + sizeof(bc->redirecting.from.number)); + bc->redirecting.from.name[0] = 0; + bc->redirecting.from.number_plan = bc->dialed.number_plan; + bc->redirecting.from.number_type = bc->dialed.number_type; + bc->redirecting.from.screening = 0;/* Unscreened */ + if (bc->fac_in.u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUserPresent) { + bc->redirecting.from.presentation = + bc->fac_in.u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUser + ? 0 /* Allowed */ : 1 /* Restricted */; + } else { + bc->redirecting.from.presentation = 0;/* Allowed */ + } + + /* Add configured prefix to the call deflection number */ + memset(&party_id, 0, sizeof(party_id)); + misdn_PartyNumber_extract(&party_id, + &bc->fac_in.u.CallDeflection.Component.Invoke.Deflection.Party); + misdn_add_number_prefix(bc->port, party_id.number_type, + party_id.number, sizeof(party_id.number)); + //party_id.presentation = 0;/* Allowed */ + //party_id.screening = 0;/* Unscreened */ + bc->redirecting.to = party_id; + + ++bc->redirecting.count; + bc->redirecting.reason = mISDN_REDIRECTING_REASON_DEFLECTION; + + misdn_copy_redirecting_to_ast(ch->ast, &bc->redirecting); + ast_string_field_set(ch->ast, call_forward, bc->redirecting.to.number); + + /* Send back positive ACK */ + bc->fac_out.Function = Fac_CallDeflection; + bc->fac_out.u.CallDeflection.InvokeID = bc->fac_in.u.CallDeflection.InvokeID; + bc->fac_out.u.CallDeflection.ComponentType = FacComponent_Result; + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_DISCONNECT); + + /* This line is BUSY to further attempts by this dialing attempt. */ + ast_queue_control(ch->ast, AST_CONTROL_BUSY); + break; + + case FacComponent_Result: + /* Positive ACK to call deflection */ + /* + * Sent in DISCONNECT or FACILITY message depending upon network option. + * It is in the FACILITY message if the call is still offered to the user + * while trying to alert the deflected to party. + */ + /* Ignore the ACK */ + break; + + default: + break; + } + } + break; +#if 0 /* We don't handle this yet */ + case Fac_CallRerouteing: + /* Private-Public ISDN interworking message */ + /* We don't handle this yet */ + break; +#endif /* We don't handle this yet */ + case Fac_DivertingLegInformation1: + /* Private-Public ISDN interworking message */ + if (ch && ch->ast) { + bc->redirecting.reason = + diversion_reason_to_misdn(bc->fac_in.u.DivertingLegInformation1.DiversionReason); + if (bc->fac_in.u.DivertingLegInformation1.DivertedToPresent) { + misdn_PresentedNumberUnscreened_extract(&bc->redirecting.to, + &bc->fac_in.u.DivertingLegInformation1.DivertedTo); + + /* Add configured prefix to redirecting.to.number */ + misdn_add_number_prefix(bc->port, bc->redirecting.to.number_type, + bc->redirecting.to.number, sizeof(bc->redirecting.to.number)); + + misdn_copy_redirecting_to_ast(ch->ast, &bc->redirecting); + ast_channel_queue_redirecting_update(ch->ast, &ch->ast->redirecting); + } else { + ch->ast->redirecting.reason = misdn_to_ast_reason(bc->redirecting.reason); + } + } + break; + case Fac_DivertingLegInformation2: + /* Private-Public ISDN interworking message */ + switch (event) { + case EVENT_SETUP: + /* Comes in on a SETUP with redirecting.from information */ + if (ch && ch->ast) { + bc->redirecting.reason = + diversion_reason_to_misdn(bc->fac_in.u.DivertingLegInformation2.DiversionReason); + bc->redirecting.count = bc->fac_in.u.DivertingLegInformation2.DiversionCounter; + if (bc->fac_in.u.DivertingLegInformation2.DivertingPresent) { + /* This information is redundant if there was a redirecting ie in the SETUP. */ + misdn_PresentedNumberUnscreened_extract(&bc->redirecting.from, + &bc->fac_in.u.DivertingLegInformation2.Diverting); + + /* Add configured prefix to redirecting.from.number */ + misdn_add_number_prefix(bc->port, bc->redirecting.from.number_type, + bc->redirecting.from.number, sizeof(bc->redirecting.from.number)); + } +#if 0 + if (bc->fac_in.u.DivertingLegInformation2.OriginalCalledPresent) { + /* We have no place to put the OriginalCalled number */ + } +#endif + misdn_copy_redirecting_to_ast(ch->ast, &bc->redirecting); + } + break; + default: + chan_misdn_log(0, bc->port," --> Expected in a SETUP message: facility type:0x%04X\n", + bc->fac_in.Function); + break; + } + break; + case Fac_DivertingLegInformation3: + /* Private-Public ISDN interworking message */ + /* Don't know what to do with this. */ + break; + +#else /* !defined(AST_MISDN_ENHANCEMENTS) */ + + case Fac_CD: + if (ch && ch->ast) { + ast_copy_string(bc->redirecting.from.number, bc->dialed.number, + sizeof(bc->redirecting.from.number)); + bc->redirecting.from.name[0] = 0; + bc->redirecting.from.number_plan = bc->dialed.number_plan; + bc->redirecting.from.number_type = bc->dialed.number_type; + bc->redirecting.from.screening = 0;/* Unscreened */ + bc->redirecting.from.presentation = + bc->fac_in.u.CDeflection.PresentationAllowed + ? 0 /* Allowed */ : 1 /* Restricted */; + + ast_copy_string(bc->redirecting.to.number, + (char *) bc->fac_in.u.CDeflection.DeflectedToNumber, + sizeof(bc->redirecting.to.number)); + bc->redirecting.to.name[0] = 0; + bc->redirecting.to.number_plan = NUMPLAN_UNKNOWN; + bc->redirecting.to.number_type = NUMTYPE_UNKNOWN; + bc->redirecting.to.presentation = 0;/* Allowed */ + bc->redirecting.to.screening = 0;/* Unscreened */ + + ++bc->redirecting.count; + bc->redirecting.reason = mISDN_REDIRECTING_REASON_DEFLECTION; + + misdn_copy_redirecting_to_ast(ch->ast, &bc->redirecting); + ast_string_field_set(ch->ast, call_forward, bc->redirecting.to.number); + + misdn_lib_send_event(bc, EVENT_DISCONNECT); + + /* This line is BUSY to further attempts by this dialing attempt. */ + ast_queue_control(ch->ast, AST_CONTROL_BUSY); + } + break; +#endif /* !defined(AST_MISDN_ENHANCEMENTS) */ + case Fac_AOCDCurrency: + if (ch && ch->ast) { + bc->AOCDtype = Fac_AOCDCurrency; + memcpy(&bc->AOCD.currency, &bc->fac_in.u.AOCDcur, sizeof(bc->AOCD.currency)); + bc->AOCD_need_export = 1; + export_aoc_vars(ch->originator, ch->ast, bc); + } + break; + case Fac_AOCDChargingUnit: + if (ch && ch->ast) { + bc->AOCDtype = Fac_AOCDChargingUnit; + memcpy(&bc->AOCD.chargingUnit, &bc->fac_in.u.AOCDchu, sizeof(bc->AOCD.chargingUnit)); + bc->AOCD_need_export = 1; + export_aoc_vars(ch->originator, ch->ast, bc); + } + break; +#if defined(AST_MISDN_ENHANCEMENTS) + case Fac_ERROR: + diagnostic_msg = misdn_to_str_error_code(bc->fac_in.u.ERROR.errorValue); + chan_misdn_log(1, bc->port, " --> Facility error code: %s\n", diagnostic_msg); + switch (event) { + case EVENT_DISCONNECT: + case EVENT_RELEASE: + case EVENT_RELEASE_COMPLETE: + /* Possible call failure as a result of Fac_CCBSCall/Fac_CCBS_T_Call */ + if (ch && ch->peer) { + misdn_cc_set_peer_var(ch->peer, MISDN_ERROR_MSG, diagnostic_msg); + } + break; + default: + break; + } + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_invoke(bc->port, bc->fac_in.u.ERROR.invokeId); + if (cc_record) { + cc_record->outstanding_message = 0; + cc_record->error_code = bc->fac_in.u.ERROR.errorValue; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + break; + case Fac_REJECT: + diagnostic_msg = misdn_to_str_reject_code(bc->fac_in.u.REJECT.Code); + chan_misdn_log(1, bc->port, " --> Facility reject code: %s\n", diagnostic_msg); + switch (event) { + case EVENT_DISCONNECT: + case EVENT_RELEASE: + case EVENT_RELEASE_COMPLETE: + /* Possible call failure as a result of Fac_CCBSCall/Fac_CCBS_T_Call */ + if (ch && ch->peer) { + misdn_cc_set_peer_var(ch->peer, MISDN_ERROR_MSG, diagnostic_msg); + } + break; + default: + break; + } + if (bc->fac_in.u.REJECT.InvokeIDPresent) { + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_invoke(bc->port, bc->fac_in.u.REJECT.InvokeID); + if (cc_record) { + cc_record->outstanding_message = 0; + cc_record->reject_code = bc->fac_in.u.REJECT.Code; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + } + break; + case Fac_RESULT: + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_invoke(bc->port, bc->fac_in.u.RESULT.InvokeID); + if (cc_record) { + cc_record->outstanding_message = 0; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + break; +#if 0 /* We don't handle this yet */ + case Fac_EctExecute: + /* We don't handle this yet */ + break; + case Fac_ExplicitEctExecute: + /* We don't handle this yet */ + break; + case Fac_EctLinkIdRequest: + /* We don't handle this yet */ + break; +#endif /* We don't handle this yet */ + case Fac_SubaddressTransfer: + /* We do not have anything to do for this message since we do not handle subaddreses. */ + break; + case Fac_RequestSubaddress: + /* We do not have anything to do for this message since we do not handle subaddreses. */ + break; + case Fac_EctInform: + /* Private-Public ISDN interworking message */ + if (ch && ch->ast && bc->fac_in.u.EctInform.RedirectionPresent) { + /* Add configured prefix to the redirection number */ + memset(&party_id, 0, sizeof(party_id)); + misdn_PresentedNumberUnscreened_extract(&party_id, + &bc->fac_in.u.EctInform.Redirection); + misdn_add_number_prefix(bc->port, party_id.number_type, + party_id.number, sizeof(party_id.number)); + + misdn_queue_connected_line_update(ch->ast, &party_id, + (bc->fac_in.u.EctInform.Status == 0 /* alerting */) + ? AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER_ALERTING + : AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER); + } + break; +#if 0 /* We don't handle this yet */ + case Fac_EctLoopTest: + /* The use of this message is unclear on how it works to detect loops. */ + /* We don't handle this yet */ + break; +#endif /* We don't handle this yet */ + case Fac_CallInfoRetain: + switch (event) { + case EVENT_ALERTING: + case EVENT_DISCONNECT: + /* CCBS/CCNR is available */ + if (ch && ch->peer) { + AST_LIST_LOCK(&misdn_cc_records_db); + if (ch->record_id == -1) { + cc_record = misdn_cc_new(); + } else { + /* + * We are doing a call-completion attempt + * or the switch is sending us extra call-completion + * availability indications (erroneously?). + * + * Assume that the network request retention option + * is not on and that the current call-completion + * request is disabled. + */ + cc_record = misdn_cc_find_by_id(ch->record_id); + if (cc_record) { + if (cc_record->ptp && cc_record->mode.ptp.bc) { + /* + * What? We are getting mixed messages from the + * switch. We are currently setup for + * point-to-point. Now we are switching to + * point-to-multipoint. + * + * Close the call-completion signaling link + */ + cc_record->mode.ptp.bc->fac_out.Function = Fac_None; + cc_record->mode.ptp.bc->out_cause = AST_CAUSE_NORMAL_CLEARING; + misdn_lib_send_event(cc_record->mode.ptp.bc, EVENT_RELEASE_COMPLETE); + } + + /* + * Resetup the existing record for a possible new + * call-completion request. + */ + new_record_id = misdn_cc_record_id_new(); + if (new_record_id < 0) { + /* Looks like we must keep the old id anyway. */ + } else { + cc_record->record_id = new_record_id; + ch->record_id = new_record_id; + } + cc_record->ptp = 0; + cc_record->port = bc->port; + memset(&cc_record->mode, 0, sizeof(cc_record->mode)); + cc_record->mode.ptmp.linkage_id = bc->fac_in.u.CallInfoRetain.CallLinkageID; + cc_record->invoke_id = ++misdn_invoke_id; + cc_record->activated = 0; + cc_record->outstanding_message = 0; + cc_record->activation_requested = 0; + cc_record->error_code = FacError_None; + cc_record->reject_code = FacReject_None; + memset(&cc_record->remote_user_free, 0, sizeof(cc_record->remote_user_free)); + memset(&cc_record->b_free, 0, sizeof(cc_record->b_free)); + cc_record->time_created = time(NULL); + + cc_record = NULL; + } else { + /* + * Where did the record go? We will have to recapture + * the call setup information. Unfortunately, some + * setup information may have been changed. + */ + ch->record_id = -1; + cc_record = misdn_cc_new(); + } + } + if (cc_record) { + ch->record_id = cc_record->record_id; + cc_record->ptp = 0; + cc_record->port = bc->port; + cc_record->mode.ptmp.linkage_id = bc->fac_in.u.CallInfoRetain.CallLinkageID; + + /* Record call information for possible call-completion attempt. */ + cc_record->redial.caller = bc->caller; + cc_record->redial.dialed = bc->dialed; + cc_record->redial.setup_bc_hlc_llc = bc->setup_bc_hlc_llc; + cc_record->redial.capability = bc->capability; + cc_record->redial.hdlc = bc->hdlc; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + + /* Set MISDN_CC_RECORD_ID in original channel */ + if (ch->record_id != -1) { + snprintf(buf, sizeof(buf), "%ld", ch->record_id); + } else { + buf[0] = 0; + } + misdn_cc_set_peer_var(ch->peer, MISDN_CC_RECORD_ID, buf); + } + break; + default: + chan_misdn_log(0, bc->port, + " --> Expected in a DISCONNECT or ALERTING message: facility type:0x%04X\n", + bc->fac_in.Function); + break; + } + break; + case Fac_CCBS_T_Call: + case Fac_CCBSCall: + switch (event) { + case EVENT_SETUP: + /* + * This is a call completion retry call. + * If we had anything to do we would do it here. + */ + break; + default: + chan_misdn_log(0, bc->port, " --> Expected in a SETUP message: facility type:0x%04X\n", + bc->fac_in.Function); + break; + } + break; + case Fac_CCBSDeactivate: + switch (bc->fac_in.u.CCBSDeactivate.ComponentType) { + case FacComponent_Result: + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_invoke(bc->port, bc->fac_in.u.CCBSDeactivate.InvokeID); + if (cc_record) { + cc_record->outstanding_message = 0; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + break; + + default: + chan_misdn_log(0, bc->port, " --> not yet handled: facility type:0x%04X\n", + bc->fac_in.Function); + break; + } + break; + case Fac_CCBSErase: + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_reference(bc->port, bc->fac_in.u.CCBSErase.CCBSReference); + if (cc_record) { + misdn_cc_delete(cc_record); + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + break; + case Fac_CCBSRemoteUserFree: + misdn_cc_handle_remote_user_free(bc->port, &bc->fac_in); + break; + case Fac_CCBSBFree: + misdn_cc_handle_b_free(bc->port, &bc->fac_in); + break; + case Fac_CCBSStatusRequest: + misdn_cc_handle_ccbs_status_request(bc->port, &bc->fac_in); + break; + case Fac_EraseCallLinkageID: + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_linkage(bc->port, + bc->fac_in.u.EraseCallLinkageID.CallLinkageID); + if (cc_record && !cc_record->activation_requested) { + /* + * The T-RETENTION timer expired before we requested + * call completion activation. Call completion is no + * longer available. + */ + misdn_cc_delete(cc_record); + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + break; + case Fac_CCBSStopAlerting: + /* We do not have anything to do for this message. */ + break; + case Fac_CCBSRequest: + case Fac_CCNRRequest: + switch (bc->fac_in.u.CCBSRequest.ComponentType) { + case FacComponent_Result: + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_invoke(bc->port, bc->fac_in.u.CCBSRequest.InvokeID); + if (cc_record && !cc_record->ptp) { + cc_record->outstanding_message = 0; + cc_record->activated = 1; + cc_record->mode.ptmp.recall_mode = bc->fac_in.u.CCBSRequest.Component.Result.RecallMode; + cc_record->mode.ptmp.reference_id = bc->fac_in.u.CCBSRequest.Component.Result.CCBSReference; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + break; + + default: + chan_misdn_log(0, bc->port, " --> not yet handled: facility type:0x%04X\n", + bc->fac_in.Function); + break; + } + break; +#if 0 /* We don't handle this yet */ + case Fac_CCBSInterrogate: + case Fac_CCNRInterrogate: + /* We don't handle this yet */ + break; + case Fac_StatusRequest: + /* We don't handle this yet */ + break; +#endif /* We don't handle this yet */ +#if 0 /* We don't handle this yet */ + case Fac_CCBS_T_Suspend: + case Fac_CCBS_T_Resume: + /* We don't handle this yet */ + break; +#endif /* We don't handle this yet */ + case Fac_CCBS_T_RemoteUserFree: + misdn_cc_handle_T_remote_user_free(bc); + break; + case Fac_CCBS_T_Available: + switch (event) { + case EVENT_ALERTING: + case EVENT_DISCONNECT: + /* CCBS-T/CCNR-T is available */ + if (ch && ch->peer) { + int set_id = 1; + + AST_LIST_LOCK(&misdn_cc_records_db); + if (ch->record_id == -1) { + cc_record = misdn_cc_new(); + } else { + /* + * We are doing a call-completion attempt + * or the switch is sending us extra call-completion + * availability indications (erroneously?). + */ + cc_record = misdn_cc_find_by_id(ch->record_id); + if (cc_record) { + if (cc_record->ptp && cc_record->mode.ptp.retention_enabled) { + /* + * Call-completion is still activated. + * The user does not have to request it again. + */ + chan_misdn_log(1, bc->port, " --> Call-completion request retention option is enabled\n"); + + set_id = 0; + } else { + if (cc_record->ptp && cc_record->mode.ptp.bc) { + /* + * The network request retention option + * is not on and the current call-completion + * request is to be disabled. + * + * We should get here only if EVENT_DISCONNECT + * + * Close the call-completion signaling link + */ + cc_record->mode.ptp.bc->fac_out.Function = Fac_None; + cc_record->mode.ptp.bc->out_cause = AST_CAUSE_NORMAL_CLEARING; + misdn_lib_send_event(cc_record->mode.ptp.bc, EVENT_RELEASE_COMPLETE); + } + + /* + * Resetup the existing record for a possible new + * call-completion request. + */ + new_record_id = misdn_cc_record_id_new(); + if (new_record_id < 0) { + /* Looks like we must keep the old id anyway. */ + } else { + cc_record->record_id = new_record_id; + ch->record_id = new_record_id; + } + cc_record->ptp = 1; + cc_record->port = bc->port; + memset(&cc_record->mode, 0, sizeof(cc_record->mode)); + cc_record->invoke_id = ++misdn_invoke_id; + cc_record->activated = 0; + cc_record->outstanding_message = 0; + cc_record->activation_requested = 0; + cc_record->error_code = FacError_None; + cc_record->reject_code = FacReject_None; + memset(&cc_record->remote_user_free, 0, sizeof(cc_record->remote_user_free)); + memset(&cc_record->b_free, 0, sizeof(cc_record->b_free)); + cc_record->time_created = time(NULL); + } + cc_record = NULL; + } else { + /* + * Where did the record go? We will have to recapture + * the call setup information. Unfortunately, some + * setup information may have been changed. + */ + ch->record_id = -1; + cc_record = misdn_cc_new(); + } + } + if (cc_record) { + ch->record_id = cc_record->record_id; + cc_record->ptp = 1; + cc_record->port = bc->port; + + /* Record call information for possible call-completion attempt. */ + cc_record->redial.caller = bc->caller; + cc_record->redial.dialed = bc->dialed; + cc_record->redial.setup_bc_hlc_llc = bc->setup_bc_hlc_llc; + cc_record->redial.capability = bc->capability; + cc_record->redial.hdlc = bc->hdlc; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + + /* Set MISDN_CC_RECORD_ID in original channel */ + if (ch->record_id != -1 && set_id) { + snprintf(buf, sizeof(buf), "%ld", ch->record_id); + } else { + buf[0] = 0; + } + misdn_cc_set_peer_var(ch->peer, MISDN_CC_RECORD_ID, buf); + } + break; + default: + chan_misdn_log(0, bc->port, + " --> Expected in a DISCONNECT or ALERTING message: facility type:0x%04X\n", + bc->fac_in.Function); + break; + } + break; + case Fac_CCBS_T_Request: + case Fac_CCNR_T_Request: + switch (bc->fac_in.u.CCBS_T_Request.ComponentType) { + case FacComponent_Result: + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_invoke(bc->port, bc->fac_in.u.CCBS_T_Request.InvokeID); + if (cc_record && cc_record->ptp) { + cc_record->outstanding_message = 0; + cc_record->activated = 1; + cc_record->mode.ptp.retention_enabled = + cc_record->mode.ptp.requested_retention + ? bc->fac_in.u.CCBS_T_Request.Component.Result.RetentionSupported + ? 1 : 0 + : 0; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + break; + + case FacComponent_Invoke: + /* We cannot be User-B in ptp mode. */ + default: + chan_misdn_log(0, bc->port, " --> not yet handled: facility type:0x%04X\n", + bc->fac_in.Function); + break; + } + break; + +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + case Fac_None: + break; + default: + chan_misdn_log(0, bc->port, " --> not yet handled: facility type:0x%04X\n", + bc->fac_in.Function); + break; + } +} /************************************************************/ /* Receive Events from isdn_lib here */ @@ -5019,6 +9197,9 @@ static void wait_for_digits(struct chan_list *ch, struct misdn_bchannel *bc, str static enum event_response_e cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) { +#if defined(AST_MISDN_ENHANCEMENTS) + struct misdn_cc_record *cc_record; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ struct chan_list *ch = find_chan_by_bc(cl_te, bc); if (event != EVENT_BCHAN_DATA && event != EVENT_TONE_GENERATE) { @@ -5051,6 +9232,7 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) case EVENT_RETRIEVE: case EVENT_NEW_BC: case EVENT_FACILITY: + case EVENT_REGISTER: break; case EVENT_RELEASE_COMPLETE: chan_misdn_log(1, bc->port, " --> no Ch, so we've already released.\n"); @@ -5079,7 +9261,7 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) } break; default: - if (!ch->ast || !MISDN_ASTERISK_PVT(ch->ast) || !MISDN_ASTERISK_TECH_PVT(ch->ast)) { + if (!ch->ast || !MISDN_ASTERISK_TECH_PVT(ch->ast)) { if (event != EVENT_BCHAN_DATA) { ast_log(LOG_NOTICE, "No Ast or No private Pointer in Event (%d:%s)\n", event, manager_isdn_get_info(event)); } @@ -5334,22 +9516,11 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) ast_set_callerid(chan, bc->caller.number, NULL, bc->caller.number); if (!ast_strlen_zero(bc->redirecting.from.number)) { - struct ast_party_redirecting redirecting; - /* Add configured prefix to redirecting.from.number */ misdn_add_number_prefix(bc->port, bc->redirecting.from.number_type, bc->redirecting.from.number, sizeof(bc->redirecting.from.number)); /* Update asterisk channel redirecting information */ - ast_party_redirecting_set_init(&redirecting, &chan->redirecting); - redirecting.from.number = bc->redirecting.from.number; - redirecting.from.number_type = - misdn_to_ast_ton(bc->redirecting.from.number_type) - | misdn_to_ast_plan(bc->redirecting.from.number_plan); - redirecting.from.number_presentation = - misdn_to_ast_pres(bc->redirecting.from.presentation) - | misdn_to_ast_screen(bc->redirecting.from.screening); - redirecting.reason = misdn_to_ast_reason(bc->redirecting.reason); - ast_channel_set_redirecting(chan, &redirecting); + misdn_copy_redirecting_to_ast(chan, &bc->redirecting); } pbx_builtin_setvar_helper(chan, "TRANSFERCAPABILITY", ast_transfercapability2str(bc->capability)); @@ -5394,6 +9565,10 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) } } + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } + /* Check for Pickup Request first */ if (!strcmp(chan->exten, ast_pickup_ext())) { if (!ch->noautorespond_on_setup) { @@ -5515,7 +9690,23 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) } break; } - +#if defined(AST_MISDN_ENHANCEMENTS) + case EVENT_REGISTER: + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } + /* + * Shut down this connection immediately. + * The current design of chan_misdn data structures + * does not allow the proper handling of inbound call records + * without an assigned B channel. Therefore, we cannot + * be the CCBS User-B party in a point-to-point setup. + */ + bc->fac_out.Function = Fac_None; + bc->out_cause = AST_CAUSE_NORMAL_CLEARING; + misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); + break; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ case EVENT_SETUP_ACKNOWLEDGE: ch->state = MISDN_CALLING_ACKNOWLEDGE; @@ -5523,6 +9714,10 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) update_name(ch->ast,bc->port,bc->channel); } + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } + if (!ast_strlen_zero(bc->infos_pending)) { /* TX Pending Infos */ strncat(bc->dialed.number, bc->infos_pending, sizeof(bc->dialed.number) - strlen(bc->dialed.number) - 1); @@ -5545,6 +9740,10 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) ch->state = MISDN_PROCEEDING; + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } + if (!ch->ast) { break; } @@ -5556,6 +9755,10 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) update_name(ch->ast, bc->port, bc->channel); } + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } + if (!bc->nt) { if (misdn_cap_is_speech(bc->capability) && misdn_inband_avail(bc)) { @@ -5577,6 +9780,10 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) break; } + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } + ast_queue_control(ch->ast, AST_CONTROL_RINGING); ast_setstate(ch->ast, AST_STATE_RINGING); @@ -5595,8 +9802,9 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) } break; case EVENT_CONNECT: - { - struct ast_party_connected_line connected; + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } /* we answer when we've got our very new L3 ID from the NT stack */ misdn_lib_send_event(bc, EVENT_CONNECT_ACKNOWLEDGE); @@ -5607,18 +9815,41 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) stop_indicate(ch); +#if defined(AST_MISDN_ENHANCEMENTS) + if (ch->record_id != -1) { + /* + * We will delete the associated call completion + * record since we now have a completed call. + * We will not wait/depend on the network to tell + * us to delete it. + */ + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(ch->record_id); + if (cc_record) { + if (cc_record->ptp && cc_record->mode.ptp.bc) { + /* Close the call-completion signaling link */ + cc_record->mode.ptp.bc->fac_out.Function = Fac_None; + cc_record->mode.ptp.bc->out_cause = AST_CAUSE_NORMAL_CLEARING; + misdn_lib_send_event(cc_record->mode.ptp.bc, EVENT_RELEASE_COMPLETE); + } + misdn_cc_delete(cc_record); + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + ch->record_id = -1; + if (ch->peer) { + misdn_cc_set_peer_var(ch->peer, MISDN_CC_RECORD_ID, ""); + + ao2_ref(ch->peer, -1); + ch->peer = NULL; + } + } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + /* Add configured prefix to connected.number */ misdn_add_number_prefix(bc->port, bc->connected.number_type, bc->connected.number, sizeof(bc->connected.number)); /* Update the connected line information on the other channel */ - ast_party_connected_line_init(&connected); - connected.id.number = bc->connected.number; - connected.id.number_type = misdn_to_ast_ton(bc->connected.number_type) - | misdn_to_ast_plan(bc->connected.number_plan); - connected.id.number_presentation = misdn_to_ast_pres(bc->connected.presentation) - | misdn_to_ast_screen(bc->connected.screening); - connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; - ast_channel_queue_connected_line_update(ch->ast, &connected); + misdn_queue_connected_line_update(ch->ast, &bc->connected, AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER); ch->l3id = bc->l3_id; ch->addr = bc->addr; @@ -5629,7 +9860,6 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) ast_queue_control(ch->ast, AST_CONTROL_ANSWER); break; - } case EVENT_CONNECT_ACKNOWLEDGE: ch->l3id = bc->l3_id; ch->addr = bc->addr; @@ -5643,6 +9873,10 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) if (ch) { struct chan_list *holded_ch = find_holded(cl_te, bc); + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } + chan_misdn_log(3, bc->port, " --> org:%d nt:%d, inbandavail:%d state:%d\n", ch->originator, bc->nt, misdn_inband_avail(bc), ch->state); if (ch->originator == ORG_AST && !bc->nt && misdn_inband_avail(bc) && ch->state != MISDN_CONNECTED) { /* If there's inband information available (e.g. a @@ -5689,6 +9923,10 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) } break; case EVENT_RELEASE: + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } + bc->need_disconnect = 0; bc->need_release = 0; @@ -5696,15 +9934,33 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) release_chan(bc); break; case EVENT_RELEASE_COMPLETE: + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } + bc->need_disconnect = 0; bc->need_release = 0; bc->need_release_complete = 0; - stop_bc_tones(ch); - hangup_chan(ch); - if (ch) { + stop_bc_tones(ch); + hangup_chan(ch); ch->state = MISDN_CLEANING; +#if defined(AST_MISDN_ENHANCEMENTS) + } else { + /* + * A call-completion signaling link established with + * REGISTER does not have a struct chan_list record + * associated with it. + */ + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_bc(bc); + if (cc_record) { + /* The call-completion signaling link is closed. */ + misdn_cc_delete(cc_record); + } + AST_LIST_UNLOCK(&misdn_cc_records_db); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ } release_chan(bc); @@ -5931,58 +10187,68 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) } break; } - case EVENT_FACILITY: - print_facility(&(bc->fac_in), bc); - - switch (bc->fac_in.Function) { -#ifdef HAVE_MISDN_FAC_RESULT - case Fac_RESULT: + case EVENT_NOTIFY: + if (bc->redirecting.to_changed) { + /* Add configured prefix to redirecting.to.number */ + misdn_add_number_prefix(bc->port, bc->redirecting.to.number_type, + bc->redirecting.to.number, sizeof(bc->redirecting.to.number)); + } + switch (bc->notify_description_code) { + case mISDN_NOTIFY_CODE_DIVERSION_ACTIVATED: + /* Ignore for now. */ + bc->redirecting.to_changed = 0; break; -#endif - case Fac_CD: - if (ch) { - struct ast_channel *bridged = ast_bridged_channel(ch->ast); - struct chan_list *ch_br; - if (bridged && MISDN_ASTERISK_TECH_PVT(bridged)) { - ch_br = MISDN_ASTERISK_TECH_PVT(bridged); - /*ch->state = MISDN_FACILITY_DEFLECTED;*/ - if (ch_br->bc) { - if (ast_exists_extension(bridged, ch->context, (char *) bc->fac_in.u.CDeflection.DeflectedToNumber, 1, bc->caller.number)) { - ch_br->state = MISDN_DIALING; - if (pbx_start_chan(ch_br) < 0) { - chan_misdn_log(-1, ch_br->bc->port, "ast_pbx_start returned < 0 in misdn_overlap_dial_task\n"); - } - } + case mISDN_NOTIFY_CODE_CALL_IS_DIVERTING: + if (bc->redirecting.to_changed) { + bc->redirecting.to_changed = 0; + if (ch && ch->ast) { + switch (ch->state) { + case MISDN_ALERTING: + /* Call is deflecting after we have seen an ALERTING message */ + bc->redirecting.reason = mISDN_REDIRECTING_REASON_NO_REPLY; + break; + default: + /* Call is deflecting for call forwarding unconditional or busy reason. */ + bc->redirecting.reason = mISDN_REDIRECTING_REASON_UNKNOWN; + break; } + misdn_copy_redirecting_to_ast(ch->ast, &bc->redirecting); + ast_channel_queue_redirecting_update(ch->ast, &ch->ast->redirecting); } - misdn_lib_send_event(bc, EVENT_DISCONNECT); } break; - case Fac_AOCDCurrency: - if (ch) { - bc->AOCDtype = Fac_AOCDCurrency; - memcpy(&(bc->AOCD.currency), &(bc->fac_in.u.AOCDcur), sizeof(bc->AOCD.currency)); - bc->AOCD_need_export = 1; - export_aoc_vars(ch->originator, ch->ast, bc); + case mISDN_NOTIFY_CODE_CALL_TRANSFER_ALERTING: + if (bc->redirecting.to_changed) { + bc->redirecting.to_changed = 0; + if (ch && ch->ast) { + misdn_queue_connected_line_update(ch->ast, &bc->redirecting.to, + AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER_ALERTING); + } } break; - case Fac_AOCDChargingUnit: - if (ch) { - bc->AOCDtype = Fac_AOCDChargingUnit; - memcpy(&(bc->AOCD.chargingUnit), &(bc->fac_in.u.AOCDchu), sizeof(bc->AOCD.chargingUnit)); - bc->AOCD_need_export = 1; - export_aoc_vars(ch->originator, ch->ast, bc); + case mISDN_NOTIFY_CODE_CALL_TRANSFER_ACTIVE: + if (bc->redirecting.to_changed) { + bc->redirecting.to_changed = 0; + if (ch && ch->ast) { + misdn_queue_connected_line_update(ch->ast, &bc->redirecting.to, + AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER); + } } break; - case Fac_None: -#ifdef HAVE_MISDN_FAC_ERROR - case Fac_ERROR: -#endif - break; default: - chan_misdn_log(0, bc->port," --> not yet handled: facility type:%d\n", bc->fac_in.Function); + bc->redirecting.to_changed = 0; + chan_misdn_log(0, bc->port," --> not yet handled: notify code:0x%02X\n", + bc->notify_description_code); + break; + } + break; + case EVENT_FACILITY: + if (bc->fac_in.Function == Fac_None) { + /* This is a FACILITY message so we MUST have a facility ie */ + chan_misdn_log(0, bc->port," --> Missing facility ie or unknown facility ie contents.\n"); + } else { + misdn_facility_ie_handler(event, bc, ch); } - break; case EVENT_RESTART: if (!bc->dummy) { @@ -6000,6 +10266,134 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) /** TE STUFF END **/ +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Get call completion record information. + * + * \param chan Asterisk channel to operate upon. (Not used) + * \param function_name Name of the function that called us. + * \param function_args Argument string passed to function (Could be NULL) + * \param buf Buffer to put returned string. + * \param size Size of the supplied buffer including the null terminator. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int misdn_cc_read(struct ast_channel *chan, const char *function_name, + char *function_args, char *buf, size_t size) +{ + char *parse; + struct misdn_cc_record *cc_record; + + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(cc_id); /* Call completion record ID value. */ + AST_APP_ARG(get_name); /* Name of what to get */ + AST_APP_ARG(other); /* Any extraneous garbage arguments */ + ); + + /* Ensure that the buffer is empty */ + *buf = 0; + + if (ast_strlen_zero(function_args)) { + ast_log(LOG_ERROR, "Function '%s' requires arguments.\n", function_name); + return -1; + } + + parse = ast_strdupa(function_args); + AST_STANDARD_APP_ARGS(args, parse); + + if (!args.argc || ast_strlen_zero(args.cc_id)) { + ast_log(LOG_ERROR, "Function '%s' missing call completion record ID.\n", + function_name); + return -1; + } + if (!isdigit(*args.cc_id)) { + ast_log(LOG_ERROR, "Function '%s' call completion record ID must be numeric.\n", + function_name); + return -1; + } + + if (ast_strlen_zero(args.get_name)) { + ast_log(LOG_ERROR, "Function '%s' missing what-to-get parameter.\n", + function_name); + return -1; + } + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(atoi(args.cc_id)); + if (cc_record) { + if (!strcasecmp("a-all", args.get_name)) { + snprintf(buf, size, "\"%s\" <%s>", cc_record->redial.caller.name, + cc_record->redial.caller.number); + } else if (!strcasecmp("a-name", args.get_name)) { + ast_copy_string(buf, cc_record->redial.caller.name, size); + } else if (!strncasecmp("a-num", args.get_name, 5)) { + ast_copy_string(buf, cc_record->redial.caller.number, size); + } else if (!strcasecmp("a-ton", args.get_name)) { + snprintf(buf, size, "%d", + misdn_to_ast_plan(cc_record->redial.caller.number_plan) + | misdn_to_ast_ton(cc_record->redial.caller.number_type)); + } else if (!strncasecmp("a-pres", args.get_name, 6)) { + ast_copy_string(buf, ast_named_caller_presentation( + misdn_to_ast_pres(cc_record->redial.caller.presentation) + | misdn_to_ast_screen(cc_record->redial.caller.screening)), size); + } else if (!strcasecmp("a-busy", args.get_name)) { + ast_copy_string(buf, cc_record->party_a_free ? "no" : "yes", size); + } else if (!strncasecmp("b-num", args.get_name, 5)) { + ast_copy_string(buf, cc_record->redial.dialed.number, size); + } else if (!strcasecmp("b-ton", args.get_name)) { + snprintf(buf, size, "%d", + misdn_to_ast_plan(cc_record->redial.dialed.number_plan) + | misdn_to_ast_ton(cc_record->redial.dialed.number_type)); + } else if (!strcasecmp("port", args.get_name)) { + snprintf(buf, size, "%d", cc_record->port); + } else if (!strcasecmp("available-notify-priority", args.get_name)) { + snprintf(buf, size, "%d", cc_record->remote_user_free.priority); + } else if (!strcasecmp("available-notify-exten", args.get_name)) { + ast_copy_string(buf, cc_record->remote_user_free.exten, size); + } else if (!strcasecmp("available-notify-context", args.get_name)) { + ast_copy_string(buf, cc_record->remote_user_free.context, size); + } else if (!strcasecmp("busy-notify-priority", args.get_name)) { + snprintf(buf, size, "%d", cc_record->b_free.priority); + } else if (!strcasecmp("busy-notify-exten", args.get_name)) { + ast_copy_string(buf, cc_record->b_free.exten, size); + } else if (!strcasecmp("busy-notify-context", args.get_name)) { + ast_copy_string(buf, cc_record->b_free.context, size); + } else { + AST_LIST_UNLOCK(&misdn_cc_records_db); + ast_log(LOG_ERROR, "Function '%s': Unknown what-to-get '%s'.\n", function_name, args.get_name); + return -1; + } + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + + return 0; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static struct ast_custom_function misdn_cc_function = { + .name = "mISDN_CC", + .synopsis = "Get call completion record information.", + .syntax = "mISDN_CC(${MISDN_CC_RECORD_ID},<what-to-get>)", + .desc = + "mISDN_CC(${MISDN_CC_RECORD_ID},<what-to-get>)\n" + "The following can be retrieved:\n" + "\"a-num\", \"a-name\", \"a-all\", \"a-ton\", \"a-pres\", \"a-busy\",\n" + "\"b-num\", \"b-ton\", \"port\",\n" + " User-A is available for call completion:\n" + " \"available-notify-priority\",\n" + " \"available-notify-exten\",\n" + " \"available-notify-context\",\n" + " User-A is busy:\n" + " \"busy-notify-priority\",\n" + " \"busy-notify-exten\",\n" + " \"busy-notify-context\"\n", + .read = misdn_cc_read, +}; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + /****************************************** * * Asterisk Channel Endpoint END @@ -6026,6 +10420,10 @@ static int unload_module(void) ast_unregister_application("misdn_set_opt"); ast_unregister_application("misdn_facility"); ast_unregister_application("misdn_check_l2l1"); +#if defined(AST_MISDN_ENHANCEMENTS) + ast_unregister_application(misdn_command_name); + ast_custom_function_unregister(&misdn_cc_function); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ ast_channel_unregister(&misdn_tech); @@ -6041,6 +10439,10 @@ static int unload_module(void) } ast_free(misdn_ports); +#if defined(AST_MISDN_ENHANCEMENTS) + misdn_cc_destroy(); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + return 0; } @@ -6070,6 +10472,10 @@ static int load_module(void) } g_config_initialized = 1; +#if defined(AST_MISDN_ENHANCEMENTS) + misdn_cc_init(); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + misdn_debug = ast_malloc(sizeof(int) * (max_ports + 1)); if (!misdn_debug) { ast_log(LOG_ERROR, "Out of memory for misdn_debug\n"); @@ -6184,6 +10590,33 @@ static int load_module(void) "exten => _X.,n,dial(mISDN/g:out/${EXTEN})\n" ); +#if defined(AST_MISDN_ENHANCEMENTS) + ast_register_application(misdn_command_name, misdn_command_exec, misdn_command_name, + "misdn_command(<command>[,<options>])\n" + "The following commands are defined:\n" + "cc-initialize\n" + " Setup mISDN support for call completion\n" + " Must call before doing any Dial() involving call completion.\n" + "ccnr-request,${MISDN_CC_RECORD_ID},<notify-context>,<user-a-extension>,<priority>\n" + " Request Call Completion No Reply activation\n" + "ccbs-request,${MISDN_CC_RECORD_ID},<notify-context>,<user-a-extension>,<priority>\n" + " Request Call Completion Busy Subscriber activation\n" + "cc-b-free,${MISDN_CC_RECORD_ID},<notify-context>,<user-a-extension>,<priority>\n" + " Set the dialplan location to notify when User-B is available but User-A is busy.\n" + " Setting this dialplan location is optional.\n" + "cc-a-busy,${MISDN_CC_RECORD_ID},<yes/no>\n" + " Set the busy status of call completion User-A\n" + "cc-deactivate,${MISDN_CC_RECORD_ID}\n" + " Deactivate the identified call completion request\n" + "\n" + "MISDN_CC_RECORD_ID is set when Dial() returns and call completion is possible\n" + "MISDN_CC_STATUS is set to ACTIVATED or ERROR after the call completion\n" + "activation request.\n" + "MISDN_ERROR_MSG is set to a descriptive message on error.\n" + ); + + ast_custom_function_register(&misdn_cc_function); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ misdn_cfg_get(0, MISDN_GEN_TRACEFILE, global_tracefile, sizeof(global_tracefile)); @@ -6214,10 +10647,632 @@ static int reload(void) /*** SOME APPS ;)***/ +#if defined(AST_MISDN_ENHANCEMENTS) +/*! +* \brief misdn_command arguments container. +*/ +AST_DEFINE_APP_ARGS_TYPE(misdn_command_args, + AST_APP_ARG(name); /* Subcommand name */ + AST_APP_ARG(arg)[10 + 1]; /* Subcommand arguments */ +); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static void misdn_cc_caller_destroy(void *obj) +{ + /* oh snap! */ +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static struct misdn_cc_caller *misdn_cc_caller_alloc(struct ast_channel *chan) +{ + struct misdn_cc_caller *cc_caller; + + if (!(cc_caller = ao2_alloc(sizeof(*cc_caller), misdn_cc_caller_destroy))) { + return NULL; + } + + cc_caller->chan = chan; + + return cc_caller; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief misdn_command(cc-initialize) subcommand handler + * + * \param chan Asterisk channel to operate upon. + * \param subcommand Arguments for the subcommand + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int misdn_command_cc_initialize(struct ast_channel *chan, struct misdn_command_args *subcommand) +{ + struct misdn_cc_caller *cc_caller; + struct ast_datastore *datastore; + + if (!(cc_caller = misdn_cc_caller_alloc(chan))) { + return -1; + } + + if (!(datastore = ast_datastore_alloc(&misdn_cc_ds_info, NULL))) { + ao2_ref(cc_caller, -1); + return -1; + } + + ast_channel_lock(chan); + + /* Inherit reference */ + datastore->data = cc_caller; + cc_caller = NULL; + + datastore->inheritance = DATASTORE_INHERIT_FOREVER; + + ast_channel_datastore_add(chan, datastore); + + ast_channel_unlock(chan); + + return 0; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief misdn_command(cc-deactivate) subcommand handler + * + * \details + * misdn_command(cc-deactivate,${MISDN_CC_RECORD_ID}) + * Deactivate a call completion service instance. + * + * \param chan Asterisk channel to operate upon. + * \param subcommand Arguments for the subcommand + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int misdn_command_cc_deactivate(struct ast_channel *chan, struct misdn_command_args *subcommand) +{ + long record_id; + const char *error_str; + struct misdn_cc_record *cc_record; + struct misdn_bchannel *bc; + struct misdn_bchannel dummy; + + static const char cmd_help[] = "%s(%s,${MISDN_CC_RECORD_ID})\n"; + + if (ast_strlen_zero(subcommand->arg[0]) || !isdigit(*subcommand->arg[0])) { + ast_log(LOG_WARNING, cmd_help, misdn_command_name, subcommand->name); + return -1; + } + record_id = atol(subcommand->arg[0]); + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(record_id); + if (cc_record && 0 <= cc_record->port) { + if (cc_record->ptp) { + if (cc_record->mode.ptp.bc) { + /* Close the call-completion signaling link */ + bc = cc_record->mode.ptp.bc; + bc->fac_out.Function = Fac_None; + bc->out_cause = AST_CAUSE_NORMAL_CLEARING; + misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); + } + misdn_cc_delete(cc_record); + } else if (cc_record->activated) { + cc_record->error_code = FacError_None; + cc_record->reject_code = FacReject_None; + cc_record->invoke_id = ++misdn_invoke_id; + cc_record->outstanding_message = 1; + + /* Build message */ + misdn_make_dummy(&dummy, cc_record->port, 0, misdn_lib_port_is_nt(cc_record->port), 0); + dummy.fac_out.Function = Fac_CCBSDeactivate; + dummy.fac_out.u.CCBSDeactivate.InvokeID = cc_record->invoke_id; + dummy.fac_out.u.CCBSDeactivate.ComponentType = FacComponent_Invoke; + dummy.fac_out.u.CCBSDeactivate.Component.Invoke.CCBSReference = cc_record->mode.ptmp.reference_id; + + /* Send message */ + print_facility(&dummy.fac_out, &dummy); + misdn_lib_send_event(&dummy, EVENT_FACILITY); + } + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + + /* Wait for the response to the call completion deactivation request. */ + misdn_cc_response_wait(chan, MISDN_CC_REQUEST_WAIT_MAX, record_id); + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(record_id); + if (cc_record) { + if (cc_record->port < 0) { + /* The network did not tell us that call completion was available. */ + error_str = NULL; + } else if (cc_record->outstanding_message) { + cc_record->outstanding_message = 0; + error_str = misdn_no_response_from_network; + } else if (cc_record->reject_code != FacReject_None) { + error_str = misdn_to_str_reject_code(cc_record->reject_code); + } else if (cc_record->reject_code != FacError_None) { + error_str = misdn_to_str_error_code(cc_record->error_code); + } else { + error_str = NULL; + } + + misdn_cc_delete(cc_record); + } else { + error_str = NULL; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + if (error_str) { + ast_verb(1, "%s(%s) diagnostic '%s' on channel %s\n", + misdn_command_name, subcommand->name, error_str, chan->name); + pbx_builtin_setvar_helper(chan, MISDN_ERROR_MSG, error_str); + } + + return 0; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief misdn_command(cc-a-busy) subcommand handler + * + * \details + * misdn_command(cc-a-busy,${MISDN_CC_RECORD_ID},<yes/no>) + * Set the status of User-A for a call completion service instance. + * + * \param chan Asterisk channel to operate upon. + * \param subcommand Arguments for the subcommand + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int misdn_command_cc_a_busy(struct ast_channel *chan, struct misdn_command_args *subcommand) +{ + long record_id; + int party_a_free; + struct misdn_cc_record *cc_record; + struct misdn_bchannel *bc; + + static const char cmd_help[] = "%s(%s,${MISDN_CC_RECORD_ID},<yes/no>)\n"; + + if (ast_strlen_zero(subcommand->arg[0]) || !isdigit(*subcommand->arg[0])) { + ast_log(LOG_WARNING, cmd_help, misdn_command_name, subcommand->name); + return -1; + } + record_id = atol(subcommand->arg[0]); + + if (ast_true(subcommand->arg[1])) { + party_a_free = 0; + } else if (ast_false(subcommand->arg[1])) { + party_a_free = 1; + } else { + ast_log(LOG_WARNING, cmd_help, misdn_command_name, subcommand->name); + return -1; + } + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(record_id); + if (cc_record && cc_record->party_a_free != party_a_free) { + /* User-A's status has changed */ + cc_record->party_a_free = party_a_free; + + if (cc_record->ptp && cc_record->mode.ptp.bc) { + cc_record->error_code = FacError_None; + cc_record->reject_code = FacReject_None; + + /* Build message */ + bc = cc_record->mode.ptp.bc; + if (cc_record->party_a_free) { + bc->fac_out.Function = Fac_CCBS_T_Resume; + bc->fac_out.u.CCBS_T_Resume.InvokeID = ++misdn_invoke_id; + } else { + bc->fac_out.Function = Fac_CCBS_T_Suspend; + bc->fac_out.u.CCBS_T_Suspend.InvokeID = ++misdn_invoke_id; + } + + /* Send message */ + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_FACILITY); + } + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + + return 0; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief misdn_command(cc-b-free) subcommand handler + * + * \details + * misdn_command(cc-b-free,${MISDN_CC_RECORD_ID},<notify-context>,<user-a-extension>,<priority>) + * Set the dialplan location to notify when User-B is free and User-A is busy. + * + * \param chan Asterisk channel to operate upon. + * \param subcommand Arguments for the subcommand + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int misdn_command_cc_b_free(struct ast_channel *chan, struct misdn_command_args *subcommand) +{ + unsigned index; + long record_id; + int priority; + char *context; + char *exten; + struct misdn_cc_record *cc_record; + + static const char cmd_help[] = "%s(%s,${MISDN_CC_RECORD_ID},<notify-context>,<user-a-extension>,<priority>)\n"; + + /* Check that all arguments are present */ + for (index = 0; index < 4; ++index) { + if (ast_strlen_zero(subcommand->arg[index])) { + ast_log(LOG_WARNING, cmd_help, misdn_command_name, subcommand->name); + return -1; + } + } + + /* These must be numeric */ + if (!isdigit(*subcommand->arg[0]) || !isdigit(*subcommand->arg[3])) { + ast_log(LOG_WARNING, cmd_help, misdn_command_name, subcommand->name); + return -1; + } + + record_id = atol(subcommand->arg[0]); + context = subcommand->arg[1]; + exten = subcommand->arg[2]; + priority = atoi(subcommand->arg[3]); + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(record_id); + if (cc_record) { + /* Save User-B free information */ + ast_copy_string(cc_record->b_free.context, context, sizeof(cc_record->b_free.context)); + ast_copy_string(cc_record->b_free.exten, exten, sizeof(cc_record->b_free.exten)); + cc_record->b_free.priority = priority; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + + return 0; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +struct misdn_cc_request { + enum FacFunction ptmp; + enum FacFunction ptp; +}; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief misdn_command(ccbs-request/ccnr-request) subcommand handler helper + * + * \details + * misdn_command(ccbs-request,${MISDN_CC_RECORD_ID},<notify-context>,<user-a-extension>,<priority>) + * misdn_command(ccnr-request,${MISDN_CC_RECORD_ID},<notify-context>,<user-a-extension>,<priority>) + * Set the dialplan location to notify when User-B is free and User-A is free. + * + * \param chan Asterisk channel to operate upon. + * \param subcommand Arguments for the subcommand + * \param request Which call-completion request message to generate. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int misdn_command_cc_request(struct ast_channel *chan, struct misdn_command_args *subcommand, const struct misdn_cc_request *request) +{ + unsigned index; + int request_retention; + long record_id; + int priority; + char *context; + char *exten; + const char *error_str; + struct misdn_cc_record *cc_record; + struct misdn_bchannel *bc; + struct misdn_bchannel dummy; + struct misdn_party_id id; + + static const char cmd_help[] = "%s(%s,${MISDN_CC_RECORD_ID},<notify-context>,<user-a-extension>,<priority>)\n"; + + /* Check that all arguments are present */ + for (index = 0; index < 4; ++index) { + if (ast_strlen_zero(subcommand->arg[index])) { + ast_log(LOG_WARNING, cmd_help, misdn_command_name, subcommand->name); + return -1; + } + } + + /* These must be numeric */ + if (!isdigit(*subcommand->arg[0]) || !isdigit(*subcommand->arg[3])) { + ast_log(LOG_WARNING, cmd_help, misdn_command_name, subcommand->name); + return -1; + } + + record_id = atol(subcommand->arg[0]); + context = subcommand->arg[1]; + exten = subcommand->arg[2]; + priority = atoi(subcommand->arg[3]); + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(record_id); + if (cc_record) { + /* Save User-B free information */ + ast_copy_string(cc_record->remote_user_free.context, context, + sizeof(cc_record->remote_user_free.context)); + ast_copy_string(cc_record->remote_user_free.exten, exten, + sizeof(cc_record->remote_user_free.exten)); + cc_record->remote_user_free.priority = priority; + + if (0 <= cc_record->port) { + if (cc_record->ptp) { + if (!cc_record->mode.ptp.bc) { + bc = misdn_lib_get_register_bc(cc_record->port); + if (bc) { + cc_record->mode.ptp.bc = bc; + cc_record->error_code = FacError_None; + cc_record->reject_code = FacReject_None; + cc_record->invoke_id = ++misdn_invoke_id; + cc_record->outstanding_message = 1; + cc_record->activation_requested = 1; + + misdn_cfg_get(bc->port, MISDN_CFG_CC_REQUEST_RETENTION, + &request_retention, sizeof(request_retention)); + cc_record->mode.ptp.requested_retention = request_retention ? 1 : 0; + + /* Build message */ + bc->fac_out.Function = request->ptp; + bc->fac_out.u.CCBS_T_Request.InvokeID = cc_record->invoke_id; + bc->fac_out.u.CCBS_T_Request.ComponentType = FacComponent_Invoke; + bc->fac_out.u.CCBS_T_Request.Component.Invoke.Q931ie = + cc_record->redial.setup_bc_hlc_llc; + memset(&id, 0, sizeof(id)); + id.number_plan = cc_record->redial.dialed.number_plan; + id.number_type = cc_record->redial.dialed.number_type; + ast_copy_string(id.number, cc_record->redial.dialed.number, + sizeof(id.number)); + misdn_Address_fill( + &bc->fac_out.u.CCBS_T_Request.Component.Invoke.Destination, + &id); + misdn_Address_fill( + &bc->fac_out.u.CCBS_T_Request.Component.Invoke.Originating, + &cc_record->redial.caller); + bc->fac_out.u.CCBS_T_Request.Component.Invoke.PresentationAllowedIndicatorPresent = 1; + bc->fac_out.u.CCBS_T_Request.Component.Invoke.PresentationAllowedIndicator = + (cc_record->redial.caller.presentation != 0) ? 0 : 1; + bc->fac_out.u.CCBS_T_Request.Component.Invoke.RetentionSupported = + request_retention ? 1 : 0; + + /* Send message */ + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_REGISTER); + } + } + } else { + cc_record->error_code = FacError_None; + cc_record->reject_code = FacReject_None; + cc_record->invoke_id = ++misdn_invoke_id; + cc_record->outstanding_message = 1; + cc_record->activation_requested = 1; + + /* Build message */ + misdn_make_dummy(&dummy, cc_record->port, 0, + misdn_lib_port_is_nt(cc_record->port), 0); + dummy.fac_out.Function = request->ptmp; + dummy.fac_out.u.CCBSRequest.InvokeID = cc_record->invoke_id; + dummy.fac_out.u.CCBSRequest.ComponentType = FacComponent_Invoke; + dummy.fac_out.u.CCBSRequest.Component.Invoke.CallLinkageID = + cc_record->mode.ptmp.linkage_id; + + /* Send message */ + print_facility(&dummy.fac_out, &dummy); + misdn_lib_send_event(&dummy, EVENT_FACILITY); + } + } + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + + /* Wait for the response to the call completion request. */ + misdn_cc_response_wait(chan, MISDN_CC_REQUEST_WAIT_MAX, record_id); + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(record_id); + if (cc_record) { + if (!cc_record->activated) { + if (cc_record->port < 0) { + /* The network did not tell us that call completion was available. */ + error_str = "No port number"; + } else if (cc_record->outstanding_message) { + cc_record->outstanding_message = 0; + error_str = misdn_no_response_from_network; + } else if (cc_record->reject_code != FacReject_None) { + error_str = misdn_to_str_reject_code(cc_record->reject_code); + } else if (cc_record->error_code != FacError_None) { + error_str = misdn_to_str_error_code(cc_record->error_code); + } else if (cc_record->ptp) { + if (cc_record->mode.ptp.bc) { + error_str = "Call-completion already requested"; + } else { + error_str = "Could not allocate call-completion signaling link"; + } + } else { + /* Should never happen. */ + error_str = "Unexpected error"; + } + + /* No need to keep the call completion record. */ + if (cc_record->ptp && cc_record->mode.ptp.bc) { + /* Close the call-completion signaling link */ + bc = cc_record->mode.ptp.bc; + bc->fac_out.Function = Fac_None; + bc->out_cause = AST_CAUSE_NORMAL_CLEARING; + misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); + } + misdn_cc_delete(cc_record); + } else { + error_str = NULL; + } + } else { + error_str = misdn_cc_record_not_found; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + if (error_str) { + ast_verb(1, "%s(%s) diagnostic '%s' on channel %s\n", + misdn_command_name, subcommand->name, error_str, chan->name); + pbx_builtin_setvar_helper(chan, MISDN_ERROR_MSG, error_str); + pbx_builtin_setvar_helper(chan, MISDN_CC_STATUS, "ERROR"); + } else { + pbx_builtin_setvar_helper(chan, MISDN_CC_STATUS, "ACTIVATED"); + } + + return 0; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief misdn_command(ccbs-request) subcommand handler + * + * \details + * misdn_command(ccbs-request,${MISDN_CC_RECORD_ID},<notify-context>,<user-a-extension>,<priority>) + * Set the dialplan location to notify when User-B is free and User-A is free. + * + * \param chan Asterisk channel to operate upon. + * \param subcommand Arguments for the subcommand + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int misdn_command_ccbs_request(struct ast_channel *chan, struct misdn_command_args *subcommand) +{ + static const struct misdn_cc_request request = { + .ptmp = Fac_CCBSRequest, + .ptp = Fac_CCBS_T_Request + }; + + return misdn_command_cc_request(chan, subcommand, &request); +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief misdn_command(ccnr-request) subcommand handler + * + * \details + * misdn_command(ccnr-request,${MISDN_CC_RECORD_ID},<notify-context>,<user-a-extension>,<priority>) + * Set the dialplan location to notify when User-B is free and User-A is free. + * + * \param chan Asterisk channel to operate upon. + * \param subcommand Arguments for the subcommand + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int misdn_command_ccnr_request(struct ast_channel *chan, struct misdn_command_args *subcommand) +{ + static const struct misdn_cc_request request = { + .ptmp = Fac_CCNRRequest, + .ptp = Fac_CCNR_T_Request + }; + + return misdn_command_cc_request(chan, subcommand, &request); +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +struct misdn_command_table { + /*! \brief subcommand name */ + const char *name; + + /*! \brief subcommand handler */ + int (*func)(struct ast_channel *chan, struct misdn_command_args *subcommand); + + /*! \brief TRUE if the subcommand can only be executed on mISDN channels */ + int misdn_only; +}; +static const struct misdn_command_table misdn_commands[] = { +/* *INDENT-OFF* */ + /* subcommand-name subcommand-handler mISDN only */ + { "cc-initialize", misdn_command_cc_initialize, 0 }, + { "cc-deactivate", misdn_command_cc_deactivate, 0 }, + { "cc-a-busy", misdn_command_cc_a_busy, 0 }, + { "cc-b-free", misdn_command_cc_b_free, 0 }, + { "ccbs-request", misdn_command_ccbs_request, 0 }, + { "ccnr-request", misdn_command_ccnr_request, 0 }, +/* *INDENT-ON* */ +}; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief misdn_command() dialplan application. + * + * \param chan Asterisk channel to operate upon. + * \param data Application options string. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int misdn_command_exec(struct ast_channel *chan, void *data) +{ + char *parse; + unsigned index; + struct misdn_command_args subcommand; + + if (ast_strlen_zero((char *) data)) { + ast_log(LOG_ERROR, "%s requires arguments\n", misdn_command_name); + return -1; + } + + ast_log(LOG_DEBUG, "%s(%s)\n", misdn_command_name, (char *) data); + + parse = ast_strdupa(data); + AST_STANDARD_APP_ARGS(subcommand, parse); + if (!subcommand.argc || ast_strlen_zero(subcommand.name)) { + ast_log(LOG_ERROR, "%s requires a subcommand\n", misdn_command_name); + return -1; + } + + for (index = 0; index < ARRAY_LEN(misdn_commands); ++index) { + if (strcasecmp(misdn_commands[index].name, subcommand.name) == 0) { + strcpy(subcommand.name, misdn_commands[index].name); + if (misdn_commands[index].misdn_only + && strcasecmp(chan->tech->type, misdn_type) != 0) { + ast_log(LOG_WARNING, + "%s(%s) only makes sense with %s channels!\n", + misdn_command_name, subcommand.name, misdn_type); + return -1; + } + return misdn_commands[index].func(chan, &subcommand); + } + } + + ast_log(LOG_WARNING, "%s(%s) subcommand is unknown\n", misdn_command_name, + subcommand.name); + return -1; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + static int misdn_facility_exec(struct ast_channel *chan, void *data) { struct chan_list *ch = MISDN_ASTERISK_TECH_PVT(chan); char *parse; + unsigned max_len; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(facility_type); @@ -6226,8 +11281,8 @@ static int misdn_facility_exec(struct ast_channel *chan, void *data) chan_misdn_log(0, 0, "TYPE: %s\n", chan->tech->type); - if (strcasecmp(chan->tech->type, "mISDN")) { - ast_log(LOG_WARNING, "misdn_facility makes only sense with chan_misdn channels!\n"); + if (strcasecmp(chan->tech->type, misdn_type)) { + ast_log(LOG_WARNING, "misdn_facility only makes sense with %s channels!\n", misdn_type); return -1; } @@ -6249,14 +11304,41 @@ static int misdn_facility_exec(struct ast_channel *chan, void *data) ast_log(LOG_WARNING, "Facility: Call Deflection requires an argument: Number\n"); } - if (strlen(args.arg[0]) >= sizeof(ch->bc->fac_out.u.CDeflection.DeflectedToNumber)) { +#if defined(AST_MISDN_ENHANCEMENTS) + max_len = sizeof(ch->bc->fac_out.u.CallDeflection.Component.Invoke.Deflection.Party.Number) - 1; + if (max_len < strlen(args.arg[0])) { ast_log(LOG_WARNING, - "Facility: Number argument too long (up to %d digits are allowed). Ignoring.\n", - (int) sizeof(ch->bc->fac_out.u.CDeflection.DeflectedToNumber)); + "Facility: Number argument too long (up to %u digits are allowed). Ignoring.\n", + max_len); + return 0; + } + ch->bc->fac_out.Function = Fac_CallDeflection; + ch->bc->fac_out.u.CallDeflection.InvokeID = ++misdn_invoke_id; + ch->bc->fac_out.u.CallDeflection.ComponentType = FacComponent_Invoke; + ch->bc->fac_out.u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUserPresent = 1; + ch->bc->fac_out.u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUser = 0; + ch->bc->fac_out.u.CallDeflection.Component.Invoke.Deflection.Party.Type = 0;/* unknown */ + ch->bc->fac_out.u.CallDeflection.Component.Invoke.Deflection.Party.LengthOfNumber = strlen(args.arg[0]); + strcpy((char *) ch->bc->fac_out.u.CallDeflection.Component.Invoke.Deflection.Party.Number, args.arg[0]); + ch->bc->fac_out.u.CallDeflection.Component.Invoke.Deflection.Subaddress.Length = 0; + +#else /* !defined(AST_MISDN_ENHANCEMENTS) */ + + max_len = sizeof(ch->bc->fac_out.u.CDeflection.DeflectedToNumber) - 1; + if (max_len < strlen(args.arg[0])) { + ast_log(LOG_WARNING, + "Facility: Number argument too long (up to %u digits are allowed). Ignoring.\n", + max_len); return 0; } ch->bc->fac_out.Function = Fac_CD; - ast_copy_string((char *)ch->bc->fac_out.u.CDeflection.DeflectedToNumber, args.arg[0], sizeof(ch->bc->fac_out.u.CDeflection.DeflectedToNumber)); + ch->bc->fac_out.u.CDeflection.PresentationAllowed = 0; + //ch->bc->fac_out.u.CDeflection.DeflectedToSubaddress[0] = 0; + strcpy((char *) ch->bc->fac_out.u.CDeflection.DeflectedToNumber, args.arg[0]); +#endif /* !defined(AST_MISDN_ENHANCEMENTS) */ + + /* Send message */ + print_facility(&ch->bc->fac_out, ch->bc); misdn_lib_send_event(ch->bc, EVENT_FACILITY); } else { chan_misdn_log(1, ch->bc->port, "Unknown Facility: %s\n", args.facility_type); @@ -6350,8 +11432,8 @@ static int misdn_set_opt_exec(struct ast_channel *chan, void *data) int txgain = 0; int change_jitter = 0; - if (strcasecmp(chan->tech->type, "mISDN")) { - ast_log(LOG_WARNING, "misdn_set_opt makes only sense with chan_misdn channels!\n"); + if (strcasecmp(chan->tech->type, misdn_type)) { + ast_log(LOG_WARNING, "misdn_set_opt makes sense only with %s channels!\n", misdn_type); return -1; } diff --git a/channels/misdn/chan_misdn_config.h b/channels/misdn/chan_misdn_config.h index d64b8c860..140e1ee26 100644 --- a/channels/misdn/chan_misdn_config.h +++ b/channels/misdn/chan_misdn_config.h @@ -65,6 +65,7 @@ enum misdn_cfg_elements { MISDN_CFG_EARLY_BCONNECT, /* int (bool) */ MISDN_CFG_INCOMING_EARLY_AUDIO, /* int (bool) */ MISDN_CFG_ECHOCANCEL, /* int */ + MISDN_CFG_CC_REQUEST_RETENTION,/* bool */ #ifdef MISDN_1_2 MISDN_CFG_PIPELINE, /* char[] */ #endif diff --git a/channels/misdn/ie.c b/channels/misdn/ie.c index 85b3d12ad..33596098d 100644 --- a/channels/misdn/ie.c +++ b/channels/misdn/ie.c @@ -1,4 +1,3 @@ - /* * Chan_Misdn -- Channel Driver for Asterisk * @@ -39,11 +38,11 @@ #define MISDN_IE_DEBG 0 /* support stuff */ -static void strnncpy(char *dest, char *src, int len, int dst_len) +static void strnncpy(char *dest, const char *src, size_t len, size_t dst_len) { if (len > dst_len-1) len = dst_len-1; - strncpy((char *)dest, (char *)src, len); + strncpy(dest, src, len); dest[len] = '\0'; } @@ -380,7 +379,7 @@ static void enc_ie_called_pn(unsigned char **ntmode, msg_t *msg, int type, int p strncpy((char *)p+3, (char *)number, strlen((char *)number)); } -static void dec_ie_called_pn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, char *number, int number_len, int nt, struct misdn_bchannel *bc) +static void dec_ie_called_pn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, char *number, size_t number_len, int nt, struct misdn_bchannel *bc) { *type = -1; *plan = -1; @@ -464,7 +463,7 @@ static void enc_ie_calling_pn(unsigned char **ntmode, msg_t *msg, int type, int } } -static void dec_ie_calling_pn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, int *screen, char *number, int number_len, int nt, struct misdn_bchannel *bc) +static void dec_ie_calling_pn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, int *screen, char *number, size_t number_len, int nt, struct misdn_bchannel *bc) { *type = -1; *plan = -1; @@ -566,7 +565,7 @@ static void enc_ie_connected_pn(unsigned char **ntmode, msg_t *msg, int type, in } } -static void dec_ie_connected_pn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, int *screen, char *number, int number_len, int nt, struct misdn_bchannel *bc) +static void dec_ie_connected_pn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, int *screen, char *number, size_t number_len, int nt, struct misdn_bchannel *bc) { *type = -1; *plan = -1; @@ -884,7 +883,7 @@ static void enc_ie_date(unsigned char **ntmode, msg_t *msg, time_t ti, int nt, s static void enc_ie_display(unsigned char **ntmode, msg_t *msg, char *display, int nt, struct misdn_bchannel *bc) { unsigned char *p; - Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); + Q931_info_t *qi = (Q931_info_t *) (msg->data + mISDN_HEADER_LEN); int l; if (!display[0]) @@ -893,27 +892,28 @@ static void enc_ie_display(unsigned char **ntmode, msg_t *msg, char *display, in return; } - if (strlen((char *)display) > 80) + l = strlen(display); + if (80 < l) { - printf("%s: WARNING: display text too long (max 80 chars), cutting.\n", __FUNCTION__); - display[80] = '\0'; + l = 80; + printf("%s: WARNING: display text too long (max %d chars), cutting.\n", __FUNCTION__, l); + display[l] = '\0'; } - /* if (MISDN_IE_DEBG) printf(" display='%s' (len=%d)\n", display, strlen((char *)display)); */ + /* if (MISDN_IE_DEBG) printf(" display='%s' (len=%d)\n", display, l); */ - l = strlen((char *)display); - p = msg_put(msg, l+2); + p = msg_put(msg, l + 2); if (nt) - *ntmode = p+1; + *ntmode = p + 1; else - qi->QI_ELEMENT(display) = p - (unsigned char *)qi - sizeof(Q931_info_t); + qi->QI_ELEMENT(display) = p - (unsigned char *) qi - sizeof(Q931_info_t); p[0] = IE_DISPLAY; p[1] = l; - strncpy((char *)p+2, (char *)display, strlen((char *)display)); + strncpy((char *) p + 2, display, l); } #if 0 -static void dec_ie_display(unsigned char *p, Q931_info_t *qi, char *display, int display_len, int nt, struct misdn_bchannel *bc) +static void dec_ie_display(unsigned char *p, Q931_info_t *qi, char *display, size_t display_len, int nt, struct misdn_bchannel *bc) { *display = '\0'; @@ -965,7 +965,7 @@ static void enc_ie_keypad(unsigned char **ntmode, msg_t *msg, char *keypad, int } #endif -static void dec_ie_keypad(unsigned char *p, Q931_info_t *qi, char *keypad, int keypad_len, int nt, struct misdn_bchannel *bc) +static void dec_ie_keypad(unsigned char *p, Q931_info_t *qi, char *keypad, size_t keypad_len, int nt, struct misdn_bchannel *bc) { *keypad = '\0'; @@ -990,7 +990,6 @@ static void dec_ie_keypad(unsigned char *p, Q931_info_t *qi, char *keypad, int k /* IE_NOTIFY */ -#if 0 static void enc_ie_notify(unsigned char **ntmode, msg_t *msg, int notify, int nt, struct misdn_bchannel *bc) { unsigned char *p; @@ -1015,9 +1014,7 @@ static void enc_ie_notify(unsigned char **ntmode, msg_t *msg, int notify, int nt p[1] = l; p[2] = 0x80 + notify; } -#endif -#if 0 static void dec_ie_notify(unsigned char *p, Q931_info_t *qi, int *notify, int nt, struct misdn_bchannel *bc) { *notify = -1; @@ -1040,7 +1037,6 @@ static void dec_ie_notify(unsigned char *p, Q931_info_t *qi, int *notify, int nt if (MISDN_IE_DEBG) printf(" notify=%d\n", *notify); } -#endif /* IE_PROGRESS */ @@ -1184,7 +1180,7 @@ static void enc_ie_redir_nr(unsigned char **ntmode, msg_t *msg, int type, int pl } } -static void dec_ie_redir_nr(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, int *screen, int *reason, char *number, int number_len, int nt, struct misdn_bchannel *bc) +static void dec_ie_redir_nr(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, int *screen, int *reason, char *number, size_t number_len, int nt, struct misdn_bchannel *bc) { *type = -1; *plan = -1; @@ -1231,11 +1227,10 @@ static void dec_ie_redir_nr(unsigned char *p, Q931_info_t *qi, int *type, int *p /* IE_REDIR_DN (redirection = during MT_NOTIFY) */ -#if 0 static void enc_ie_redir_dn(unsigned char **ntmode, msg_t *msg, int type, int plan, int present, char *number, int nt, struct misdn_bchannel *bc) { unsigned char *p; -/* Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); */ + Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); int l; if (type<0 || type>7) @@ -1264,9 +1259,9 @@ static void enc_ie_redir_dn(unsigned char **ntmode, msg_t *msg, int type, int pl p = msg_put(msg, l+2); if (nt) *ntmode = p+1; - else -/* #warning REINSERT redir_dn, when included in te-mode */ - /*qi->QI_ELEMENT(redir_dn) = p - (unsigned char *)qi - sizeof(Q931_info_t)*/; + else { + qi->QI_ELEMENT(redirect_dn) = p - (unsigned char *)qi - sizeof(Q931_info_t); + } p[0] = IE_REDIR_DN; p[1] = l; if (present >= 0) @@ -1282,10 +1277,8 @@ static void enc_ie_redir_dn(unsigned char **ntmode, msg_t *msg, int type, int pl strncpy((char *)p+3, (char *)number, strlen((char *)number)); } } -#endif -#if 0 -static void dec_ie_redir_dn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, char *number, int number_len, int nt, struct misdn_bchannel *bc) +static void dec_ie_redir_dn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, char *number, size_t number_len, int nt, struct misdn_bchannel *bc) { *type = -1; *plan = -1; @@ -1295,9 +1288,8 @@ static void dec_ie_redir_dn(unsigned char *p, Q931_info_t *qi, int *type, int *p if (!nt) { p = NULL; -/* #warning REINSERT redir_dn, when included in te-mode */ -/* if (qi->QI_ELEMENT(redir_dn)) */ -/* p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(redir_dn) + 1; */ + if (qi->QI_ELEMENT(redirect_dn)) + p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(redirect_dn) + 1; } if (!p) return; @@ -1320,7 +1312,6 @@ static void dec_ie_redir_dn(unsigned char *p, Q931_info_t *qi, int *type, int *p if (MISDN_IE_DEBG) printf(" type=%d plan=%d present=%d number='%s'\n", *type, *plan, *present, number); } -#endif /* IE_USERUSER */ @@ -1331,9 +1322,6 @@ static void enc_ie_useruser(unsigned char **ntmode, msg_t *msg, int protocol, ch Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); int l; - char debug[768]; - int i; - if (protocol<0 || protocol>127) { printf("%s: ERROR: protocol(%d) is out of range.\n", __FUNCTION__, protocol); @@ -1344,14 +1332,16 @@ static void enc_ie_useruser(unsigned char **ntmode, msg_t *msg, int protocol, ch return; } - i = 0; - while(i < user_len) - { - if (MISDN_IE_DEBG) sprintf(debug+(i*3), " %02x", user[i]); - i++; - } + if (MISDN_IE_DEBG) { + size_t i; + char debug[768]; - if (MISDN_IE_DEBG) printf(" protocol=%d user-user%s\n", protocol, debug); + for (i = 0; i < user_len; ++i) { + sprintf(debug + (i * 3), " %02x", user[i]); + } + debug[i * 3] = 0; + printf(" protocol=%d user-user%s\n", protocol, debug); + } l = user_len+1; p = msg_put(msg, l+3); @@ -1369,9 +1359,6 @@ static void enc_ie_useruser(unsigned char **ntmode, msg_t *msg, int protocol, ch #if 1 static void dec_ie_useruser(unsigned char *p, Q931_info_t *qi, int *protocol, char *user, int *user_len, int nt, struct misdn_bchannel *bc) { - char debug[768]; - int i; - *user_len = 0; *protocol = -1; @@ -1390,15 +1377,16 @@ static void dec_ie_useruser(unsigned char *p, Q931_info_t *qi, int *protocol, ch *protocol = p[1]; memcpy(user, p+2, (*user_len<=128)?*(user_len):128); /* clip to 128 maximum */ - i = 0; - while(i < *user_len) - { - if (MISDN_IE_DEBG) sprintf(debug+(i*3), " %02x", user[i]); - i++; - } - debug[i*3] = '\0'; + if (MISDN_IE_DEBG) { + int i; + char debug[768]; - if (MISDN_IE_DEBG) printf(" protocol=%d user-user%s\n", *protocol, debug); + for (i = 0; i < *user_len; ++i) { + sprintf(debug + (i * 3), " %02x", user[i]); + } + debug[i * 3] = 0; + printf(" protocol=%d user-user%s\n", *protocol, debug); + } } #endif diff --git a/channels/misdn/isdn_lib.c b/channels/misdn/isdn_lib.c index 4a4ea9cc2..4a0da06d3 100644 --- a/channels/misdn/isdn_lib.c +++ b/channels/misdn/isdn_lib.c @@ -50,10 +50,6 @@ int misdn_lib_get_l2_up(struct misdn_stack *stack); struct misdn_stack *get_misdn_stack(void); -static int set_chan_in_stack(struct misdn_stack *stack, int channel); - -int release_cr(struct misdn_stack *stack, mISDNuser_head_t *hh); - int misdn_lib_port_is_pri(int port) { struct misdn_stack *stack=get_misdn_stack(); @@ -285,7 +281,6 @@ struct misdn_bchannel *stack_holder_find(struct misdn_stack *stack, unsigned lon /* from isdn_lib.h */ /* user iface */ -int te_lib_init( void ) ; /* returns midev */ void te_lib_destroy(int midev) ; struct misdn_bchannel *manager_find_bc_by_pid(int pid); struct misdn_bchannel *manager_find_bc_holded(struct misdn_bchannel* bc); @@ -483,6 +478,14 @@ static void dump_chan_list(struct misdn_stack *stack) cb_log(6, stack->port, "Idx:%d stack->cchan:%d in_use:%d Chan:%d\n", i, stack->channels[i], stack->bc[i].in_use, i + 1); } +#if defined(AST_MISDN_ENHANCEMENTS) + for (i = MAX_BCHANS + 1; i < ARRAY_LEN(stack->bc); ++i) { + if (stack->bc[i].in_use) { + cb_log(6, stack->port, "Idx:%d stack->cchan:%d REGISTER Chan:%d in_use\n", + i, stack->channels[i], i + 1); + } + } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ } @@ -499,7 +502,7 @@ static int set_chan_in_stack(struct misdn_stack *stack, int channel) { cb_log(4,stack->port,"set_chan_in_stack: %d\n",channel); dump_chan_list(stack); - if (channel >=1 && channel <= MAX_BCHANS) { + if (1 <= channel && channel <= ARRAY_LEN(stack->channels)) { if (!stack->channels[channel-1]) stack->channels[channel-1] = 1; else { @@ -520,7 +523,7 @@ static int find_free_chan_in_stack(struct misdn_stack *stack, struct misdn_bchan { int i; int chan = 0; - int bnums = stack->pri ? stack->b_num : stack->b_num - 1; + int bnums; if (bc->channel_found) { return 0; @@ -528,32 +531,46 @@ static int find_free_chan_in_stack(struct misdn_stack *stack, struct misdn_bchan bc->channel_found = 1; - cb_log(5,stack->port,"find_free_chan: req_chan:%d\n",channel); +#if defined(AST_MISDN_ENHANCEMENTS) + if (bc->is_register_pool) { + for (i = MAX_BCHANS + 1; i < ARRAY_LEN(stack->channels); ++i) { + if (!stack->channels[i]) { + chan = i + 1; + cb_log(3, stack->port, " --> found REGISTER chan: %d\n", chan); + break; + } + } + } else +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + { + cb_log(5, stack->port, "find_free_chan: req_chan:%d\n", channel); - if (channel < 0 || channel > MAX_BCHANS) { - cb_log(0, stack->port, " !! out of bound call to find_free_chan_in_stack! (ch:%d)\n", channel); - return 0; - } + if (channel < 0 || channel > MAX_BCHANS) { + cb_log(0, stack->port, " !! out of bound call to find_free_chan_in_stack! (ch:%d)\n", channel); + return 0; + } - channel--; + --channel; - if (dec) { - for (i = bnums; i >=0; i--) { - if (i != 15 && (channel < 0 || i == channel)) { /* skip E1 D channel ;) and work with chan preselection */ - if (!stack->channels[i]) { - cb_log (3, stack->port, " --> found chan%s: %d\n", channel>=0?" (preselected)":"", i+1); - chan=i+1; - break; + bnums = stack->pri ? stack->b_num : stack->b_num - 1; + if (dec) { + for (i = bnums; i >= 0; --i) { + if (i != 15 && (channel < 0 || i == channel)) { /* skip E1 D channel ;) and work with chan preselection */ + if (!stack->channels[i]) { + chan = i + 1; + cb_log(3, stack->port, " --> found chan%s: %d\n", channel >= 0 ? " (preselected)" : "", chan); + break; + } } } - } - } else { - for (i = 0; i <= bnums; i++) { - if (i != 15 && (channel < 0 || i == channel)) { /* skip E1 D channel ;) and work with chan preselection */ - if (!stack->channels[i]) { - cb_log (3, stack->port, " --> found chan%s: %d\n", channel>=0?" (preselected)":"", i+1); - chan=i+1; - break; + } else { + for (i = 0; i <= bnums; ++i) { + if (i != 15 && (channel < 0 || i == channel)) { /* skip E1 D channel ;) and work with chan preselection */ + if (!stack->channels[i]) { + chan = i + 1; + cb_log(3, stack->port, " --> found chan%s: %d\n", channel >= 0 ? " (preselected)" : "", chan); + break; + } } } } @@ -576,17 +593,16 @@ static int find_free_chan_in_stack(struct misdn_stack *stack, struct misdn_bchan return 0; } -static int empty_chan_in_stack(struct misdn_stack *stack, int channel) +static void empty_chan_in_stack(struct misdn_stack *stack, int channel) { - if (channel<=0 || channel>MAX_BCHANS) { - cb_log(0,stack?stack->port:0, "empty_chan_in_stack: cannot empty channel %d\n",channel); - return -1; + if (channel < 1 || ARRAY_LEN(stack->channels) < channel) { + cb_log(0, stack->port, "empty_chan_in_stack: cannot empty channel %d\n", channel); + return; } - cb_log (4, stack?stack->port:0, "empty_chan_in_stack: %d\n",channel); + cb_log(4, stack->port, "empty_chan_in_stack: %d\n", channel); stack->channels[channel - 1] = 0; dump_chan_list(stack); - return 0; } char *bc_state2str(enum bchannel_state state) { @@ -671,7 +687,16 @@ static void empty_bc(struct misdn_bchannel *bc) bc->redirecting.from.number[0] = 0; bc->redirecting.from.subaddress[0] = 0; + bc->redirecting.to.presentation = 0; /* allowed */ + bc->redirecting.to.number_plan = NUMPLAN_ISDN; + bc->redirecting.to.number_type = NUMTYPE_UNKNOWN; + bc->redirecting.to.name[0] = 0; + bc->redirecting.to.number[0] = 0; + bc->redirecting.to.subaddress[0] = 0; + bc->redirecting.reason = mISDN_REDIRECTING_REASON_UNKNOWN; + bc->redirecting.count = 0; + bc->redirecting.to_changed = 0; bc->dummy=0; @@ -731,6 +756,8 @@ static void empty_bc(struct misdn_bchannel *bc) bc->presentation = 0; /* allowed */ bc->set_presentation = 0; + bc->notify_description_code = mISDN_NOTIFY_CODE_INVALID; + bc->evq=EVENT_NOTHING; bc->progress_coding=0; @@ -810,22 +837,26 @@ static void clear_l3(struct misdn_stack *stack) { int i; - for (i=0; i<=stack->b_num; i++) { - if (global_state == MISDN_INITIALIZED) { + if (global_state == MISDN_INITIALIZED) { + for (i = 0; i <= stack->b_num; ++i) { cb_event(EVENT_CLEANUP, &stack->bc[i], NULL); empty_chan_in_stack(stack, i + 1); empty_bc(&stack->bc[i]); clean_up_bc(&stack->bc[i]); stack->bc[i].in_use = 0; } - +#if defined(AST_MISDN_ENHANCEMENTS) + for (i = MAX_BCHANS + 1; i < ARRAY_LEN(stack->bc); ++i) { + empty_chan_in_stack(stack, i + 1); + empty_bc(&stack->bc[i]); + stack->bc[i].in_use = 0; + } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ } } static int new_te_id = 0; -#define MAXPROCS 0x100 - static int misdn_lib_get_l1_down(struct misdn_stack *stack) { /* Pull Up L1 */ @@ -1179,33 +1210,20 @@ int setup_bc(struct misdn_bchannel *bc) /** IFACE **/ -static int init_bc(struct misdn_stack *stack, struct misdn_bchannel *bc, int midev, int port, int bidx, char *msn, int firsttime) +static int init_bc(struct misdn_stack *stack, struct misdn_bchannel *bc, int midev, int port, int bidx) { - unsigned char buff[1025] = ""; - iframe_t *frm = (iframe_t *)buff; - int ret; - if (!bc) { return -1; } cb_log(8, port, "Init.BC %d.\n",bidx); - memset(bc, 0,sizeof(struct misdn_bchannel)); - bc->send_lock=malloc(sizeof(struct send_lock)); if (!bc->send_lock) { return -1; } pthread_mutex_init(&bc->send_lock->lock, NULL); - if (msn) { - int l = sizeof(bc->msn); - strncpy(bc->msn,msn, l); - bc->msn[l-1] = 0; - } - - empty_bc(bc); bc_state_change(bc, BCHAN_CLEANED); @@ -1232,8 +1250,13 @@ static int init_bc(struct misdn_stack *stack, struct misdn_bchannel *bc, int mid } - { +#if 0 /* This code does not seem to do anything useful */ + if (bidx <= stack->b_num) { + unsigned char buff[1025]; + iframe_t *frm = (iframe_t *) buff; stack_info_t *stinf; + int ret; + ret = mISDN_get_stack_info(midev, stack->port, buff, sizeof(buff)); if (ret < 0) { cb_log(0, port, "%s: Cannot get stack info for this port. (ret=%d)\n", __FUNCTION__, ret); @@ -1244,6 +1267,7 @@ static int init_bc(struct misdn_stack *stack, struct misdn_bchannel *bc, int mid cb_log(8, port, " --> Child %x\n",stinf->child[bidx]); } +#endif return 0; } @@ -1256,19 +1280,17 @@ static struct misdn_stack *stack_init(int midev, int port, int ptp) unsigned char buff[1025]; iframe_t *frm = (iframe_t *)buff; stack_info_t *stinf; + struct misdn_stack *stack; int i; layer_info_t li; - struct misdn_stack *stack = malloc(sizeof(struct misdn_stack)); - if (!stack ) return NULL; - + stack = calloc(1, sizeof(struct misdn_stack)); + if (!stack) { + return NULL; + } cb_log(8, port, "Init. Stack.\n"); - memset(stack,0,sizeof(struct misdn_stack)); - - for (i=0; i<MAX_BCHANS + 1; i++ ) stack->channels[i]=0; - stack->port=port; stack->midev=midev; stack->ptp=ptp; @@ -1406,11 +1428,6 @@ static struct misdn_stack *stack_init(int midev, int port, int ptp) } - if (!stack->nt) { - /*assume L1 is up, we'll get DEACTIVATES soon, for non - * up L1s*/ - stack->l1link=0; - } stack->l1link=0; stack->l2link=0; #if 0 @@ -1505,6 +1522,14 @@ static struct misdn_bchannel *find_bc_by_masked_l3id(struct misdn_stack *stack, return &stack->bc[i]; } } +#if defined(AST_MISDN_ENHANCEMENTS) + /* Search the B channel records for a REGISTER signaling link. */ + for (i = MAX_BCHANS + 1; i < ARRAY_LEN(stack->bc); ++i) { + if ((stack->bc[i].l3_id & mask) == (l3id & mask)) { + return &stack->bc[i]; + } + } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ return stack_holder_find(stack, l3id); } @@ -1518,6 +1543,14 @@ struct misdn_bchannel *find_bc_by_l3id(struct misdn_stack *stack, unsigned long return &stack->bc[i]; } } +#if defined(AST_MISDN_ENHANCEMENTS) + /* Search the B channel records for a REGISTER signaling link. */ + for (i = MAX_BCHANS + 1; i < ARRAY_LEN(stack->bc); ++i) { + if (stack->bc[i].l3_id == l3id) { + return &stack->bc[i]; + } + } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ return stack_holder_find(stack, l3id); } @@ -1656,7 +1689,9 @@ static int handle_event ( struct misdn_bchannel *bc, enum event_e event, iframe_ if (!bc->channel) { cb_log(0, stack->port, "Any Channel Requested, but we have no more!!\n"); } else { - cb_log(0, stack->port, "Requested Channel Already in Use releasing this call with cause 34!!!!\n"); + cb_log(0, stack->port, + "Requested Channel Already in Use releasing this call with cause %d!!!!\n", + bc->out_cause); } /* when the channel is already in use, we can't @@ -1687,7 +1722,9 @@ static int handle_event ( struct misdn_bchannel *bc, enum event_e event, iframe_ static int handle_cr ( struct misdn_stack *stack, iframe_t *frm) { + struct misdn_bchannel dummybc; struct misdn_bchannel *bc; + int channel; if (!stack) return -1; @@ -1714,50 +1751,40 @@ static int handle_cr ( struct misdn_stack *stack, iframe_t *frm) break; case CC_RELEASE_CR|INDICATION: cb_log(4, stack->port, " --> lib: RELEASE_CR Ind with l3id:%x\n", frm->dinfo); - { - struct misdn_bchannel *bc=find_bc_by_l3id(stack, frm->dinfo); - struct misdn_bchannel dummybc; - - if (!bc) { - cb_log(4, stack->port, " --> Didn't find BC so temporarily creating dummy BC (l3id:%x) on this port.\n", frm->dinfo); - misdn_make_dummy(&dummybc, stack->port, frm->dinfo, stack->nt, 0); - - bc=&dummybc; - } - - if (bc) { - int channel = bc->channel; - cb_log(4, stack->port, " --> lib: CLEANING UP l3id: %x\n",frm->dinfo); + bc = find_bc_by_l3id(stack, frm->dinfo); + if (!bc) { + cb_log(4, stack->port, " --> Didn't find BC so temporarily creating dummy BC (l3id:%x) on this port.\n", frm->dinfo); + misdn_make_dummy(&dummybc, stack->port, frm->dinfo, stack->nt, 0); + bc = &dummybc; + } - /*bc->pid = 0;*/ - bc->need_disconnect=0; - bc->need_release=0; - bc->need_release_complete=0; + channel = bc->channel; + cb_log(4, stack->port, " --> lib: CLEANING UP l3id: %x\n", frm->dinfo); - cb_event(EVENT_CLEANUP, bc, glob_mgr->user_data); + /* bc->pid = 0; */ + bc->need_disconnect = 0; + bc->need_release = 0; + bc->need_release_complete = 0; - empty_bc(bc); - clean_up_bc(bc); + cb_event(EVENT_CLEANUP, bc, glob_mgr->user_data); - if (channel>0) - empty_chan_in_stack(stack,channel); - bc->in_use=0; + empty_bc(bc); + clean_up_bc(bc); - dump_chan_list(stack); + if (channel > 0) + empty_chan_in_stack(stack, channel); + bc->in_use = 0; - if (bc->stack_holder) { - cb_log(4,stack->port, "REMOVING Holder\n"); - stack_holder_remove( stack, bc); - free(bc); - } - } - else { - if (stack->nt) - cb_log(4, stack->port, "BC with dinfo: %x not found.. (prim was %x and addr %x)\n",frm->dinfo, frm->prim, frm->addr); - } + dump_chan_list(stack); - return 1; + if (bc->stack_holder) { + cb_log(4, stack->port, "REMOVING Holder\n"); + stack_holder_remove(stack, bc); + free(bc); } + + return 1; + default: break; } @@ -1864,7 +1891,7 @@ int misdn_lib_port_up(int port, int check) } -int release_cr(struct misdn_stack *stack, mISDNuser_head_t *hh) +static int release_cr(struct misdn_stack *stack, mISDNuser_head_t *hh) { struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); struct misdn_bchannel dummybc; @@ -1882,13 +1909,10 @@ int release_cr(struct misdn_stack *stack, mISDNuser_head_t *hh) bc=&dummybc; } - if (bc) { - if ( (bc->l3_id & 0xff00) == 0xff00) { - cb_log(4, stack->port, " --> Removing Process Id:%x on this port.\n", bc->l3_id&0xff); - stack->procids[bc->l3_id&0xff] = 0 ; - } + if ((bc->l3_id & 0xff00) == 0xff00) { + cb_log(4, stack->port, " --> Removing Process Id:%x on this port.\n", bc->l3_id & 0xff); + stack->procids[bc->l3_id & 0xff] = 0; } - else cb_log(0, stack->port, "Couldn't find BC so I couldn't remove the Process!!!! this is a bad port.\n"); if (handle_cr(stack, &frm)<0) { } @@ -1898,315 +1922,317 @@ int release_cr(struct misdn_stack *stack, mISDNuser_head_t *hh) static int handle_event_nt(void *dat, void *arg) { + struct misdn_bchannel dummybc; + struct misdn_bchannel *bc; manager_t *mgr = (manager_t *)dat; msg_t *msg = (msg_t *)arg; + msg_t *dmsg; mISDNuser_head_t *hh; + struct misdn_stack *stack; + enum event_e event; int reject=0; - - struct misdn_stack *stack=find_stack_by_mgr(mgr); int port; + int l3id; + int channel; + int tmpcause; if (!msg || !mgr) return(-EINVAL); + stack = find_stack_by_mgr(mgr); hh=(mISDNuser_head_t*)msg->data; port=stack->port; cb_log(5, stack->port, " --> lib: prim %x dinfo %x\n",hh->prim, hh->dinfo); + switch(hh->prim) { + case CC_RETRIEVE|INDICATION: { - switch(hh->prim){ - case CC_RETRIEVE|INDICATION: - { - struct misdn_bchannel *bc; - struct misdn_bchannel *hold_bc; + struct misdn_bchannel *hold_bc; + iframe_t frm; /* fake te frm to add callref to global callreflist */ - iframe_t frm; /* fake te frm to add callref to global callreflist */ - frm.dinfo = hh->dinfo; + frm.dinfo = hh->dinfo; + frm.addr=stack->upper_id | FLG_MSG_DOWN; + frm.prim = CC_NEW_CR|INDICATION; + if (handle_cr( stack, &frm)< 0) { + goto ERR_NO_CHANNEL; + } - frm.addr=stack->upper_id | FLG_MSG_DOWN; + bc = find_bc_by_l3id(stack, hh->dinfo); + hold_bc = stack_holder_find(stack, bc->l3_id); + cb_log(4, stack->port, "bc_l3id:%x holded_bc_l3id:%x\n",bc->l3_id, hold_bc->l3_id); - frm.prim = CC_NEW_CR|INDICATION; + if (hold_bc) { + cb_log(4, stack->port, "REMOVING Holder\n"); - if (handle_cr( stack, &frm)< 0) { - msg_t *dmsg; - cb_log(4, stack->port, "Patch from MEIDANIS:Sending RELEASE_COMPLETE %x (No free Chan for you..)\n", hh->dinfo); - dmsg = create_l3msg(CC_RELEASE_COMPLETE | REQUEST,MT_RELEASE_COMPLETE, hh->dinfo,sizeof(RELEASE_COMPLETE_t), 1); - stack->nst.manager_l3(&stack->nst, dmsg); - free_msg(msg); - return 0; - } + /* swap the backup to our new channel back */ + stack_holder_remove(stack, hold_bc); + memcpy(bc, hold_bc, sizeof(*bc)); + free(hold_bc); - bc = find_bc_by_l3id(stack, hh->dinfo); - hold_bc = stack_holder_find(stack, bc->l3_id); - cb_log(4, stack->port, "bc_l3id:%x holded_bc_l3id:%x\n",bc->l3_id, hold_bc->l3_id); + bc->holded=0; + bc->b_stid=0; + } + break; + } - if (hold_bc) { - cb_log(4, stack->port, "REMOVING Holder\n"); + case CC_SETUP | CONFIRM: + l3id = *((int *) (msg->data + mISDNUSER_HEAD_SIZE)); - /*swap the backup to our new channel back*/ - stack_holder_remove(stack, hold_bc); - memcpy(bc, hold_bc, sizeof(*bc)); - free(hold_bc); + cb_log(4, stack->port, " --> lib: Event_ind:SETUP CONFIRM [NT] : new L3ID is %x\n", l3id); - bc->holded=0; - bc->b_stid=0; - } + bc = find_bc_by_l3id(stack, hh->dinfo); + if (bc) { + cb_log (2, bc->port, "I IND :CC_SETUP|CONFIRM: old l3id:%x new l3id:%x\n", bc->l3_id, l3id); + bc->l3_id = l3id; + cb_event(EVENT_NEW_L3ID, bc, glob_mgr->user_data); + } else { + cb_log(4, stack->port, "Bc Not found (after SETUP CONFIRM)\n"); + } + free_msg(msg); + return 0; + case CC_SETUP | INDICATION: + bc = misdn_lib_get_free_bc(stack->port, 0, 1, 0); + if (!bc) { + goto ERR_NO_CHANNEL; } - break; + cb_log(4, stack->port, " --> new_process: New L3Id: %x\n",hh->dinfo); + bc->l3_id=hh->dinfo; + break; - case CC_SETUP|CONFIRM: - { - struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); - int l3id = *((int *)(((u_char *)msg->data)+ mISDNUSER_HEAD_SIZE)); - cb_log(4, stack->port, " --> lib: Event_ind:SETUP CONFIRM [NT] : new L3ID is %x\n",l3id ); +#if defined(AST_MISDN_ENHANCEMENTS) + case CC_REGISTER | CONFIRM: + l3id = *((int *) (msg->data + mISDNUSER_HEAD_SIZE)); - if (!bc) { cb_log(4, stack->port, "Bc Not found (after SETUP CONFIRM)\n"); return 0; } - cb_log (2,bc->port,"I IND :CC_SETUP|CONFIRM: old l3id:%x new l3id:%x\n", bc->l3_id, l3id); - bc->l3_id=l3id; - cb_event(EVENT_NEW_L3ID, bc, glob_mgr->user_data); + cb_log(4, stack->port, " --> lib: Event_ind:REGISTER CONFIRM [NT] : new L3ID is %x\n", l3id); + + bc = find_bc_by_l3id(stack, hh->dinfo); + if (bc) { + cb_log (2, bc->port, "I IND :CC_REGISTER|CONFIRM: old l3id:%x new l3id:%x\n", bc->l3_id, l3id); + bc->l3_id = l3id; + } else { + cb_log(4, stack->port, "Bc Not found (after REGISTER CONFIRM)\n"); } free_msg(msg); return 0; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ - case CC_SETUP|INDICATION: - { - struct misdn_bchannel* bc=misdn_lib_get_free_bc(stack->port, 0, 1, 0); - if (!bc) - ERR_NO_CHANNEL: - { - msg_t *dmsg; - cb_log(4, stack->port, "Patch from MEIDANIS:Sending RELEASE_COMPLETE %x (No free Chan for you..)\n", hh->dinfo); - dmsg = create_l3msg(CC_RELEASE_COMPLETE | REQUEST,MT_RELEASE_COMPLETE, hh->dinfo,sizeof(RELEASE_COMPLETE_t), 1); - stack->nst.manager_l3(&stack->nst, dmsg); - free_msg(msg); - return 0; - } - - cb_log(4, stack->port, " --> new_process: New L3Id: %x\n",hh->dinfo); - bc->l3_id=hh->dinfo; +#if defined(AST_MISDN_ENHANCEMENTS) + case CC_REGISTER | INDICATION: + bc = misdn_lib_get_register_bc(stack->port); + if (!bc) { + goto ERR_NO_CHANNEL; } + + cb_log(4, stack->port, " --> new_process: New L3Id: %x\n",hh->dinfo); + bc->l3_id=hh->dinfo; break; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ - case CC_CONNECT_ACKNOWLEDGE|INDICATION: + case CC_CONNECT_ACKNOWLEDGE|INDICATION: break; - case CC_ALERTING|INDICATION: - case CC_PROCEEDING|INDICATION: - case CC_SETUP_ACKNOWLEDGE|INDICATION: - if(!stack->ptp) break; - case CC_CONNECT|INDICATION: + case CC_ALERTING|INDICATION: + case CC_PROCEEDING|INDICATION: + case CC_SETUP_ACKNOWLEDGE|INDICATION: + case CC_CONNECT|INDICATION: break; - case CC_DISCONNECT|INDICATION: - { - struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); - if (!bc) { - bc=find_bc_by_masked_l3id(stack, hh->dinfo, 0xffff0000); - if (bc) { - int myprocid=bc->l3_id&0x0000ffff; - hh->dinfo=(hh->dinfo&0xffff0000)|myprocid; - cb_log(3,stack->port,"Reject dinfo: %x cause:%d\n",hh->dinfo,bc->cause); - reject=1; - } + case CC_DISCONNECT|INDICATION: + bc = find_bc_by_l3id(stack, hh->dinfo); + if (!bc) { + bc=find_bc_by_masked_l3id(stack, hh->dinfo, 0xffff0000); + if (bc) { + int myprocid=bc->l3_id&0x0000ffff; + + hh->dinfo=(hh->dinfo&0xffff0000)|myprocid; + cb_log(3,stack->port,"Reject dinfo: %x cause:%d\n",hh->dinfo,bc->cause); + reject=1; } } break; - case CC_FACILITY|INDICATION: - { - struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); - if (!bc) { - bc=find_bc_by_masked_l3id(stack, hh->dinfo, 0xffff0000); - if (bc) { - int myprocid=bc->l3_id&0x0000ffff; - hh->dinfo=(hh->dinfo&0xffff0000)|myprocid; - cb_log(4,bc->port,"Repaired reject Bug, new dinfo: %x\n",hh->dinfo); - } + case CC_FACILITY|INDICATION: + bc = find_bc_by_l3id(stack, hh->dinfo); + if (!bc) { + bc=find_bc_by_masked_l3id(stack, hh->dinfo, 0xffff0000); + if (bc) { + int myprocid=bc->l3_id&0x0000ffff; + + hh->dinfo=(hh->dinfo&0xffff0000)|myprocid; + cb_log(4,bc->port,"Repaired reject Bug, new dinfo: %x\n",hh->dinfo); } } break; - case CC_RELEASE_COMPLETE|INDICATION: - break; - - case CC_SUSPEND|INDICATION: - { - msg_t *dmsg; - cb_log(4, stack->port, " --> Got Suspend, sending Reject for now\n"); - dmsg = create_l3msg(CC_SUSPEND_REJECT | REQUEST,MT_SUSPEND_REJECT, hh->dinfo,sizeof(RELEASE_COMPLETE_t), 1); - stack->nst.manager_l3(&stack->nst, dmsg); - free_msg(msg); - return 0; - } + case CC_RELEASE_COMPLETE|INDICATION: break; - case CC_RESUME|INDICATION: - break; - case CC_RELEASE|CONFIRM: - { - struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); + case CC_SUSPEND|INDICATION: + cb_log(4, stack->port, " --> Got Suspend, sending Reject for now\n"); + dmsg = create_l3msg(CC_SUSPEND_REJECT | REQUEST,MT_SUSPEND_REJECT, hh->dinfo,sizeof(RELEASE_COMPLETE_t), 1); + stack->nst.manager_l3(&stack->nst, dmsg); + free_msg(msg); + return 0; - if (bc) { - cb_log(1, stack->port, "CC_RELEASE|CONFIRM (l3id:%x), sending RELEASE_COMPLETE\n", hh->dinfo); - misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); - } - } - break; + case CC_RESUME|INDICATION: + break; - case CC_RELEASE|INDICATION: - break; + case CC_RELEASE|CONFIRM: + bc = find_bc_by_l3id(stack, hh->dinfo); + if (bc) { + cb_log(1, stack->port, "CC_RELEASE|CONFIRM (l3id:%x), sending RELEASE_COMPLETE\n", hh->dinfo); + misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); + } + break; - case CC_RELEASE_CR|INDICATION: - release_cr(stack, hh); - free_msg(msg); - return 0 ; + case CC_RELEASE|INDICATION: break; - case CC_NEW_CR|INDICATION: - /* Got New CR for bchan, for now I handle this one in */ - /* connect_ack, Need to be changed */ - { - struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); - int l3id = *((int *)(((u_char *)msg->data)+ mISDNUSER_HEAD_SIZE)); - if (!bc) { cb_log(0, stack->port, " --> In NEW_CR: didn't found bc ??\n"); return -1;}; - if (((l3id&0xff00)!=0xff00) && ((bc->l3_id&0xff00)==0xff00)) { - cb_log(4, stack->port, " --> Removing Process Id:%x on this port.\n", 0xff&bc->l3_id); - stack->procids[bc->l3_id&0xff] = 0 ; - } - cb_log(4, stack->port, "lib: Event_ind:CC_NEW_CR : very new L3ID is %x\n",l3id ); + case CC_RELEASE_CR|INDICATION: + release_cr(stack, hh); + free_msg(msg); + return 0; - bc->l3_id =l3id; - cb_event(EVENT_NEW_L3ID, bc, glob_mgr->user_data); + case CC_NEW_CR|INDICATION: + /* Got New CR for bchan, for now I handle this one in */ + /* connect_ack, Need to be changed */ + l3id = *((int *) (msg->data + mISDNUSER_HEAD_SIZE)); - free_msg(msg); - return 0; + bc = find_bc_by_l3id(stack, hh->dinfo); + if (!bc) { + cb_log(0, stack->port, " --> In NEW_CR: didn't found bc ??\n"); + return -1; + } + if (((l3id&0xff00)!=0xff00) && ((bc->l3_id&0xff00)==0xff00)) { + cb_log(4, stack->port, " --> Removing Process Id:%x on this port.\n", 0xff&bc->l3_id); + stack->procids[bc->l3_id&0xff] = 0 ; } + cb_log(4, stack->port, "lib: Event_ind:CC_NEW_CR : very new L3ID is %x\n",l3id ); - case DL_ESTABLISH | INDICATION: - case DL_ESTABLISH | CONFIRM: - { - cb_log(3, stack->port, "%% GOT L2 Activate Info.\n"); + bc->l3_id =l3id; + if (!bc->is_register_pool) { + cb_event(EVENT_NEW_L3ID, bc, glob_mgr->user_data); + } - if (stack->ptp && stack->l2link) { - cb_log(0, stack->port, "%% GOT L2 Activate Info. but we're activated already.. this l2 is faulty, blocking port\n"); - cb_event(EVENT_PORT_ALARM, &stack->bc[0], glob_mgr->user_data); - } + free_msg(msg); + return 0; - if (stack->ptp && !stack->restart_sent) { - /* make sure we restart the interface of the - * other side */ - stack->restart_sent=1; - misdn_lib_send_restart(stack->port, -1); + case DL_ESTABLISH | INDICATION: + case DL_ESTABLISH | CONFIRM: + cb_log(3, stack->port, "%% GOT L2 Activate Info.\n"); - } + if (stack->ptp && stack->l2link) { + cb_log(0, stack->port, "%% GOT L2 Activate Info. but we're activated already.. this l2 is faulty, blocking port\n"); + cb_event(EVENT_PORT_ALARM, &stack->bc[0], glob_mgr->user_data); + } - /* when we get the L2 UP, the L1 is UP definitely too*/ - stack->l2link = 1; - stack->l2upcnt=0; + if (stack->ptp && !stack->restart_sent) { + /* make sure we restart the interface of the + * other side */ + stack->restart_sent=1; + misdn_lib_send_restart(stack->port, -1); - free_msg(msg); - return 0; } - break; + /* when we get the L2 UP, the L1 is UP definitely too*/ + stack->l2link = 1; + stack->l2upcnt=0; - case DL_RELEASE | INDICATION: - case DL_RELEASE | CONFIRM: - { - if (stack->ptp) { - cb_log(3 , stack->port, "%% GOT L2 DeActivate Info.\n"); + free_msg(msg); + return 0; - if (stack->l2upcnt>3) { - cb_log(0 , stack->port, "!!! Could not Get the L2 up after 3 Attempts!!!\n"); - } else { + case DL_RELEASE | INDICATION: + case DL_RELEASE | CONFIRM: + if (stack->ptp) { + cb_log(3 , stack->port, "%% GOT L2 DeActivate Info.\n"); + + if (stack->l2upcnt>3) { + cb_log(0 , stack->port, "!!! Could not Get the L2 up after 3 Attempts!!!\n"); + } else { #if 0 - if (stack->nt) misdn_lib_reinit_nt_stack(stack->port); + if (stack->nt) + misdn_lib_reinit_nt_stack(stack->port); #endif - if (stack->l1link) { - misdn_lib_get_l2_up(stack); - stack->l2upcnt++; - } + if (stack->l1link) { + misdn_lib_get_l2_up(stack); + stack->l2upcnt++; } + } - } else - cb_log(3, stack->port, "%% GOT L2 DeActivate Info.\n"); + } else + cb_log(3, stack->port, "%% GOT L2 DeActivate Info.\n"); - stack->l2link = 0; - free_msg(msg); - return 0; - } + stack->l2link = 0; + free_msg(msg); + return 0; + + default: break; - } } - { - /* Parse Events and fire_up to App. */ - struct misdn_bchannel *bc; - struct misdn_bchannel dummybc; + /* Parse Events and fire_up to App. */ + event = isdn_msg_get_event(msgs_g, msg, 1); - enum event_e event = isdn_msg_get_event(msgs_g, msg, 1); + bc = find_bc_by_l3id(stack, hh->dinfo); + if (!bc) { + cb_log(4, stack->port, " --> Didn't find BC so temporarily creating dummy BC (l3id:%x).\n", hh->dinfo); + misdn_make_dummy(&dummybc, stack->port, hh->dinfo, stack->nt, 0); + bc = &dummybc; + } - bc=find_bc_by_l3id(stack, hh->dinfo); + isdn_msg_parse_event(msgs_g, msg, bc, 1); - if (!bc) { - cb_log(4, stack->port, " --> Didn't find BC so temporarily creating dummy BC (l3id:%x).\n", hh->dinfo); - misdn_make_dummy(&dummybc, stack->port, hh->dinfo, stack->nt, 0); - bc=&dummybc; + switch (event) { + case EVENT_SETUP: + if (bc->channel <= 0 || bc->channel == 0xff) { + bc->channel = 0; + } + + if (find_free_chan_in_stack(stack, bc, bc->channel, 0) < 0) { + goto ERR_NO_CHANNEL; } - if (bc ) { - isdn_msg_parse_event(msgs_g,msg,bc, 1); + break; + case EVENT_RELEASE: + case EVENT_RELEASE_COMPLETE: + channel = bc->channel; + tmpcause = bc->cause; - switch (event) { - case EVENT_SETUP: - if (bc->channel<=0 || bc->channel==0xff) - bc->channel=0; + empty_bc(bc); + bc->cause = tmpcause; + clean_up_bc(bc); - if (find_free_chan_in_stack(stack,bc, bc->channel,0)<0) - goto ERR_NO_CHANNEL; - break; - case EVENT_RELEASE: - case EVENT_RELEASE_COMPLETE: - { - int channel=bc->channel; - int tmpcause=bc->cause; - empty_bc(bc); - bc->cause=tmpcause; - clean_up_bc(bc); - - if (channel>0) - empty_chan_in_stack(stack,channel); - bc->in_use=0; - } - break; + if (channel > 0) + empty_chan_in_stack(stack, channel); + bc->in_use = 0; + break; + default: + break; + } - default: + if(!isdn_get_info(msgs_g, event, 1)) { + cb_log(4, stack->port, "Unknown Event Ind: prim %x dinfo %x\n", hh->prim, hh->dinfo); + } else { + if (reject) { + switch(bc->cause) { + case AST_CAUSE_USER_BUSY: + cb_log(1, stack->port, "Siemens Busy reject..\n"); + break; + default: break; } - - if(!isdn_get_info(msgs_g,event,1)) { - cb_log(4, stack->port, "Unknown Event Ind: prim %x dinfo %x\n",hh->prim, hh->dinfo); - } else { - if (reject) { - switch(bc->cause){ - case AST_CAUSE_USER_BUSY: - cb_log(1, stack->port, "Siemens Busy reject..\n"); - - break; - default: - break; - } - } - cb_event(event, bc, glob_mgr->user_data); - } - } else { - cb_log(4, stack->port, "No BC found with l3id: prim %x dinfo %x\n",hh->prim, hh->dinfo); } - - free_msg(msg); + cb_event(event, bc, glob_mgr->user_data); } + free_msg(msg); + return 0; +ERR_NO_CHANNEL: + cb_log(4, stack->port, "Patch from MEIDANIS:Sending RELEASE_COMPLETE %x (No free Chan for you..)\n", hh->dinfo); + dmsg = create_l3msg(CC_RELEASE_COMPLETE | REQUEST, MT_RELEASE_COMPLETE, hh->dinfo, sizeof(RELEASE_COMPLETE_t), 1); + stack->nst.manager_l3(&stack->nst, dmsg); + free_msg(msg); return 0; } @@ -2690,158 +2716,144 @@ static int handle_frm_nt(msg_t *msg) static int handle_frm(msg_t *msg) { - iframe_t *frm = (iframe_t*) msg->data; - - struct misdn_stack *stack=find_stack_by_addr(frm->addr); + struct misdn_bchannel dummybc; + struct misdn_bchannel *bc; + iframe_t *frm; + struct misdn_stack *stack; + enum event_e event; + enum event_response_e response; + int ret; + int channel; + int tmpcause; + int tmp_out_cause; + frm = (iframe_t*) msg->data; + stack = find_stack_by_addr(frm->addr); if (!stack || stack->nt) { return 0; } cb_log(4, stack ? stack->port : 0, "handle_frm: frm->addr:%x frm->prim:%x\n", frm->addr, frm->prim); - { - struct misdn_bchannel dummybc; - struct misdn_bchannel *bc; - int ret=handle_cr(stack, frm); - - if (ret<0) { - cb_log(3,stack?stack->port:0,"handle_frm: handle_cr <0 prim:%x addr:%x\n", frm->prim, frm->addr); - - - } - - if(ret) { - free_msg(msg); - return 1; - } - - bc=find_bc_by_l3id(stack, frm->dinfo); - - if (!bc && (frm->prim==(CC_RESTART|CONFIRM)) ) { - misdn_make_dummy(&dummybc, stack->port, MISDN_ID_GLOBAL, stack->nt, 0); - bc=&dummybc; - } + ret = handle_cr(stack, frm); + if (ret < 0) { + cb_log(3, stack ? stack->port : 0, "handle_frm: handle_cr <0 prim:%x addr:%x\n", frm->prim, frm->addr); + } + if (ret) { + free_msg(msg); + return 1; + } - if (!bc && (frm->prim==(CC_SETUP|INDICATION)) ) { - misdn_make_dummy(&dummybc, stack->port, MISDN_ID_GLOBAL, stack->nt, 0); - dummybc.port=stack->port; - dummybc.l3_id=frm->dinfo; - bc=&dummybc; + bc = find_bc_by_l3id(stack, frm->dinfo); + if (!bc) { + misdn_make_dummy(&dummybc, stack->port, 0, stack->nt, 0); + switch (frm->prim) { + case CC_RESTART | CONFIRM: + dummybc.l3_id = MISDN_ID_GLOBAL; + bc = &dummybc; + break; + case CC_SETUP | INDICATION: + dummybc.l3_id = frm->dinfo; + bc = &dummybc; - misdn_lib_send_event(bc,EVENT_RELEASE_COMPLETE); + misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); free_msg(msg); return 1; - } - - -handle_frm_bc: - if (bc ) { - enum event_e event = isdn_msg_get_event(msgs_g, msg, 0); - enum event_response_e response=RESPONSE_OK; - int ret; - - isdn_msg_parse_event(msgs_g,msg,bc, 0); - - /** Preprocess some Events **/ - ret = handle_event(bc, event, frm); - if (ret<0) { - cb_log(0,stack->port,"couldn't handle event\n"); - free_msg(msg); - return 1; + default: + if (frm->prim == (CC_FACILITY | INDICATION)) { + cb_log(5, stack->port, " --> Using Dummy BC for FACILITY\n"); + } else { + cb_log(0, stack->port, " --> Didn't find BC so temporarily creating dummy BC (l3id:%x) on this port.\n", frm->dinfo); + dummybc.l3_id = frm->dinfo; } - /* shoot up event to App: */ - cb_log(5, stack->port, "lib Got Prim: Addr %x prim %x dinfo %x\n",frm->addr, frm->prim, frm->dinfo); - - if(!isdn_get_info(msgs_g,event,0)) - cb_log(0, stack->port, "Unknown Event Ind: Addr:%x prim %x dinfo %x\n",frm->addr, frm->prim, frm->dinfo); - else - response=cb_event(event, bc, glob_mgr->user_data); -#if 1 - if (event == EVENT_SETUP) { - switch (response) { - case RESPONSE_IGNORE_SETUP_WITHOUT_CLOSE: + bc = &dummybc; + break; + } + } - cb_log(0, stack->port, "TOTALLY IGNORING SETUP\n"); + event = isdn_msg_get_event(msgs_g, msg, 0); + isdn_msg_parse_event(msgs_g, msg, bc, 0); - break; - case RESPONSE_IGNORE_SETUP: - /* I think we should send CC_RELEASE_CR, but am not sure*/ - bc->out_cause = AST_CAUSE_NORMAL_CLEARING; - - case RESPONSE_RELEASE_SETUP: - misdn_lib_send_event(bc,EVENT_RELEASE_COMPLETE); - if (bc->channel>0) - empty_chan_in_stack(stack, bc->channel); - empty_bc(bc); - bc_state_change(bc,BCHAN_CLEANED); - bc->in_use=0; - - cb_log(0, stack->port, "GOT IGNORE SETUP\n"); - break; - case RESPONSE_OK: - cb_log(4, stack->port, "GOT SETUP OK\n"); + /* Preprocess some Events */ + ret = handle_event(bc, event, frm); + if (ret < 0) { + cb_log(0, stack->port, "couldn't handle event\n"); + free_msg(msg); + return 1; + } + /* shoot up event to App: */ + cb_log(5, stack->port, "lib Got Prim: Addr %x prim %x dinfo %x\n", frm->addr, frm->prim, frm->dinfo); - break; - default: - break; - } - } - - if (event == EVENT_RELEASE_COMPLETE) { - /* release bchannel only after we've announced the RELEASE_COMPLETE */ - int channel=bc->channel; - int tmpcause=bc->cause; - int tmp_out_cause=bc->out_cause; - empty_bc(bc); - bc->cause=tmpcause; - bc->out_cause=tmp_out_cause; - clean_up_bc(bc); - - if (tmpcause == AST_CAUSE_REQUESTED_CHAN_UNAVAIL) { - cb_log(0,stack->port,"**** Received CAUSE:%d, so not cleaning up channel %d\n", AST_CAUSE_REQUESTED_CHAN_UNAVAIL, channel); - cb_log(0,stack->port,"**** This channel is now no longer available,\nplease try to restart it with 'misdn send restart <port> <channel>'\n"); - set_chan_in_stack(stack, channel); - bc->channel=channel; - misdn_lib_send_restart(stack->port, channel); - } else { - if (channel>0) - empty_chan_in_stack(stack, channel); - } - bc->in_use=0; - } + if (!isdn_get_info(msgs_g, event, 0)) { + cb_log(0, stack->port, "Unknown Event Ind: Addr:%x prim %x dinfo %x\n", frm->addr, frm->prim, frm->dinfo); + response = RESPONSE_OK; + } else { + response = cb_event(event, bc, glob_mgr->user_data); + } - if (event == EVENT_RESTART) { - cb_log(0, stack->port, "**** Received RESTART_ACK channel:%d\n", bc->restart_channel); - empty_chan_in_stack(stack, bc->restart_channel); + switch (event) { + case EVENT_SETUP: + switch (response) { + case RESPONSE_IGNORE_SETUP_WITHOUT_CLOSE: + cb_log(0, stack->port, "TOTALLY IGNORING SETUP\n"); + break; + case RESPONSE_IGNORE_SETUP: + /* I think we should send CC_RELEASE_CR, but am not sure*/ + bc->out_cause = AST_CAUSE_NORMAL_CLEARING; + /* fall through */ + case RESPONSE_RELEASE_SETUP: + misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); + if (bc->channel > 0) { + empty_chan_in_stack(stack, bc->channel); } + empty_bc(bc); + bc_state_change(bc, BCHAN_CLEANED); + bc->in_use = 0; - cb_log(5, stack->port, "Freeing Msg on prim:%x \n",frm->prim); - - - free_msg(msg); - return 1; -#endif - - } else { - struct misdn_bchannel dummybc; - if (frm->prim!=(CC_FACILITY|INDICATION)) - cb_log(0, stack->port, " --> Didn't find BC so temporarily creating dummy BC (l3id:%x) on this port.\n", frm->dinfo); - else - cb_log(5, stack->port, " --> Using Dummy BC for FACILITY\n"); - - memset (&dummybc,0,sizeof(dummybc)); - dummybc.port=stack->port; - dummybc.l3_id=frm->dinfo; - bc=&dummybc; - goto handle_frm_bc; + cb_log(0, stack->port, "GOT IGNORE SETUP\n"); + break; + case RESPONSE_OK: + cb_log(4, stack->port, "GOT SETUP OK\n"); + break; + default: + break; } + break; + case EVENT_RELEASE_COMPLETE: + /* release bchannel only after we've announced the RELEASE_COMPLETE */ + channel = bc->channel; + tmpcause = bc->cause; + tmp_out_cause = bc->out_cause; + + empty_bc(bc); + bc->cause = tmpcause; + bc->out_cause = tmp_out_cause; + clean_up_bc(bc); + + if (tmpcause == AST_CAUSE_REQUESTED_CHAN_UNAVAIL) { + cb_log(0, stack->port, "**** Received CAUSE:%d, so not cleaning up channel %d\n", AST_CAUSE_REQUESTED_CHAN_UNAVAIL, channel); + cb_log(0, stack->port, "**** This channel is now no longer available,\nplease try to restart it with 'misdn send restart <port> <channel>'\n"); + set_chan_in_stack(stack, channel); + bc->channel = channel; + misdn_lib_send_restart(stack->port, channel); + } else if (channel > 0) { + empty_chan_in_stack(stack, channel); + } + bc->in_use = 0; + break; + case EVENT_RESTART: + cb_log(0, stack->port, "**** Received RESTART_ACK channel:%d\n", bc->restart_channel); + empty_chan_in_stack(stack, bc->restart_channel); + break; + default: + break; } - cb_log(4, stack->port, "TE_FRM_HANDLER: Returning 0 on prim:%x \n",frm->prim); - return 0; + cb_log(5, stack->port, "Freeing Msg on prim:%x \n", frm->prim); + free_msg(msg); + return 1; } @@ -3148,7 +3160,8 @@ static void misdn_lib_isdn_event_catcher(void *arg) /** App Interface **/ -int te_lib_init(void) { +static int te_lib_init(void) +{ char buff[1025] = ""; iframe_t *frm = (iframe_t *) buff; int midev; @@ -3163,18 +3176,12 @@ int te_lib_init(void) { mISDN_write_frame(midev, buff, 0, MGR_NEWENTITY | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); ret = mISDN_read_frame(midev, frm, sizeof(iframe_t), 0, MGR_NEWENTITY | CONFIRM, TIMEOUT_1SEC); - - if (ret < mISDN_HEADER_LEN) { - noentity: + entity = frm->dinfo & 0xffff; + if (ret < mISDN_HEADER_LEN || !entity) { fprintf(stderr, "cannot request MGR_NEWENTITY from mISDN: %s\n", strerror(errno)); exit(-1); } - entity = frm->dinfo & 0xffff ; - - if (!entity) - goto noentity; - return midev; } @@ -3221,9 +3228,11 @@ struct misdn_bchannel *manager_find_bc_holded(struct misdn_bchannel* bc) static int test_inuse(struct misdn_bchannel *bc) { struct timeval now; - gettimeofday(&now, NULL); + if (!bc->in_use) { - if (misdn_lib_port_is_pri(bc->port) && bc->last_used.tv_sec == now.tv_sec ) { + gettimeofday(&now, NULL); + if (bc->last_used.tv_sec == now.tv_sec + && misdn_lib_port_is_pri(bc->port)) { cb_log(2, bc->port, "channel with stid:%x for one second still in use! (n:%d lu:%d)\n", bc->b_stid, (int) now.tv_sec, (int) bc->last_used.tv_sec); return 1; @@ -3262,6 +3271,7 @@ struct misdn_bchannel *misdn_lib_get_free_bc(int port, int channel, int inout, i { struct misdn_stack *stack; int i; + int maxnum; if (channel < 0 || channel > MAX_BCHANS) { cb_log(0, port, "Requested channel out of bounds (%d)\n", channel); @@ -3270,71 +3280,111 @@ struct misdn_bchannel *misdn_lib_get_free_bc(int port, int channel, int inout, i usleep(1000); - for (stack=glob_mgr->stack_list; stack; stack=stack->next) { - - if (stack->port == port) { - int maxnum; + /* Find the port stack structure */ + stack = find_stack_by_port(port); + if (!stack) { + cb_log(0, port, "Port is not configured (%d)\n", port); + return NULL; + } - if (stack->blocked) { - cb_log(0,port,"Port is blocked\n"); - return NULL; - } + if (stack->blocked) { + cb_log(0, port, "Port is blocked\n"); + return NULL; + } - if (channel > 0) { - if (channel <= stack->b_num) { - for (i = 0; i < stack->b_num; i++) { - if ( stack->bc[i].channel == channel) { - if (test_inuse(&stack->bc[i])) { - cb_log(0,port,"Requested channel:%d on port:%d is already in use\n",channel, port); - return NULL; - - } else { - prepare_bc(&stack->bc[i], channel); - return &stack->bc[i]; - } - } + if (channel > 0) { + if (channel <= stack->b_num) { + for (i = 0; i < stack->b_num; i++) { + if (stack->bc[i].channel == channel) { + if (test_inuse(&stack->bc[i])) { + cb_log(0, port, "Requested channel:%d on port:%d is already in use\n", channel, port); + return NULL; + } else { + prepare_bc(&stack->bc[i], channel); + return &stack->bc[i]; } - } else { - cb_log(0,port,"Requested channel:%d is out of bounds on port:%d\n",channel, port); - return NULL; } } + } else { + cb_log(0, port, "Requested channel:%d is out of bounds on port:%d\n", channel, port); + return NULL; + } + } - maxnum = inout && !stack->pri && !stack->ptp ? stack->b_num + 1 : stack->b_num; - - if (dec) { - for (i = maxnum-1; i>=0; i--) { - if (!test_inuse(&stack->bc[i])) { - /* 3. channel on bri means CW*/ - if (!stack->pri && i==stack->b_num) - stack->bc[i].cw=1; - - prepare_bc(&stack->bc[i], channel); - stack->bc[i].dec=1; - return &stack->bc[i]; - } + /* Note: channel == 0 here */ + maxnum = (inout && !stack->pri && !stack->ptp) ? stack->b_num + 1 : stack->b_num; + if (dec) { + for (i = maxnum - 1; i >= 0; --i) { + if (!test_inuse(&stack->bc[i])) { + /* 3. channel on bri means CW*/ + if (!stack->pri && i == stack->b_num) { + stack->bc[i].cw = 1; } - } else { - for (i = 0; i <maxnum; i++) { - if (!test_inuse(&stack->bc[i])) { - /* 3. channel on bri means CW*/ - if (!stack->pri && i==stack->b_num) - stack->bc[i].cw=1; - prepare_bc(&stack->bc[i], channel); - return &stack->bc[i]; - } + prepare_bc(&stack->bc[i], channel); + stack->bc[i].dec = 1; + return &stack->bc[i]; + } + } + } else { + for (i = 0; i < maxnum; ++i) { + if (!test_inuse(&stack->bc[i])) { + /* 3. channel on bri means CW */ + if (!stack->pri && i == stack->b_num) { + stack->bc[i].cw = 1; } + + prepare_bc(&stack->bc[i], channel); + return &stack->bc[i]; } + } + } - cb_log(1,port,"There is no free channel on port (%d)\n",port); - return NULL; + cb_log(1, port, "There is no free channel on port (%d)\n", port); + return NULL; +} + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \brief Allocate a B channel struct from the REGISTER pool + * + * \param port Logical port number + * + * \retval B channel struct on success. + * \retval NULL on error. + */ +struct misdn_bchannel *misdn_lib_get_register_bc(int port) +{ + struct misdn_stack *stack; + struct misdn_bchannel *bc; + unsigned index; + + /* Find the port stack structure */ + stack = find_stack_by_port(port); + if (!stack) { + cb_log(0, port, "Port is not configured (%d)\n", port); + return NULL; + } + + if (stack->blocked) { + cb_log(0, port, "Port is blocked\n"); + return NULL; + } + + for (index = MAX_BCHANS + 1; index < ARRAY_LEN(stack->bc); ++index) { + bc = &stack->bc[index]; + if (!test_inuse(bc)) { + prepare_bc(bc, 0); + bc->need_disconnect = 0; + bc->need_release = 0; + return bc; } } - cb_log(0,port,"Port is not configured (%d)\n",port); + cb_log(1, port, "There is no free REGISTER link on port (%d)\n", port); return NULL; } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ /*! * \internal @@ -3350,12 +3400,77 @@ static const char *fac2str(enum FacFunction facility) } arr[] = { /* *INDENT-OFF* */ { Fac_None, "Fac_None" }, +#if defined(AST_MISDN_ENHANCEMENTS) + { Fac_ERROR, "Fac_ERROR" }, + { Fac_RESULT, "Fac_RESULT" }, + { Fac_REJECT, "Fac_REJECT" }, + + { Fac_ActivationDiversion, "Fac_ActivationDiversion" }, + { Fac_DeactivationDiversion, "Fac_DeactivationDiversion" }, + { Fac_ActivationStatusNotificationDiv, "Fac_ActivationStatusNotificationDiv" }, + { Fac_DeactivationStatusNotificationDiv, "Fac_DeactivationStatusNotificationDiv" }, + { Fac_InterrogationDiversion, "Fac_InterrogationDiversion" }, + { Fac_DiversionInformation, "Fac_DiversionInformation" }, + { Fac_CallDeflection, "Fac_CallDeflection" }, + { Fac_CallRerouteing, "Fac_CallRerouteing" }, + { Fac_DivertingLegInformation2, "Fac_DivertingLegInformation2" }, + { Fac_InterrogateServedUserNumbers, "Fac_InterrogateServedUserNumbers" }, + { Fac_DivertingLegInformation1, "Fac_DivertingLegInformation1" }, + { Fac_DivertingLegInformation3, "Fac_DivertingLegInformation3" }, + + { Fac_EctExecute, "Fac_EctExecute" }, + { Fac_ExplicitEctExecute, "Fac_ExplicitEctExecute" }, + { Fac_RequestSubaddress, "Fac_RequestSubaddress" }, + { Fac_SubaddressTransfer, "Fac_SubaddressTransfer" }, + { Fac_EctLinkIdRequest, "Fac_EctLinkIdRequest" }, + { Fac_EctInform, "Fac_EctInform" }, + { Fac_EctLoopTest, "Fac_EctLoopTest" }, + + { Fac_ChargingRequest, "Fac_ChargingRequest" }, + { Fac_AOCSCurrency, "Fac_AOCSCurrency" }, + { Fac_AOCSSpecialArr, "Fac_AOCSSpecialArr" }, + { Fac_AOCDCurrency, "Fac_AOCDCurrency" }, + { Fac_AOCDChargingUnit, "Fac_AOCDChargingUnit" }, + { Fac_AOCECurrency, "Fac_AOCECurrency" }, + { Fac_AOCEChargingUnit, "Fac_AOCEChargingUnit" }, + + { Fac_StatusRequest, "Fac_StatusRequest" }, + + { Fac_CallInfoRetain, "Fac_CallInfoRetain" }, + { Fac_EraseCallLinkageID, "Fac_EraseCallLinkageID" }, + { Fac_CCBSDeactivate, "Fac_CCBSDeactivate" }, + { Fac_CCBSErase, "Fac_CCBSErase" }, + { Fac_CCBSRemoteUserFree, "Fac_CCBSRemoteUserFree" }, + { Fac_CCBSCall, "Fac_CCBSCall" }, + { Fac_CCBSStatusRequest, "Fac_CCBSStatusRequest" }, + { Fac_CCBSBFree, "Fac_CCBSBFree" }, + { Fac_CCBSStopAlerting, "Fac_CCBSStopAlerting" }, + + { Fac_CCBSRequest, "Fac_CCBSRequest" }, + { Fac_CCBSInterrogate, "Fac_CCBSInterrogate" }, + + { Fac_CCNRRequest, "Fac_CCNRRequest" }, + { Fac_CCNRInterrogate, "Fac_CCNRInterrogate" }, + + { Fac_CCBS_T_Call, "Fac_CCBS_T_Call" }, + { Fac_CCBS_T_Suspend, "Fac_CCBS_T_Suspend" }, + { Fac_CCBS_T_Resume, "Fac_CCBS_T_Resume" }, + { Fac_CCBS_T_RemoteUserFree, "Fac_CCBS_T_RemoteUserFree" }, + { Fac_CCBS_T_Available, "Fac_CCBS_T_Available" }, + + { Fac_CCBS_T_Request, "Fac_CCBS_T_Request" }, + + { Fac_CCNR_T_Request, "Fac_CCNR_T_Request" }, + +#else + { Fac_CFActivate, "Fac_CFActivate" }, { Fac_CFDeactivate, "Fac_CFDeactivate" }, { Fac_CD, "Fac_CD" }, { Fac_AOCDCurrency, "Fac_AOCDCurrency" }, { Fac_AOCDChargingUnit, "Fac_AOCDChargingUnit" }, +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ /* *INDENT-ON* */ }; @@ -3403,14 +3518,25 @@ void misdn_lib_log_ies(struct misdn_bchannel *bc) bc->caller.screening); cb_log(2, stack->port, - " --> redirecting:\"%s\" <%s> type:%d plan:%d pres:%d screen:%d reason:%d\n", + " --> redirecting-from:\"%s\" <%s> type:%d plan:%d pres:%d screen:%d\n", bc->redirecting.from.name, bc->redirecting.from.number, bc->redirecting.from.number_type, bc->redirecting.from.number_plan, bc->redirecting.from.presentation, - bc->redirecting.from.screening, - bc->redirecting.reason); + bc->redirecting.from.screening); + cb_log(2, stack->port, + " --> redirecting-to:\"%s\" <%s> type:%d plan:%d pres:%d screen:%d\n", + bc->redirecting.to.name, + bc->redirecting.to.number, + bc->redirecting.to.number_type, + bc->redirecting.to.number_plan, + bc->redirecting.to.presentation, + bc->redirecting.to.screening); + cb_log(2, stack->port, + " --> redirecting reason:%d count:%d\n", + bc->redirecting.reason, + bc->redirecting.count); cb_log(2, stack->port, " --> connected:\"%s\" <%s> type:%d plan:%d pres:%d screen:%d\n", @@ -3427,7 +3553,7 @@ void misdn_lib_log_ies(struct misdn_bchannel *bc) cb_log(4, stack->port, " --> addr:%x l3id:%x b_stid:%x layer_id:%x\n", bc->addr, bc->l3_id, bc->b_stid, bc->layer_id); - cb_log(4, stack->port, " --> facility:%s out_facility:%s\n",fac2str(bc->fac_in.Function),fac2str(bc->fac_out.Function)); + cb_log(4, stack->port, " --> facility in:%s out:%s\n", fac2str(bc->fac_in.Function), fac2str(bc->fac_out.Function)); cb_log(5, stack->port, " --> urate:%d rate:%d mode:%d user1:%d\n", bc->urate, bc->rate, bc->mode,bc->user1); @@ -3454,8 +3580,13 @@ static void misdn_send_unlock(struct misdn_bchannel *bc) int misdn_lib_send_event(struct misdn_bchannel *bc, enum event_e event ) { msg_t *msg; - int retval=0; + struct misdn_bchannel *bc2; + struct misdn_bchannel *holded_bc; struct misdn_stack *stack; + int retval = 0; + int channel; + int tmpcause; + int tmp_out_cause; if (!bc) RETURN(-1,OUT_POST_UNLOCK); @@ -3495,6 +3626,7 @@ int misdn_lib_send_event(struct misdn_bchannel *bc, enum event_e event ) misdn_lib_log_ies(bc); switch (event) { + case EVENT_REGISTER: case EVENT_SETUP: if (create_process(glob_mgr->midev, bc) < 0) { cb_log(0, stack->port, " No free channel at the moment @ send_event\n"); @@ -3560,8 +3692,7 @@ int misdn_lib_send_event(struct misdn_bchannel *bc, enum event_e event ) break; case EVENT_HOLD_ACKNOWLEDGE: - { - struct misdn_bchannel *holded_bc=malloc(sizeof(struct misdn_bchannel)); + holded_bc = malloc(sizeof(struct misdn_bchannel)); if (!holded_bc) { cb_log(0,bc->port, "Could not allocate holded_bc!!!\n"); RETURN(-1,OUT); @@ -3576,10 +3707,7 @@ int misdn_lib_send_event(struct misdn_bchannel *bc, enum event_e event ) /*kill the bridge and clean the bchannel*/ if (stack->nt) { - int channel; if (bc->bc_state == BCHAN_BRIDGED) { - struct misdn_bchannel *bc2; - misdn_split_conf(bc,bc->conf_id); bc2 = find_bc_by_confid(bc->conf_id); if (!bc2) { @@ -3599,14 +3727,12 @@ int misdn_lib_send_event(struct misdn_bchannel *bc, enum event_e event ) bc->in_use=0; } - - } - break; + break; /* finishing the channel eh ? */ case EVENT_DISCONNECT: if (!bc->need_disconnect) { - cb_log(0,bc->port," --> we have already send Disconnect\n"); + cb_log(0, bc->port, " --> we have already sent DISCONNECT\n"); RETURN(-1,OUT); } @@ -3614,7 +3740,7 @@ int misdn_lib_send_event(struct misdn_bchannel *bc, enum event_e event ) break; case EVENT_RELEASE: if (!bc->need_release) { - cb_log(0,bc->port," --> we have already send Release\n"); + cb_log(0, bc->port, " --> we have already sent RELEASE\n"); RETURN(-1,OUT); } bc->need_disconnect=0; @@ -3622,7 +3748,7 @@ int misdn_lib_send_event(struct misdn_bchannel *bc, enum event_e event ) break; case EVENT_RELEASE_COMPLETE: if (!bc->need_release_complete) { - cb_log(0,bc->port," --> we have already send Release_complete\n"); + cb_log(0, bc->port, " --> we have already sent RELEASE_COMPLETE\n"); RETURN(-1,OUT); } bc->need_disconnect=0; @@ -3631,10 +3757,10 @@ int misdn_lib_send_event(struct misdn_bchannel *bc, enum event_e event ) if (!stack->nt) { /* create cleanup in TE */ - int channel=bc->channel; + channel = bc->channel; + tmpcause = bc->cause; + tmp_out_cause = bc->out_cause; - int tmpcause=bc->cause; - int tmp_out_cause=bc->out_cause; empty_bc(bc); bc->cause=tmpcause; bc->out_cause=tmp_out_cause; @@ -4206,8 +4332,10 @@ int misdn_lib_init(char *portlist, struct misdn_lib_iface *iface, void *user_dat tok=strtok_r(NULL," ,",&tokb)) { int port = atoi(tok); struct misdn_stack *stack; - static int first=1; + struct misdn_stack *help; int ptp=0; + int i; + int r; if (strstr(tok, "ptp")) ptp=1; @@ -4223,27 +4351,34 @@ int misdn_lib_init(char *portlist, struct misdn_lib_iface *iface, void *user_dat exit(1); } - { - int i; - for(i=0;i<=stack->b_num; i++) { - int r; - if ((r=init_bc(stack, &stack->bc[i], stack->midev,port,i, "", 1))<0) { - cb_log(0, port, "Got Err @ init_bc :%d\n",r); - exit(1); - } + /* Initialize the B channel records for real B channels. */ + for (i = 0; i <= stack->b_num; i++) { + r = init_bc(stack, &stack->bc[i], stack->midev, port, i); + if (r < 0) { + cb_log(0, port, "Got Err @ init_bc :%d\n", r); + exit(1); } } - - if (stack && first) { - mgr->stack_list=stack; - first=0; - continue; +#if defined(AST_MISDN_ENHANCEMENTS) + /* Initialize the B channel records for REGISTER signaling links. */ + for (i = MAX_BCHANS + 1; i < ARRAY_LEN(stack->bc); ++i) { + r = init_bc(stack, &stack->bc[i], stack->midev, port, i); + if (r < 0) { + cb_log(0, port, "Got Err @ init_bc :%d\n", r); + exit(1); + } + stack->bc[i].is_register_pool = 1; } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ - if (stack) { - struct misdn_stack * help; - for ( help=mgr->stack_list; help; help=help->next ) - if (help->next == NULL) break; + /* Add the new stack to the end of the list */ + help = mgr->stack_list; + if (!help) { + mgr->stack_list = stack; + } else { + while (help->next) { + help = help->next; + } help->next = stack; } } @@ -4473,8 +4608,9 @@ void manager_clean_bc(struct misdn_bchannel *bc ) { struct misdn_stack *stack=get_stack_by_bc(bc); - if (bc->channel>0) + if (stack && bc->channel > 0) { empty_chan_in_stack(stack, bc->channel); + } empty_bc(bc); bc->in_use=0; diff --git a/channels/misdn/isdn_lib.h b/channels/misdn/isdn_lib.h index 616721332..7bd516565 100644 --- a/channels/misdn/isdn_lib.h +++ b/channels/misdn/isdn_lib.h @@ -133,6 +133,7 @@ enum event_e { EVENT_PROCEEDING, EVENT_PROGRESS, EVENT_SETUP, + EVENT_REGISTER, EVENT_ALERTING, EVENT_CONNECT, EVENT_SETUP_ACKNOWLEDGE, @@ -216,6 +217,25 @@ enum mISDN_REDIRECTING_REASON { mISDN_REDIRECTING_REASON_CALL_FWD = 0xF }; +/*! + * \brief Notification description code enumeration + */ +enum mISDN_NOTIFY_CODE { + mISDN_NOTIFY_CODE_INVALID = -1, + /*! Call is placed on hold (Q.931) */ + mISDN_NOTIFY_CODE_USER_SUSPEND = 0x00, + /*! Call is taken off of hold (Q.931) */ + mISDN_NOTIFY_CODE_USER_RESUME = 0x01, + /*! Call is diverting (EN 300 207-1 Section 7.2.1) */ + mISDN_NOTIFY_CODE_CALL_IS_DIVERTING = 0x7B, + /*! Call diversion is enabled (cfu, cfb, cfnr) (EN 300 207-1 Section 7.2.1) */ + mISDN_NOTIFY_CODE_DIVERSION_ACTIVATED = 0x68, + /*! Call transfer, alerting (EN 300 369-1 Section 7.2) */ + mISDN_NOTIFY_CODE_CALL_TRANSFER_ALERTING = 0x69, + /*! Call transfer, active(answered) (EN 300 369-1 Section 7.2) */ + mISDN_NOTIFY_CODE_CALL_TRANSFER_ACTIVE = 0x6A, +}; + enum { /*CODECS*/ INFO_CODEC_ULAW=2, INFO_CODEC_ALAW=3 @@ -241,6 +261,21 @@ enum layer_e { /*! Maximum keypad facility content length plus null terminator */ #define MISDN_MAX_KEYPAD_LEN (31 + 1) +/*! \brief Dialed/Called information struct */ +struct misdn_party_dialing { + /*! \brief Type-of-number in ISDN terms for the dialed/called number */ + enum mISDN_NUMBER_TYPE number_type; + + /*! \brief Type-of-number numbering plan. */ + enum mISDN_NUMBER_PLAN number_plan; + + /*! \brief Dialed/Called Phone Number (Address) */ + char number[MISDN_MAX_NUMBER_LEN]; + + /*! \brief Dialed/Called Subaddress number */ + char subaddress[MISDN_MAX_SUBADDRESS_LEN]; +}; + /*! \brief Connected-Line/Calling/Redirecting ID info struct */ struct misdn_party_id { /*! \brief Number presentation restriction code @@ -278,8 +313,17 @@ struct misdn_party_redirecting { /*! \brief Who is redirecting the call (Sent to the party the call is redirected toward) */ struct misdn_party_id from; + /*! \brief Where the call is being redirected toward (Sent to the calling party) */ + struct misdn_party_id to; + /*! \brief Reason a call is being redirected (Q.931 field value) */ enum mISDN_REDIRECTING_REASON reason; + + /*! \brief Number of times the call has been redirected */ + int count; + + /*! \brief TRUE if the redirecting.to information has changed */ + int to_changed; }; @@ -288,6 +332,17 @@ struct misdn_bchannel { /*! \brief B channel send locking structure */ struct send_lock *send_lock; +#if defined(AST_MISDN_ENHANCEMENTS) + /*! \brief The BC, HLC (optional) and LLC (optional) contents from the SETUP message. */ + struct Q931_Bc_Hlc_Llc setup_bc_hlc_llc; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + + /*! + * \brief Dialed/Called information struct + * \note The number_type element is set to "dialplan" in /etc/asterisk/misdn.conf for outgoing calls + */ + struct misdn_party_dialing dialed; + /*! \brief Originating/Caller ID information struct * \note The number_type element can be set to "localdialplan" in /etc/asterisk/misdn.conf for outgoing calls * \note The number element can be set to "callerid" in /etc/asterisk/misdn.conf for outgoing calls @@ -360,6 +415,9 @@ struct misdn_bchannel { /*! \brief TRUE if the B channel number is preselected */ int channel_preselected; + /*! \brief TRUE if the B channel is allocated from the REGISTER pool */ + int is_register_pool; + /*! \brief TRUE if B channel record is in use */ int in_use; @@ -382,8 +440,6 @@ struct misdn_bchannel { /*! \brief Not used. Contents are setup but not used. */ void *astbuf; - void *misdnbuf; /* Not used */ - /*! \brief TRUE if the TE side should choose the B channel to use * \note This value is user configurable in /etc/asterisk/misdn.conf */ @@ -530,6 +586,9 @@ struct misdn_bchannel { /*! \brief TRUE if the user set the presentation restriction code */ int set_presentation; + /*! \brief Notification indicator ie description code */ + enum mISDN_NOTIFY_CODE notify_description_code; + /*! \brief SETUP message bearer capability field code value */ int capability; @@ -560,32 +619,12 @@ struct misdn_bchannel { int hdlc; /* V110 */ - /*! \brief Dialed/Called information struct */ - struct { - /*! \brief Type-of-number in ISDN terms for the dialed/called number - * \note This value is set to "dialplan" in /etc/asterisk/misdn.conf for outgoing calls - */ - enum mISDN_NUMBER_TYPE number_type; - - /*! \brief Type-of-number numbering plan. */ - enum mISDN_NUMBER_PLAN number_plan; - - /*! \brief Dialed/Called Phone Number (Address) */ - char number[MISDN_MAX_NUMBER_LEN]; - - /*! \brief Dialed/Called Subaddress number */ - char subaddress[MISDN_MAX_SUBADDRESS_LEN]; - } dialed; - /*! \brief Display message that can be displayed by the user phone. * \note Maximum displayable length is 34 or 82 octets. * It is also settable by the misdn_set_opt() application. */ char display[84]; - /*! \brief Not used. Contents are setup but not used. */ - char msn[MISDN_MAX_NUMBER_LEN]; - /*! \brief Q.931 Keypad Facility IE contents * \note Contents exported and imported to Asterisk variable MISDN_KEYPAD */ @@ -696,6 +735,9 @@ char *manager_isdn_get_info(enum event_e event); void misdn_lib_transfer(struct misdn_bchannel* holded_bc); struct misdn_bchannel* misdn_lib_get_free_bc(int port, int channel, int inout, int dec); +#if defined(AST_MISDN_ENHANCEMENTS) +struct misdn_bchannel *misdn_lib_get_register_bc(int port); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ void manager_bchannel_activate(struct misdn_bchannel *bc); void manager_bchannel_deactivate(struct misdn_bchannel * bc); diff --git a/channels/misdn/isdn_lib_intern.h b/channels/misdn/isdn_lib_intern.h index 3347fe335..7e7ea8a61 100644 --- a/channels/misdn/isdn_lib_intern.h +++ b/channels/misdn/isdn_lib_intern.h @@ -51,7 +51,15 @@ struct isdn_msg { /* for isdn_msg_parser.c */ msg_t *create_l3msg(int prim, int mt, int dinfo , int size, int nt); +#if defined(AST_MISDN_ENHANCEMENTS) +/* Max call-completion REGISTER signaling links per stack/port */ +#define MISDN_MAX_REGISTER_LINKS MAX_BCHANS +#else +/* Max call-completion REGISTER signaling links per stack/port */ +#define MISDN_MAX_REGISTER_LINKS 0 +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ +#define MAXPROCS 0x100 struct misdn_stack { /** is first element because &nst equals &mISDNlist **/ @@ -87,8 +95,6 @@ struct misdn_stack { /*! \brief TRUE if Layer 2 is UP */ int l2link; - time_t l2establish; /* Not used */ - /*! \brief TRUE if Layer 1 is UP */ int l1link; @@ -105,7 +111,7 @@ struct misdn_stack { int pri; /*! \brief CR Process ID allocation table. TRUE if ID allocated */ - int procids[0x100+1]; + int procids[MAXPROCS]; /*! \brief Queue of Event messages to send to mISDN */ msg_queue_t downqueue; @@ -115,13 +121,17 @@ struct misdn_stack { /*! \brief Logical Layer 1 port associated with this stack */ int port; - /*! \brief B Channel record pool array */ - struct misdn_bchannel bc[MAX_BCHANS + 1]; - - struct misdn_bchannel* bc_list; /* Not used */ - - /*! \brief Array of B channels in use (a[0] = B1). TRUE if B channel in use */ - int channels[MAX_BCHANS + 1]; + /*! + * \brief B Channel record pool array + * (Must be dimensioned the same as struct misdn_stack.channels[]) + */ + struct misdn_bchannel bc[MAX_BCHANS + 1 + MISDN_MAX_REGISTER_LINKS]; + + /*! + * \brief Array of B channels in use (a[0] = B1). TRUE if B channel in use. + * (Must be dimensioned the same as struct misdn_stack.bc[]) + */ + char channels[MAX_BCHANS + 1 + MISDN_MAX_REGISTER_LINKS]; /*! \brief List of holded channels */ struct misdn_bchannel *holding; diff --git a/channels/misdn/isdn_msg_parser.c b/channels/misdn/isdn_msg_parser.c index 01c084ff7..4201cab2e 100644 --- a/channels/misdn/isdn_msg_parser.c +++ b/channels/misdn/isdn_msg_parser.c @@ -61,6 +61,80 @@ static void build_display_str(char *display, size_t display_length, int display_ } } +/*! + * \internal + * \brief Encode the Facility IE and put it into the message structure. + * + * \param ntmode Where the encoded facility was put when in NT mode. + * \param msg General message structure + * \param fac Data to encode into the facility ie. + * \param nt TRUE if in NT mode. + * + * \return Nothing + */ +static void enc_ie_facility(unsigned char **ntmode, msg_t *msg, struct FacParm *fac, int nt) +{ + int len; + Q931_info_t *qi; + unsigned char *p; + unsigned char buf[256]; + + len = encodeFac(buf, fac); + if (len <= 0) { + /* + * mISDN does not know how to build the requested facility structure + * Clear facility information + */ + fac->Function = Fac_None; + return; + } + + p = msg_put(msg, len); + if (nt) { + *ntmode = p + 1; + } else { + qi = (Q931_info_t *) (msg->data + mISDN_HEADER_LEN); + qi->QI_ELEMENT(facility) = p - (unsigned char *) qi - sizeof(Q931_info_t); + } + + memcpy(p, buf, len); + + /* Clear facility information */ + fac->Function = Fac_None; +} + +/*! + * \internal + * \brief Decode the Facility IE. + * + * \param p Encoded facility ie data to decode. (NT mode) + * \param qi Encoded facility ie data to decode. (TE mode) + * \param fac Where to put the decoded facility ie data if it is available. + * \param nt TRUE if in NT mode. + * \param bc Associated B channel + * + * \return Nothing + */ +static void dec_ie_facility(unsigned char *p, Q931_info_t *qi, struct FacParm *fac, int nt, struct misdn_bchannel *bc) +{ + fac->Function = Fac_None; + + if (!nt) { + p = NULL; + if (qi->QI_ELEMENT(facility)) { + p = (unsigned char *) qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(facility) + 1; + } + } + if (!p) { + return; + } + + if (decodeFac(p, fac)) { + cb_log(3, bc->port, "Decoding facility ie failed! Unrecognized facility message?\n"); + } +} + + static void set_channel(struct misdn_bchannel *bc, int channel) { @@ -93,7 +167,7 @@ static void set_channel(struct misdn_bchannel *bc, int channel) static void parse_proceeding (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - CALL_PROCEEDING_t *proceeding=(CALL_PROCEEDING_t*)((unsigned long)msg->data+ HEADER_LEN); + CALL_PROCEEDING_t *proceeding = (CALL_PROCEEDING_t *) (msg->data + HEADER_LEN); //struct misdn_stack *stack=get_stack_by_bc(bc); { @@ -106,6 +180,9 @@ static void parse_proceeding (struct isdn_msg msgs[], msg_t *msg, struct misdn_b dec_ie_progress(proceeding->PROGRESS, (Q931_info_t *)proceeding, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); + dec_ie_facility(proceeding->FACILITY, (Q931_info_t *) proceeding, &bc->fac_in, nt, bc); + + /* dec_ie_redir_dn */ #ifdef DEBUG printf("Parsing PROCEEDING Msg\n"); @@ -124,6 +201,11 @@ static msg_t *build_proceeding (struct isdn_msg msgs[], struct misdn_bchannel *b if (nt) enc_ie_progress(&proceeding->PROGRESS, msg, 0, nt?1:5, 8, nt,bc); + if (bc->fac_out.Function != Fac_None) { + enc_ie_facility(&proceeding->FACILITY, msg, &bc->fac_out, nt); + } + + /* enc_ie_redir_dn */ #ifdef DEBUG printf("Building PROCEEDING Msg\n"); @@ -134,9 +216,13 @@ static msg_t *build_proceeding (struct isdn_msg msgs[], struct misdn_bchannel *b static void parse_alerting (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - ALERTING_t *alerting=(ALERTING_t*)((unsigned long)(msg->data+HEADER_LEN)); + ALERTING_t *alerting = (ALERTING_t *) (msg->data + HEADER_LEN); //Q931_info_t *qi=(Q931_info_t*)(msg->data+HEADER_LEN); + dec_ie_facility(alerting->FACILITY, (Q931_info_t *) alerting, &bc->fac_in, nt, bc); + + /* dec_ie_redir_dn */ + dec_ie_progress(alerting->PROGRESS, (Q931_info_t *)alerting, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); #ifdef DEBUG @@ -158,6 +244,13 @@ static msg_t *build_alerting (struct isdn_msg msgs[], struct misdn_bchannel *bc, if (nt) enc_ie_progress(&alerting->PROGRESS, msg, 0, nt?1:5, 8, nt,bc); + + if (bc->fac_out.Function != Fac_None) { + enc_ie_facility(&alerting->FACILITY, msg, &bc->fac_out, nt); + } + + /* enc_ie_redir_dn */ + #ifdef DEBUG printf("Building ALERTING Msg\n"); #endif @@ -168,11 +261,13 @@ static msg_t *build_alerting (struct isdn_msg msgs[], struct misdn_bchannel *bc, static void parse_progress (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - PROGRESS_t *progress=(PROGRESS_t*)((unsigned long)(msg->data+HEADER_LEN)); + PROGRESS_t *progress = (PROGRESS_t *) (msg->data + HEADER_LEN); //Q931_info_t *qi=(Q931_info_t*)(msg->data+HEADER_LEN); dec_ie_progress(progress->PROGRESS, (Q931_info_t *)progress, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); + dec_ie_facility(progress->FACILITY, (Q931_info_t *) progress, &bc->fac_in, nt, bc); + #ifdef DEBUG printf("Parsing PROGRESS Msg\n"); #endif @@ -186,17 +281,92 @@ static msg_t *build_progress (struct isdn_msg msgs[], struct misdn_bchannel *bc, progress=(PROGRESS_t*)((msg->data+HEADER_LEN)); + if (bc->fac_out.Function != Fac_None) { + enc_ie_facility(&progress->FACILITY, msg, &bc->fac_out, nt); + } + #ifdef DEBUG printf("Building PROGRESS Msg\n"); #endif return msg; } +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Extract the SETUP message's BC, HLC, and LLC encoded ie contents. + * + * \param setup Indexed setup message contents + * \param nt TRUE if in NT mode. + * \param bc Associated B channel + * + * \return Nothing + */ +static void extract_setup_Bc_Hlc_Llc(SETUP_t *setup, int nt, struct misdn_bchannel *bc) +{ + __u8 *p; + Q931_info_t *qi; + + qi = (Q931_info_t *) setup; + + /* Extract Bearer Capability */ + if (nt) { + p = (__u8 *) setup->BEARER; + } else { + if (qi->QI_ELEMENT(bearer_capability)) { + p = (__u8 *) qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(bearer_capability) + 1; + } else { + p = NULL; + } + } + if (!p || *p == 0 || sizeof(bc->setup_bc_hlc_llc.Bc.Contents) < *p) { + bc->setup_bc_hlc_llc.Bc.Length = 0; + } else { + bc->setup_bc_hlc_llc.Bc.Length = *p; + memcpy(bc->setup_bc_hlc_llc.Bc.Contents, p + 1, *p); + } + + /* Extract Low Layer Compatibility */ + if (nt) { + p = (__u8 *) setup->LLC; + } else { + if (qi->QI_ELEMENT(llc)) { + p = (__u8 *) qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(llc) + 1; + } else { + p = NULL; + } + } + if (!p || *p == 0 || sizeof(bc->setup_bc_hlc_llc.Llc.Contents) < *p) { + bc->setup_bc_hlc_llc.Llc.Length = 0; + } else { + bc->setup_bc_hlc_llc.Llc.Length = *p; + memcpy(bc->setup_bc_hlc_llc.Llc.Contents, p + 1, *p); + } + + /* Extract High Layer Compatibility */ + if (nt) { + p = (__u8 *) setup->HLC; + } else { + if (qi->QI_ELEMENT(hlc)) { + p = (__u8 *) qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(hlc) + 1; + } else { + p = NULL; + } + } + if (!p || *p == 0 || sizeof(bc->setup_bc_hlc_llc.Hlc.Contents) < *p) { + bc->setup_bc_hlc_llc.Hlc.Length = 0; + } else { + bc->setup_bc_hlc_llc.Hlc.Length = *p; + memcpy(bc->setup_bc_hlc_llc.Hlc.Contents, p + 1, *p); + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + static void parse_setup (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - SETUP_t *setup= (SETUP_t*)((unsigned long)msg->data+HEADER_LEN); - Q931_info_t *qi=(Q931_info_t*)((unsigned long)msg->data+HEADER_LEN); + SETUP_t *setup = (SETUP_t *) (msg->data + HEADER_LEN); + Q931_info_t *qi = (Q931_info_t *) (msg->data + HEADER_LEN); int type; int plan; int present; @@ -207,7 +377,7 @@ static void parse_setup (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchann printf("Parsing SETUP Msg\n"); #endif - dec_ie_calling_pn(setup->CALLING_PN, qi, &type, &plan, &present, &screen, bc->caller.number, sizeof(bc->caller.number) - 1, nt, bc); + dec_ie_calling_pn(setup->CALLING_PN, qi, &type, &plan, &present, &screen, bc->caller.number, sizeof(bc->caller.number), nt, bc); bc->caller.number_type = type; bc->caller.number_plan = plan; switch (present) { @@ -228,15 +398,17 @@ static void parse_setup (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchann bc->caller.screening = 0; /* Unscreened */ } - dec_ie_called_pn(setup->CALLED_PN, (Q931_info_t *) setup, &type, &plan, bc->dialed.number, sizeof(bc->dialed.number) - 1, nt, bc); + dec_ie_facility(setup->FACILITY, (Q931_info_t *) setup, &bc->fac_in, nt, bc); + + dec_ie_called_pn(setup->CALLED_PN, (Q931_info_t *) setup, &type, &plan, bc->dialed.number, sizeof(bc->dialed.number), nt, bc); bc->dialed.number_type = type; bc->dialed.number_plan = plan; - dec_ie_keypad(setup->KEYPAD, (Q931_info_t *) setup, bc->keypad, sizeof(bc->keypad) - 1, nt, bc); + dec_ie_keypad(setup->KEYPAD, (Q931_info_t *) setup, bc->keypad, sizeof(bc->keypad), nt, bc); dec_ie_complete(setup->COMPLETE, (Q931_info_t *) setup, &bc->sending_complete, nt, bc); - dec_ie_redir_nr(setup->REDIR_NR, (Q931_info_t *) setup, &type, &plan, &present, &screen, &reason, bc->redirecting.from.number, sizeof(bc->redirecting.from.number) - 1, nt, bc); + dec_ie_redir_nr(setup->REDIR_NR, (Q931_info_t *) setup, &type, &plan, &present, &screen, &reason, bc->redirecting.from.number, sizeof(bc->redirecting.from.number), nt, bc); bc->redirecting.from.number_type = type; bc->redirecting.from.number_plan = plan; switch (present) { @@ -317,6 +489,9 @@ static void parse_setup (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchann dec_ie_progress(setup->PROGRESS, (Q931_info_t *)setup, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); +#if defined(AST_MISDN_ENHANCEMENTS) + extract_setup_Bc_Hlc_Llc(setup, nt, bc); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ } #define ANY_CHANNEL 0xff /* IE attribute for 'any channel' */ @@ -333,6 +508,9 @@ static msg_t *build_setup (struct isdn_msg msgs[], struct misdn_bchannel *bc, in else enc_ie_channel_id(&setup->CHANNEL_ID, msg, 1, bc->channel, nt,bc); + if (bc->fac_out.Function != Fac_None) { + enc_ie_facility(&setup->FACILITY, msg, &bc->fac_out, nt); + } enc_ie_calling_pn(&setup->CALLING_PN, msg, bc->caller.number_type, bc->caller.number_plan, bc->caller.presentation, bc->caller.screening, bc->caller.number, nt, bc); @@ -342,9 +520,17 @@ static msg_t *build_setup (struct isdn_msg msgs[], struct misdn_bchannel *bc, in } if (bc->redirecting.from.number[0]) { +#if 1 + /* ETSI and Q.952 do not define the screening field */ + enc_ie_redir_nr(&setup->REDIR_NR, msg, bc->redirecting.from.number_type, bc->redirecting.from.number_plan, + bc->redirecting.from.presentation, 0, bc->redirecting.reason, + bc->redirecting.from.number, nt, bc); +#else + /* Q.931 defines the screening field */ enc_ie_redir_nr(&setup->REDIR_NR, msg, bc->redirecting.from.number_type, bc->redirecting.from.number_plan, bc->redirecting.from.presentation, bc->redirecting.from.screening, bc->redirecting.reason, bc->redirecting.from.number, nt, bc); +#endif } if (bc->keypad[0]) { @@ -409,6 +595,10 @@ static msg_t *build_setup (struct isdn_msg msgs[], struct misdn_bchannel *bc, in cb_log(1,bc->port,"ENCODING USERUESRINFO:%s\n",bc->uu); } +#if defined(AST_MISDN_ENHANCEMENTS) + extract_setup_Bc_Hlc_Llc(setup, nt, bc); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + #ifdef DEBUG printf("Building SETUP Msg\n"); #endif @@ -418,7 +608,7 @@ static msg_t *build_setup (struct isdn_msg msgs[], struct misdn_bchannel *bc, in static void parse_connect (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - CONNECT_t *connect=(CONNECT_t*)((unsigned long)(msg->data+HEADER_LEN)); + CONNECT_t *connect = (CONNECT_t *) (msg->data + HEADER_LEN); int type; int plan; int pres; @@ -429,7 +619,7 @@ static void parse_connect (struct isdn_msg msgs[], msg_t *msg, struct misdn_bcha dec_ie_progress(connect->PROGRESS, (Q931_info_t *)connect, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); dec_ie_connected_pn(connect->CONNECT_PN, (Q931_info_t *) connect, &type, &plan, - &pres, &screen, bc->connected.number, sizeof(bc->connected.number) - 1, nt, bc); + &pres, &screen, bc->connected.number, sizeof(bc->connected.number), nt, bc); bc->connected.number_type = type; bc->connected.number_plan = plan; switch (pres) { @@ -450,6 +640,8 @@ static void parse_connect (struct isdn_msg msgs[], msg_t *msg, struct misdn_bcha bc->connected.screening = 0; /* Unscreened */ } + dec_ie_facility(connect->FACILITY, (Q931_info_t *) connect, &bc->fac_in, nt, bc); + /* cb_log(1,bc->port,"CONNETED PN: %s cpn_dialplan:%d\n", connected_pn, type); */ @@ -488,6 +680,10 @@ static msg_t *build_connect (struct isdn_msg msgs[], struct misdn_bchannel *bc, } } + if (bc->fac_out.Function != Fac_None) { + enc_ie_facility(&connect->FACILITY, msg, &bc->fac_out, nt); + } + #ifdef DEBUG printf("Building CONNECT Msg\n"); #endif @@ -497,7 +693,7 @@ static msg_t *build_connect (struct isdn_msg msgs[], struct misdn_bchannel *bc, static void parse_setup_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - SETUP_ACKNOWLEDGE_t *setup_acknowledge=(SETUP_ACKNOWLEDGE_t*)((unsigned long)(msg->data+HEADER_LEN)); + SETUP_ACKNOWLEDGE_t *setup_acknowledge = (SETUP_ACKNOWLEDGE_t *) (msg->data + HEADER_LEN); { int exclusive, channel; @@ -508,6 +704,9 @@ static void parse_setup_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct } dec_ie_progress(setup_acknowledge->PROGRESS, (Q931_info_t *)setup_acknowledge, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); + + dec_ie_facility(setup_acknowledge->FACILITY, (Q931_info_t *) setup_acknowledge, &bc->fac_in, nt, bc); + #ifdef DEBUG printf("Parsing SETUP_ACKNOWLEDGE Msg\n"); #endif @@ -528,6 +727,10 @@ static msg_t *build_setup_acknowledge (struct isdn_msg msgs[], struct misdn_bcha if (nt) enc_ie_progress(&setup_acknowledge->PROGRESS, msg, 0, nt?1:5, 8, nt,bc); + if (bc->fac_out.Function != Fac_None) { + enc_ie_facility(&setup_acknowledge->FACILITY, msg, &bc->fac_out, nt); + } + #ifdef DEBUG printf("Building SETUP_ACKNOWLEDGE Msg\n"); #endif @@ -862,12 +1065,14 @@ static msg_t *build_retrieve_reject (struct isdn_msg msgs[], struct misdn_bchann static void parse_disconnect (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - DISCONNECT_t *disconnect=(DISCONNECT_t*)((unsigned long)(msg->data+HEADER_LEN)); + DISCONNECT_t *disconnect = (DISCONNECT_t *) (msg->data + HEADER_LEN); int location; int cause; dec_ie_cause(disconnect->CAUSE, (Q931_info_t *)(disconnect), &location, &cause, nt,bc); if (cause>0) bc->cause=cause; + dec_ie_facility(disconnect->FACILITY, (Q931_info_t *) disconnect, &bc->fac_in, nt, bc); + dec_ie_progress(disconnect->PROGRESS, (Q931_info_t *)disconnect, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); #ifdef DEBUG printf("Parsing DISCONNECT Msg\n"); @@ -885,7 +1090,13 @@ static msg_t *build_disconnect (struct isdn_msg msgs[], struct misdn_bchannel *b disconnect=(DISCONNECT_t*)((msg->data+HEADER_LEN)); enc_ie_cause(&disconnect->CAUSE, msg, (nt)?1:0, bc->out_cause,nt,bc); - if (nt) enc_ie_progress(&disconnect->PROGRESS, msg, 0, nt?1:5, 8 ,nt,bc); + if (nt) { + enc_ie_progress(&disconnect->PROGRESS, msg, 0, nt ? 1 : 5, 8, nt, bc); + } + + if (bc->fac_out.Function != Fac_None) { + enc_ie_facility(&disconnect->FACILITY, msg, &bc->fac_out, nt); + } if (bc->uulen) { int protocol=4; @@ -902,7 +1113,7 @@ static msg_t *build_disconnect (struct isdn_msg msgs[], struct misdn_bchannel *b static void parse_restart (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - RESTART_t *restart=(RESTART_t*)((unsigned long)(msg->data+HEADER_LEN)); + RESTART_t *restart = (RESTART_t *) (msg->data + HEADER_LEN); struct misdn_stack *stack=get_stack_by_bc(bc); @@ -944,12 +1155,15 @@ static msg_t *build_restart (struct isdn_msg msgs[], struct misdn_bchannel *bc, static void parse_release (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - RELEASE_t *release=(RELEASE_t*)((unsigned long)(msg->data+HEADER_LEN)); + RELEASE_t *release = (RELEASE_t *) (msg->data + HEADER_LEN); int location; int cause; dec_ie_cause(release->CAUSE, (Q931_info_t *)(release), &location, &cause, nt,bc); if (cause>0) bc->cause=cause; + + dec_ie_facility(release->FACILITY, (Q931_info_t *) release, &bc->fac_in, nt, bc); + #ifdef DEBUG printf("Parsing RELEASE Msg\n"); #endif @@ -968,6 +1182,10 @@ static msg_t *build_release (struct isdn_msg msgs[], struct misdn_bchannel *bc, if (bc->out_cause>= 0) enc_ie_cause(&release->CAUSE, msg, nt?1:0, bc->out_cause, nt,bc); + if (bc->fac_out.Function != Fac_None) { + enc_ie_facility(&release->FACILITY, msg, &bc->fac_out, nt); + } + if (bc->uulen) { int protocol=4; enc_ie_useruser(&release->USER_USER, msg, protocol, bc->uu, bc->uulen, nt,bc); @@ -983,7 +1201,7 @@ static msg_t *build_release (struct isdn_msg msgs[], struct misdn_bchannel *bc, static void parse_release_complete (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - RELEASE_COMPLETE_t *release_complete=(RELEASE_COMPLETE_t*)((unsigned long)(msg->data+HEADER_LEN)); + RELEASE_COMPLETE_t *release_complete = (RELEASE_COMPLETE_t *) (msg->data + HEADER_LEN); int location; int cause; iframe_t *frm = (iframe_t*) msg->data; @@ -1009,6 +1227,8 @@ static void parse_release_complete (struct isdn_msg msgs[], msg_t *msg, struct m dec_ie_cause(release_complete->CAUSE, (Q931_info_t *)(release_complete), &location, &cause, nt,bc); if (cause>0) bc->cause=cause; + dec_ie_facility(release_complete->FACILITY, (Q931_info_t *) release_complete, &bc->fac_in, nt, bc); + #ifdef DEBUG printf("Parsing RELEASE_COMPLETE Msg\n"); #endif @@ -1024,6 +1244,10 @@ static msg_t *build_release_complete (struct isdn_msg msgs[], struct misdn_bchan enc_ie_cause(&release_complete->CAUSE, msg, nt?1:0, bc->out_cause, nt,bc); + if (bc->fac_out.Function != Fac_None) { + enc_ie_facility(&release_complete->FACILITY, msg, &bc->fac_out, nt); + } + if (bc->uulen) { int protocol=4; enc_ie_useruser(&release_complete->USER_USER, msg, protocol, bc->uu, bc->uulen, nt,bc); @@ -1042,12 +1266,13 @@ static void parse_facility (struct isdn_msg msgs[], msg_t *msg, struct misdn_bch FACILITY_t *facility = (FACILITY_t*)(msg->data+HEADER_LEN); Q931_info_t *qi = (Q931_info_t*)(msg->data+HEADER_LEN); unsigned char *p = NULL; - int err; #ifdef DEBUG printf("Parsing FACILITY Msg\n"); #endif + bc->fac_in.Function = Fac_None; + if (!bc->nt) { if (qi->QI_ELEMENT(facility)) p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(facility) + 1; @@ -1057,9 +1282,8 @@ static void parse_facility (struct isdn_msg msgs[], msg_t *msg, struct misdn_bch if (!p) return; - err = decodeFac(p, &(bc->fac_in)); - if (err) { - cb_log(5, bc->port, "Decoding FACILITY failed! (%d)\n", err); + if (decodeFac(p, &bc->fac_in)) { + cb_log(3, bc->port, "Decoding facility ie failed! Unrecognized facility message?\n"); } } @@ -1079,7 +1303,11 @@ static msg_t *build_facility (struct isdn_msg msgs[], struct misdn_bchannel *bc, len = encodeFac(fac_tmp, &(bc->fac_out)); if (len <= 0) { - /* mISDN does not know how to build the requested facility structure */ + /* + * mISDN does not know how to build the requested facility structure + * Clear facility information + */ + bc->fac_out.Function = Fac_None; return NULL; } @@ -1097,6 +1325,9 @@ static msg_t *build_facility (struct isdn_msg msgs[], struct misdn_bchannel *bc, memcpy(ie_fac, fac_tmp, len); + /* Clear facility information */ + bc->fac_out.Function = Fac_None; + if (*bc->display) { #ifdef DEBUG printf("Sending %s as Display\n", bc->display); @@ -1107,11 +1338,106 @@ static msg_t *build_facility (struct isdn_msg msgs[], struct misdn_bchannel *bc, return msg; } +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Parse a received REGISTER message + * + * \param msgs Search table entry that called us. + * \param msg Received message contents + * \param bc Associated B channel + * \param nt TRUE if in NT mode. + * + * \return Nothing + */ +static void parse_register(struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +{ + int HEADER_LEN; + REGISTER_t *reg; + + HEADER_LEN = nt ? mISDNUSER_HEAD_SIZE : mISDN_HEADER_LEN; + reg = (REGISTER_t *) (msg->data + HEADER_LEN); + + /* + * A facility ie is optional. + * The peer may just be establishing a connection to send + * messages later. + */ + dec_ie_facility(reg->FACILITY, (Q931_info_t *) reg, &bc->fac_in, nt, bc); +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Construct a REGISTER message + * + * \param msgs Search table entry that called us. + * \param bc Associated B channel + * \param nt TRUE if in NT mode. + * + * \return Allocated built message + */ +static msg_t *build_register(struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +{ + int HEADER_LEN; + REGISTER_t *reg; + msg_t *msg; + + msg = (msg_t *) create_l3msg(CC_REGISTER | REQUEST, MT_REGISTER, bc ? bc->l3_id : -1, sizeof(REGISTER_t), nt); + HEADER_LEN = nt ? mISDNUSER_HEAD_SIZE : mISDN_HEADER_LEN; + reg = (REGISTER_t *) (msg->data + HEADER_LEN); + + if (bc->fac_out.Function != Fac_None) { + enc_ie_facility(®->FACILITY, msg, &bc->fac_out, nt); + } + + return msg; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + static void parse_notify (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { + int HEADER_LEN = nt ? mISDNUSER_HEAD_SIZE : mISDN_HEADER_LEN; + NOTIFY_t *notify = (NOTIFY_t *) (msg->data + HEADER_LEN); + int description_code; + int type; + int plan; + int present; + char number[sizeof(bc->redirecting.to.number)]; + #ifdef DEBUG printf("Parsing NOTIFY Msg\n"); #endif + + dec_ie_notify(notify->NOTIFY, (Q931_info_t *) notify, &description_code, nt, bc); + if (description_code < 0) { + bc->notify_description_code = mISDN_NOTIFY_CODE_INVALID; + } else { + bc->notify_description_code = description_code; + } + + dec_ie_redir_dn(notify->REDIR_DN, (Q931_info_t *) notify, &type, &plan, &present, number, sizeof(number), nt, bc); + if (0 <= type) { + bc->redirecting.to_changed = 1; + + bc->redirecting.to.number_type = type; + bc->redirecting.to.number_plan = plan; + switch (present) { + default: + case 0: + bc->redirecting.to.presentation = 0; /* presentation allowed */ + break; + case 1: + bc->redirecting.to.presentation = 1; /* presentation restricted */ + break; + case 2: + bc->redirecting.to.presentation = 2; /* Number not available */ + break; + } + bc->redirecting.to.screening = 0; /* Unscreened */ + strcpy(bc->redirecting.to.number, number); + } } static msg_t *build_notify (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) @@ -1120,11 +1446,20 @@ static msg_t *build_notify (struct isdn_msg msgs[], struct misdn_bchannel *bc, i NOTIFY_t *notify; msg_t *msg =(msg_t*)create_l3msg(CC_NOTIFY | REQUEST, MT_NOTIFY, bc?bc->l3_id:-1, sizeof(NOTIFY_t) ,nt); - notify=(NOTIFY_t*)((msg->data+HEADER_LEN)); - #ifdef DEBUG printf("Building NOTIFY Msg\n"); #endif + + notify = (NOTIFY_t *) (msg->data + HEADER_LEN); + + enc_ie_notify(¬ify->NOTIFY, msg, bc->notify_description_code, nt, bc); + + if (bc->redirecting.to_changed) { + bc->redirecting.to_changed = 0; + enc_ie_redir_dn(¬ify->REDIR_DN, msg, bc->redirecting.to.number_type, + bc->redirecting.to.number_plan, bc->redirecting.to.presentation, + bc->redirecting.to.number, nt, bc); + } return msg; } @@ -1152,11 +1487,11 @@ static msg_t *build_status_enquiry (struct isdn_msg msgs[], struct misdn_bchanne static void parse_information (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - INFORMATION_t *information=(INFORMATION_t*)((unsigned long)(msg->data+HEADER_LEN)); + INFORMATION_t *information = (INFORMATION_t *) (msg->data + HEADER_LEN); int type, plan; - dec_ie_called_pn(information->CALLED_PN, (Q931_info_t *) information, &type, &plan, bc->info_dad, sizeof(bc->info_dad) - 1, nt, bc); - dec_ie_keypad(information->KEYPAD, (Q931_info_t *) information, bc->keypad, sizeof(bc->keypad) - 1, nt, bc); + dec_ie_called_pn(information->CALLED_PN, (Q931_info_t *) information, &type, &plan, bc->info_dad, sizeof(bc->info_dad), nt, bc); + dec_ie_keypad(information->KEYPAD, (Q931_info_t *) information, bc->keypad, sizeof(bc->keypad), nt, bc); #ifdef DEBUG printf("Parsing INFORMATION Msg\n"); @@ -1191,7 +1526,7 @@ static msg_t *build_information (struct isdn_msg msgs[], struct misdn_bchannel * static void parse_status (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - STATUS_t *status=(STATUS_t*)((unsigned long)(msg->data+HEADER_LEN)); + STATUS_t *status = (STATUS_t *) (msg->data + HEADER_LEN); int location; int cause; @@ -1253,6 +1588,9 @@ struct isdn_msg msgs_g[] = { { CC_ALERTING, EVENT_ALERTING, parse_alerting, build_alerting, "ALERTING" }, { CC_PROGRESS, EVENT_PROGRESS, parse_progress, build_progress, "PROGRESS" }, { CC_SETUP, EVENT_SETUP, parse_setup, build_setup, "SETUP" }, +#if defined(AST_MISDN_ENHANCEMENTS) + { CC_REGISTER, EVENT_REGISTER, parse_register, build_register, "REGISTER" }, +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ { CC_CONNECT, EVENT_CONNECT, parse_connect, build_connect, "CONNECT" }, { CC_SETUP_ACKNOWLEDGE, EVENT_SETUP_ACKNOWLEDGE, parse_setup_acknowledge, build_setup_acknowledge, "SETUP_ACKNOWLEDGE" }, { CC_CONNECT_ACKNOWLEDGE, EVENT_CONNECT_ACKNOWLEDGE, parse_connect_acknowledge, build_connect_acknowledge, "CONNECT_ACKNOWLEDGE " }, diff --git a/channels/misdn_config.c b/channels/misdn_config.c index 444b939f2..3643d67e2 100644 --- a/channels/misdn_config.c +++ b/channels/misdn_config.c @@ -361,6 +361,8 @@ static const struct misdn_cfg_spec port_spec[] = { "MSN's for TE ports, listen on those numbers on the above ports, and\n" "\tindicate the incoming calls to Asterisk.\n" "\tHere you can give a comma separated list, or simply an '*' for any msn." }, + { "cc_request_retention", MISDN_CFG_CC_REQUEST_RETENTION, MISDN_CTYPE_BOOL, "yes", NONE, + "Enable/Disable call-completion request retention support (ptp)." }, }; static const struct misdn_cfg_spec gen_spec[] = { diff --git a/configs/misdn.conf.sample b/configs/misdn.conf.sample index e813953b3..07cff4de4 100644 --- a/configs/misdn.conf.sample +++ b/configs/misdn.conf.sample @@ -161,7 +161,10 @@ context=misdn language=en ; -; sets the musiconhold class +; This option specifies a default music on hold class to +; use when put on hold if the channel's moh class was not +; explicitly set with Set(CHANNEL(musicclass)=whatever) and +; the peer channel did not suggest a class to use. ; musicclass=default @@ -190,10 +193,10 @@ far_alerting=no ; allowed_bearers=all -; Prefixes for national and international Type-Of-Number. These are +; Incoming number prefixes for the indicated Type-Of-Number. These are ; inserted before any number (caller, dialed, connected, redirecting, ; redirection) received from the ISDN link if that number has the -; correspondng Type-Of-Number. +; corresponding Type-Of-Number. ; See the dialplan options. ; ; default values: @@ -456,6 +459,17 @@ max_incoming=-1 ; max_outgoing=-1 +; +; Enable/disable the call-completion retention option support (ptp only). +; +; Note: To use the CCBS/CCNR supplementary service feature and other +; supplementary services using FACILITY messages requires a +; modified version of mISDN from: +; http://svn.digium.com/svn/thirdparty/mISDN/trunk +; http://svn.digium.com/svn/thirdparty/mISDNuser/trunk +; +cc_request_retention=yes + [intern] ; define your ports, e.g. 1,2 (depends on mISDN-driver loading order) ports=1,2 |