aboutsummaryrefslogtreecommitdiffstats
path: root/channels/sig_pri.c
diff options
context:
space:
mode:
authormmichelson <mmichelson@f38db490-d61c-443f-a65b-d21fe96a405b>2010-04-09 15:31:32 +0000
committermmichelson <mmichelson@f38db490-d61c-443f-a65b-d21fe96a405b>2010-04-09 15:31:32 +0000
commit0eb1e5407a6eacd46d98e134dc81e8b857c103b7 (patch)
tree0b1d16ff83df2f35441f03a082b848262b8a2557 /channels/sig_pri.c
parent6c57cdc6ac82a6a6700ebdb788d690471d8fc49d (diff)
Merge Call completion support into trunk.
From Reviewboard: CCSS stands for Call Completion Supplementary Services. An admittedly out-of-date overview of the architecture can be found in the file doc/CCSS_architecture.pdf in the CCSS branch. Off the top of my head, the big differences between what is implemented and what is in the document are as follows: 1. We did not end up modifying the Hangup application at all. 2. The document states that a single call completion monitor may be used across multiple calls to the same device. This proved to not be such a good idea when implementing protocol-specific monitors, and so we ended up using one monitor per-device per-call. 3. There are some configuration options which were conceived after the document was written. These are documented in the ccss.conf.sample that is on this review request. For some basic understanding of terminology used throughout this code, see the ccss.tex document that is on this review. This implements CCBS and CCNR in several flavors. First up is a "generic" implementation, which can work over any channel technology provided that the channel technology can accurately report device state. Call completion is requested using the dialplan application CallCompletionRequest and can be canceled using CallCompletionCancel. Device state subscriptions are used in order to monitor the state of called parties. Next, there is a SIP-specific implementation of call completion. This method uses the methods outlined in draft-ietf-bliss-call-completion-06 to implement call completion using SIP signaling. There are a few things to note here: * The agent/monitor terminology used throughout Asterisk sometimes is the reverse of what is defined in the referenced draft. * Implementation of the draft required support for SIP PUBLISH. I attempted to write this in a generic-enough fashion such that if someone were to want to write PUBLISH support for other event packages, such as dialog-state or presence, most of the effort would be in writing callbacks specific to the event package. * A subportion of supporting PUBLISH reception was that we had to implement a PIDF parser. The PIDF support added is a bit minimal. I first wrote a validation routine to ensure that the PIDF document is formatted properly. The rest of the PIDF reading is done in-line in the call-completion-specific PUBLISH-handling code. In other words, while there is PIDF support here, it is not in any state where it could easily be applied to other event packages as is. Finally, there are a variety of ISDN-related call completion protocols supported. These were written by Richard Mudgett, and as such I can't really say much about their implementation. There are notes in the CHANGES file that indicate the ISDN protocols over which call completion is supported. Review: https://reviewboard.asterisk.org/r/523 git-svn-id: http://svn.digium.com/svn/asterisk/trunk@256528 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'channels/sig_pri.c')
-rw-r--r--channels/sig_pri.c1552
1 files changed, 1512 insertions, 40 deletions
diff --git a/channels/sig_pri.c b/channels/sig_pri.c
index fd3b4111e..5749e89b3 100644
--- a/channels/sig_pri.c
+++ b/channels/sig_pri.c
@@ -55,11 +55,32 @@
/* define this to send PRI user-user information elements */
#undef SUPPORT_USERUSER
-#if 0
-#define DEFAULT_PRI_DEBUG (PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_STATE)
-#else
-#define DEFAULT_PRI_DEBUG 0
-#endif
+#if defined(HAVE_PRI_CCSS)
+struct sig_pri_cc_agent_prv {
+ /*! Asterisk span D channel control structure. */
+ struct sig_pri_pri *pri;
+ /*! CC id value to use with libpri. -1 if invalid. */
+ long cc_id;
+ /*! TRUE if CC has been requested and we are waiting for the response. */
+ unsigned char cc_request_response_pending;
+};
+
+struct sig_pri_cc_monitor_instance {
+ /*! \brief Asterisk span D channel control structure. */
+ struct sig_pri_pri *pri;
+ /*! CC id value to use with libpri. (-1 if already canceled). */
+ long cc_id;
+ /*! CC core id value. */
+ int core_id;
+ /*! Device name(Channel name less sequence number) */
+ char name[1];
+};
+
+/*! Upper level agent/monitor type name. */
+static const char *sig_pri_cc_type_name;
+/*! Container of sig_pri monitor instances. */
+static struct ao2_container *sig_pri_cc_monitors;
+#endif /* defined(HAVE_PRI_CCSS) */
static int pri_matchdigittimeout = 3000;
@@ -120,6 +141,45 @@ static void sig_pri_set_digital(struct sig_pri_chan *p, int flag)
p->calls->set_digital(p->chan_pvt, flag);
}
+static const char *sig_pri_get_orig_dialstring(struct sig_pri_chan *p)
+{
+ if (p->calls->get_orig_dialstring) {
+ return p->calls->get_orig_dialstring(p->chan_pvt);
+ }
+ ast_log(LOG_ERROR, "get_orig_dialstring callback not defined\n");
+ return "";
+}
+
+#if defined(HAVE_PRI_CCSS)
+static void sig_pri_make_cc_dialstring(struct sig_pri_chan *p, char *buf, size_t buf_size)
+{
+ if (p->calls->make_cc_dialstring) {
+ p->calls->make_cc_dialstring(p->chan_pvt, buf, buf_size);
+ } else {
+ ast_log(LOG_ERROR, "make_cc_dialstring callback not defined\n");
+ buf[0] = '\0';
+ }
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+/*!
+ * \internal
+ * \brief Reevaluate the PRI span device state.
+ * \since 1.8
+ *
+ * \param pri Asterisk D channel control structure.
+ *
+ * \return Nothing
+ *
+ * \note Assumes the pri->lock is already obtained.
+ */
+static void sig_pri_span_devstate_changed(struct sig_pri_pri *pri)
+{
+ if (pri->calls->update_span_devstate) {
+ pri->calls->update_span_devstate(pri);
+ }
+}
+
/*!
* \internal
* \brief Set the caller id information in the parent module.
@@ -733,6 +793,12 @@ static struct ast_channel *sig_pri_new_ast_channel(struct sig_pri_chan *p, int s
pbx_builtin_setvar_helper(c, "TRANSFERCAPABILITY", ast_transfercapability2str(transfercapability));
sig_pri_set_digital(p, 1);
}
+ if (p->pri && !pri_grab(p, p->pri)) {
+ sig_pri_span_devstate_changed(p->pri);
+ pri_rel(p->pri);
+ } else {
+ ast_log(LOG_WARNING, "Failed to grab PRI!\n");
+ }
return c;
}
@@ -1476,6 +1542,615 @@ static void sig_pri_lock_owner(struct sig_pri_pri *pri, int chanpos)
}
}
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief Compare the CC agent private data by libpri cc_id.
+ * \since 1.8
+ *
+ * \param obj pointer to the (user-defined part) of an object.
+ * \param arg callback argument from ao2_callback()
+ * \param flags flags from ao2_callback()
+ *
+ * \return values are a combination of enum _cb_results.
+ */
+static int sig_pri_cc_agent_cmp_cc_id(void *obj, void *arg, int flags)
+{
+ struct ast_cc_agent *agent_1 = obj;
+ struct sig_pri_cc_agent_prv *agent_prv_1 = agent_1->private_data;
+ struct sig_pri_cc_agent_prv *agent_prv_2 = arg;
+
+ return (agent_prv_1 && agent_prv_1->pri == agent_prv_2->pri
+ && agent_prv_1->cc_id == agent_prv_2->cc_id) ? CMP_MATCH | CMP_STOP : 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief Find the CC agent by libpri cc_id.
+ * \since 1.8
+ *
+ * \param pri sig_pri PRI control structure.
+ * \param cc_id CC record ID to find.
+ *
+ * \note
+ * Since agents are refcounted, and this function returns
+ * a reference to the agent, it is imperative that you decrement
+ * the refcount of the agent once you have finished using it.
+ *
+ * \retval agent on success.
+ * \retval NULL not found.
+ */
+static struct ast_cc_agent *sig_pri_find_cc_agent_by_cc_id(struct sig_pri_pri *pri, long cc_id)
+{
+ struct sig_pri_cc_agent_prv finder = {
+ .pri = pri,
+ .cc_id = cc_id,
+ };
+
+ return ast_cc_agent_callback(0, sig_pri_cc_agent_cmp_cc_id, &finder,
+ sig_pri_cc_type_name);
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief Compare the CC monitor instance by libpri cc_id.
+ * \since 1.8
+ *
+ * \param obj pointer to the (user-defined part) of an object.
+ * \param arg callback argument from ao2_callback()
+ * \param flags flags from ao2_callback()
+ *
+ * \return values are a combination of enum _cb_results.
+ */
+static int sig_pri_cc_monitor_cmp_cc_id(void *obj, void *arg, int flags)
+{
+ struct sig_pri_cc_monitor_instance *monitor_1 = obj;
+ struct sig_pri_cc_monitor_instance *monitor_2 = arg;
+
+ return (monitor_1->pri == monitor_2->pri
+ && monitor_1->cc_id == monitor_2->cc_id) ? CMP_MATCH | CMP_STOP : 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief Find the CC monitor instance by libpri cc_id.
+ * \since 1.8
+ *
+ * \param pri sig_pri PRI control structure.
+ * \param cc_id CC record ID to find.
+ *
+ * \note
+ * Since monitor_instances are refcounted, and this function returns
+ * a reference to the instance, it is imperative that you decrement
+ * the refcount of the instance once you have finished using it.
+ *
+ * \retval monitor_instance on success.
+ * \retval NULL not found.
+ */
+static struct sig_pri_cc_monitor_instance *sig_pri_find_cc_monitor_by_cc_id(struct sig_pri_pri *pri, long cc_id)
+{
+ struct sig_pri_cc_monitor_instance finder = {
+ .pri = pri,
+ .cc_id = cc_id,
+ };
+
+ return ao2_callback(sig_pri_cc_monitors, 0, sig_pri_cc_monitor_cmp_cc_id, &finder);
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief Destroy the given monitor instance.
+ * \since 1.8
+ *
+ * \param data Monitor instance to destroy.
+ *
+ * \return Nothing
+ */
+static void sig_pri_cc_monitor_instance_destroy(void *data)
+{
+ struct sig_pri_cc_monitor_instance *monitor_instance = data;
+
+ if (monitor_instance->cc_id != -1) {
+ ast_mutex_lock(&monitor_instance->pri->lock);
+ pri_cc_cancel(monitor_instance->pri->pri, monitor_instance->cc_id);
+ ast_mutex_unlock(&monitor_instance->pri->lock);
+ }
+ monitor_instance->pri->calls->module_unref();
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief Construct a new monitor instance.
+ * \since 1.8
+ *
+ * \param core_id CC core ID.
+ * \param pri sig_pri PRI control structure.
+ * \param cc_id CC record ID.
+ * \param device_name Name of device (Asterisk channel name less sequence number).
+ *
+ * \note
+ * Since monitor_instances are refcounted, and this function returns
+ * a reference to the instance, it is imperative that you decrement
+ * the refcount of the instance once you have finished using it.
+ *
+ * \retval monitor_instance on success.
+ * \retval NULL on error.
+ */
+static struct sig_pri_cc_monitor_instance *sig_pri_cc_monitor_instance_init(int core_id, struct sig_pri_pri *pri, long cc_id, const char *device_name)
+{
+ struct sig_pri_cc_monitor_instance *monitor_instance;
+
+ if (!pri->calls->module_ref || !pri->calls->module_unref) {
+ return NULL;
+ }
+
+ monitor_instance = ao2_alloc(sizeof(*monitor_instance) + strlen(device_name),
+ sig_pri_cc_monitor_instance_destroy);
+ if (!monitor_instance) {
+ return NULL;
+ }
+
+ monitor_instance->cc_id = cc_id;
+ monitor_instance->pri = pri;
+ monitor_instance->core_id = core_id;
+ strcpy(monitor_instance->name, device_name);
+
+ pri->calls->module_ref();
+
+ ao2_link(sig_pri_cc_monitors, monitor_instance);
+ return monitor_instance;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief Announce to the CC core that protocol CC monitor is available for this call.
+ * \since 1.8
+ *
+ * \param pri sig_pri PRI control structure.
+ * \param chanpos Channel position in the span.
+ * \param cc_id CC record ID.
+ * \param service CCBS/CCNR indication.
+ *
+ * \note Assumes the pri->lock is already obtained.
+ * \note Assumes the sig_pri_lock_private(pri->pvts[chanpos]) is already obtained.
+ * \note Assumes the sig_pri_lock_owner(pri, chanpos) is already obtained.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int sig_pri_cc_available(struct sig_pri_pri *pri, int chanpos, long cc_id, enum ast_cc_service_type service)
+{
+ struct sig_pri_chan *pvt;
+ struct ast_cc_config_params *cc_params;
+ struct sig_pri_cc_monitor_instance *monitor;
+ enum ast_cc_monitor_policies monitor_policy;
+ int core_id;
+ int res;
+ char device_name[AST_CHANNEL_NAME];
+ char dialstring[AST_CHANNEL_NAME];
+
+ pvt = pri->pvts[chanpos];
+
+ core_id = ast_cc_get_current_core_id(pvt->owner);
+ if (core_id == -1) {
+ return -1;
+ }
+
+ cc_params = ast_channel_get_cc_config_params(pvt->owner);
+ if (!cc_params) {
+ return -1;
+ }
+
+ res = -1;
+ monitor_policy = ast_get_cc_monitor_policy(cc_params);
+ switch (monitor_policy) {
+ case AST_CC_MONITOR_NEVER:
+ /* CCSS is not enabled. */
+ break;
+ case AST_CC_MONITOR_NATIVE:
+ case AST_CC_MONITOR_ALWAYS:
+ /*
+ * If it is AST_CC_MONITOR_ALWAYS and native fails we will attempt the fallback
+ * later in the call to sig_pri_cc_generic_check().
+ */
+ ast_channel_get_device_name(pvt->owner, device_name, sizeof(device_name));
+ sig_pri_make_cc_dialstring(pvt, dialstring, sizeof(dialstring));
+ monitor = sig_pri_cc_monitor_instance_init(core_id, pri, cc_id, device_name);
+ if (!monitor) {
+ break;
+ }
+ res = ast_queue_cc_frame(pvt->owner, sig_pri_cc_type_name, dialstring, service,
+ monitor);
+ if (res) {
+ monitor->cc_id = -1;
+ ao2_unlink(sig_pri_cc_monitors, monitor);
+ ao2_ref(monitor, -1);
+ }
+ break;
+ case AST_CC_MONITOR_GENERIC:
+ ast_queue_cc_frame(pvt->owner, AST_CC_GENERIC_MONITOR_TYPE,
+ sig_pri_get_orig_dialstring(pvt), service, NULL);
+ /* Say it failed to force caller to cancel native CC. */
+ break;
+ }
+ return res;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+/*!
+ * \internal
+ * \brief Check if generic CC monitor is needed and request it.
+ * \since 1.8
+ *
+ * \param pri sig_pri PRI control structure.
+ * \param chanpos Channel position in the span.
+ * \param service CCBS/CCNR indication.
+ *
+ * \note Assumes the pri->lock is already obtained.
+ * \note Assumes the sig_pri_lock_private(pri->pvts[chanpos]) is already obtained.
+ *
+ * \return Nothing
+ */
+static void sig_pri_cc_generic_check(struct sig_pri_pri *pri, int chanpos, enum ast_cc_service_type service)
+{
+ struct ast_channel *owner;
+ struct ast_cc_config_params *cc_params;
+#if defined(HAVE_PRI_CCSS)
+ struct ast_cc_monitor *monitor;
+ char device_name[AST_CHANNEL_NAME];
+#endif /* defined(HAVE_PRI_CCSS) */
+ enum ast_cc_monitor_policies monitor_policy;
+ int core_id;
+
+ if (!pri->pvts[chanpos]->outgoing) {
+ /* This is not an outgoing call so it cannot be CC monitor. */
+ return;
+ }
+
+ sig_pri_lock_owner(pri, chanpos);
+ owner = pri->pvts[chanpos]->owner;
+ if (!owner) {
+ return;
+ }
+ core_id = ast_cc_get_current_core_id(owner);
+ if (core_id == -1) {
+ /* No CC core setup */
+ goto done;
+ }
+
+ cc_params = ast_channel_get_cc_config_params(owner);
+ if (!cc_params) {
+ /* Could not get CC config parameters. */
+ goto done;
+ }
+
+#if defined(HAVE_PRI_CCSS)
+ ast_channel_get_device_name(owner, device_name, sizeof(device_name));
+ monitor = ast_cc_get_monitor_by_recall_core_id(core_id, device_name);
+ if (monitor) {
+ /* CC monitor is already present so no need for generic CC. */
+ ao2_ref(monitor, -1);
+ goto done;
+ }
+#endif /* defined(HAVE_PRI_CCSS) */
+
+ monitor_policy = ast_get_cc_monitor_policy(cc_params);
+ switch (monitor_policy) {
+ case AST_CC_MONITOR_NEVER:
+ /* CCSS is not enabled. */
+ break;
+ case AST_CC_MONITOR_NATIVE:
+ if (pri->sig == SIG_BRI_PTMP && pri->nodetype == PRI_NETWORK) {
+ /* Request generic CC monitor. */
+ ast_queue_cc_frame(owner, AST_CC_GENERIC_MONITOR_TYPE,
+ sig_pri_get_orig_dialstring(pri->pvts[chanpos]), service, NULL);
+ }
+ break;
+ case AST_CC_MONITOR_ALWAYS:
+ if (pri->sig == SIG_BRI_PTMP && pri->nodetype != PRI_NETWORK) {
+ /*
+ * Cannot monitor PTMP TE side since this is not defined.
+ * We are playing the roll of a phone in this case and
+ * a phone cannot monitor a party over the network without
+ * protocol help.
+ */
+ break;
+ }
+ /*
+ * We are either falling back or this is a PTMP NT span.
+ * Request generic CC monitor.
+ */
+ ast_queue_cc_frame(owner, AST_CC_GENERIC_MONITOR_TYPE,
+ sig_pri_get_orig_dialstring(pri->pvts[chanpos]), service, NULL);
+ break;
+ case AST_CC_MONITOR_GENERIC:
+ if (pri->sig == SIG_BRI_PTMP && pri->nodetype == PRI_NETWORK) {
+ /* Request generic CC monitor. */
+ ast_queue_cc_frame(owner, AST_CC_GENERIC_MONITOR_TYPE,
+ sig_pri_get_orig_dialstring(pri->pvts[chanpos]), service, NULL);
+ }
+ break;
+ }
+
+done:
+ ast_channel_unlock(owner);
+}
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief The CC link canceled the CC instance.
+ * \since 1.8
+ *
+ * \param pri sig_pri PRI control structure.
+ * \param cc_id CC record ID.
+ * \param is_agent TRUE if the cc_id is for an agent.
+ *
+ * \return Nothing
+ */
+static void sig_pri_cc_link_canceled(struct sig_pri_pri *pri, long cc_id, int is_agent)
+{
+ if (is_agent) {
+ struct ast_cc_agent *agent;
+
+ agent = sig_pri_find_cc_agent_by_cc_id(pri, cc_id);
+ if (!agent) {
+ return;
+ }
+ ast_cc_failed(agent->core_id, "%s agent got canceled by link",
+ sig_pri_cc_type_name);
+ ao2_ref(agent, -1);
+ } else {
+ struct sig_pri_cc_monitor_instance *monitor;
+
+ monitor = sig_pri_find_cc_monitor_by_cc_id(pri, cc_id);
+ if (!monitor) {
+ return;
+ }
+ monitor->cc_id = -1;
+ ast_cc_monitor_failed(monitor->core_id, monitor->name,
+ "%s monitor got canceled by link", sig_pri_cc_type_name);
+ ao2_ref(monitor, -1);
+ }
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+/*!
+ * \internal
+ * \brief TRUE if PRI event came in on a CIS call.
+ * \since 1.8
+ *
+ * \param channel PRI encoded span/channel
+ *
+ * \retval non-zero if CIS call.
+ */
+static int sig_pri_is_cis_call(int channel)
+{
+ return channel != -1 && (channel & PRI_CIS_CALL);
+}
+
+/*!
+ * \internal
+ * \brief Handle the CIS associated PRI subcommand events.
+ * \since 1.8
+ *
+ * \param pri sig_pri PRI control structure.
+ * \param event_id PRI event id
+ * \param subcmds Subcommands to process if any. (Could be NULL).
+ * \param call_rsp libpri opaque call structure to send any responses toward.
+ * Could be NULL either because it is not available or the call is for the
+ * dummy call reference. However, this should not be NULL in the cases that
+ * need to use the pointer to send a response message back.
+ *
+ * \note Assumes the pri->lock is already obtained.
+ *
+ * \return Nothing
+ */
+static void sig_pri_handle_cis_subcmds(struct sig_pri_pri *pri, int event_id,
+ const struct pri_subcommands *subcmds, q931_call *call_rsp)
+{
+ int index;
+#if defined(HAVE_PRI_CCSS)
+ struct ast_cc_agent *agent;
+ struct sig_pri_cc_agent_prv *agent_prv;
+ struct sig_pri_cc_monitor_instance *monitor;
+#endif /* defined(HAVE_PRI_CCSS) */
+
+ if (!subcmds) {
+ return;
+ }
+ for (index = 0; index < subcmds->counter_subcmd; ++index) {
+ const struct pri_subcommand *subcmd = &subcmds->subcmd[index];
+
+ switch (subcmd->cmd) {
+#if defined(STATUS_REQUEST_PLACE_HOLDER)
+ case PRI_SUBCMD_STATUS_REQ:
+ case PRI_SUBCMD_STATUS_REQ_RSP:
+ /* Ignore for now. */
+ break;
+#endif /* defined(STATUS_REQUEST_PLACE_HOLDER) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_REQ:
+ agent = sig_pri_find_cc_agent_by_cc_id(pri, subcmd->u.cc_request.cc_id);
+ if (!agent) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_request.cc_id);
+ break;
+ }
+ if (!ast_cc_request_is_within_limits()) {
+ if (pri_cc_req_rsp(pri->pri, subcmd->u.cc_request.cc_id,
+ 5/* queue_full */)) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_request.cc_id);
+ }
+ ast_cc_failed(agent->core_id, "%s agent system CC queue full",
+ sig_pri_cc_type_name);
+ ao2_ref(agent, -1);
+ break;
+ }
+ agent_prv = agent->private_data;
+ agent_prv->cc_request_response_pending = 1;
+ if (ast_cc_agent_accept_request(agent->core_id,
+ "%s caller accepted CC offer.", sig_pri_cc_type_name)) {
+ agent_prv->cc_request_response_pending = 0;
+ if (pri_cc_req_rsp(pri->pri, subcmd->u.cc_request.cc_id,
+ 2/* short_term_denial */)) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_request.cc_id);
+ }
+ ast_cc_failed(agent->core_id, "%s agent CC core request accept failed",
+ sig_pri_cc_type_name);
+ }
+ ao2_ref(agent, -1);
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_REQ_RSP:
+ monitor = sig_pri_find_cc_monitor_by_cc_id(pri,
+ subcmd->u.cc_request_rsp.cc_id);
+ if (!monitor) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_request_rsp.cc_id);
+ break;
+ }
+ switch (subcmd->u.cc_request_rsp.status) {
+ case 0:/* success */
+ ast_cc_monitor_request_acked(monitor->core_id,
+ "%s far end accepted CC request", sig_pri_cc_type_name);
+ break;
+ case 1:/* timeout */
+ ast_verb(2, "core_id:%d %s CC request timeout\n", monitor->core_id,
+ sig_pri_cc_type_name);
+ ast_cc_monitor_failed(monitor->core_id, monitor->name,
+ "%s CC request timeout", sig_pri_cc_type_name);
+ break;
+ case 2:/* error */
+ ast_verb(2, "core_id:%d %s CC request error: %s\n", monitor->core_id,
+ sig_pri_cc_type_name,
+ pri_facility_error2str(subcmd->u.cc_request_rsp.fail_code));
+ ast_cc_monitor_failed(monitor->core_id, monitor->name,
+ "%s CC request error", sig_pri_cc_type_name);
+ break;
+ case 3:/* reject */
+ ast_verb(2, "core_id:%d %s CC request reject: %s\n", monitor->core_id,
+ sig_pri_cc_type_name,
+ pri_facility_reject2str(subcmd->u.cc_request_rsp.fail_code));
+ ast_cc_monitor_failed(monitor->core_id, monitor->name,
+ "%s CC request reject", sig_pri_cc_type_name);
+ break;
+ default:
+ ast_verb(2, "core_id:%d %s CC request unknown status %d\n",
+ monitor->core_id, sig_pri_cc_type_name,
+ subcmd->u.cc_request_rsp.status);
+ ast_cc_monitor_failed(monitor->core_id, monitor->name,
+ "%s CC request unknown status", sig_pri_cc_type_name);
+ break;
+ }
+ ao2_ref(monitor, -1);
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_REMOTE_USER_FREE:
+ monitor = sig_pri_find_cc_monitor_by_cc_id(pri,
+ subcmd->u.cc_remote_user_free.cc_id);
+ if (!monitor) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_remote_user_free.cc_id);
+ break;
+ }
+ ast_cc_monitor_callee_available(monitor->core_id,
+ "%s callee has become available", sig_pri_cc_type_name);
+ ao2_ref(monitor, -1);
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_B_FREE:
+ monitor = sig_pri_find_cc_monitor_by_cc_id(pri,
+ subcmd->u.cc_b_free.cc_id);
+ if (!monitor) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_b_free.cc_id);
+ break;
+ }
+ ast_cc_monitor_party_b_free(monitor->core_id);
+ ao2_ref(monitor, -1);
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_STATUS_REQ:
+ monitor = sig_pri_find_cc_monitor_by_cc_id(pri,
+ subcmd->u.cc_status_req.cc_id);
+ if (!monitor) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_status_req.cc_id);
+ break;
+ }
+ ast_cc_monitor_status_request(monitor->core_id);
+ ao2_ref(monitor, -1);
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_STATUS_REQ_RSP:
+ agent = sig_pri_find_cc_agent_by_cc_id(pri, subcmd->u.cc_status_req_rsp.cc_id);
+ if (!agent) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_status_req_rsp.cc_id);
+ break;
+ }
+ ast_cc_agent_status_response(agent->core_id,
+ subcmd->u.cc_status_req_rsp.status ? AST_DEVICE_INUSE
+ : AST_DEVICE_NOT_INUSE);
+ ao2_ref(agent, -1);
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_STATUS:
+ agent = sig_pri_find_cc_agent_by_cc_id(pri, subcmd->u.cc_status.cc_id);
+ if (!agent) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_status.cc_id);
+ break;
+ }
+ if (subcmd->u.cc_status.status) {
+ ast_cc_agent_caller_busy(agent->core_id, "%s agent caller is busy",
+ sig_pri_cc_type_name);
+ } else {
+ ast_cc_agent_caller_available(agent->core_id,
+ "%s agent caller is available", sig_pri_cc_type_name);
+ }
+ ao2_ref(agent, -1);
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_CANCEL:
+ sig_pri_cc_link_canceled(pri, subcmd->u.cc_cancel.cc_id,
+ subcmd->u.cc_cancel.is_agent);
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_STOP_ALERTING:
+ monitor = sig_pri_find_cc_monitor_by_cc_id(pri,
+ subcmd->u.cc_stop_alerting.cc_id);
+ if (!monitor) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_stop_alerting.cc_id);
+ break;
+ }
+ ast_cc_monitor_stop_ringing(monitor->core_id);
+ ao2_ref(monitor, -1);
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+ default:
+ ast_debug(2,
+ "Unknown CIS subcommand(%d) in %s event on span %d.\n",
+ subcmd->cmd, pri_event2str(event_id), pri->span);
+ break;
+ }
+ }
+}
+
/*!
* \internal
* \brief Handle the call associated PRI subcommand events.
@@ -1647,6 +2322,63 @@ static void sig_pri_handle_subcmds(struct sig_pri_pri *pri, int chanpos, int eve
}
break;
#endif /* defined(HAVE_PRI_CALL_REROUTING) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_AVAILABLE:
+ sig_pri_lock_owner(pri, chanpos);
+ owner = pri->pvts[chanpos]->owner;
+ if (owner) {
+ enum ast_cc_service_type service;
+
+ switch (event_id) {
+ case PRI_EVENT_RINGING:
+ service = AST_CC_CCNR;
+ break;
+ case PRI_EVENT_HANGUP_REQ:
+ /* We will assume that the cause was busy/congestion. */
+ service = AST_CC_CCBS;
+ break;
+ default:
+ service = AST_CC_NONE;
+ break;
+ }
+ if (service == AST_CC_NONE
+ || sig_pri_cc_available(pri, chanpos, subcmd->u.cc_available.cc_id,
+ service)) {
+ pri_cc_cancel(pri->pri, subcmd->u.cc_available.cc_id);
+ }
+ ast_channel_unlock(owner);
+ } else {
+ /* No asterisk channel. */
+ pri_cc_cancel(pri->pri, subcmd->u.cc_available.cc_id);
+ }
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_CALL:
+ sig_pri_lock_owner(pri, chanpos);
+ owner = pri->pvts[chanpos]->owner;
+ if (owner) {
+ struct ast_cc_agent *agent;
+
+ agent = sig_pri_find_cc_agent_by_cc_id(pri, subcmd->u.cc_call.cc_id);
+ if (agent) {
+ ast_setup_cc_recall_datastore(owner, agent->core_id);
+ ast_cc_agent_set_interfaces_chanvar(owner);
+ ast_cc_agent_recalling(agent->core_id,
+ "%s caller is attempting recall", sig_pri_cc_type_name);
+ ao2_ref(agent, -1);
+ }
+
+ ast_channel_unlock(owner);
+ }
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
+#if defined(HAVE_PRI_CCSS)
+ case PRI_SUBCMD_CC_CANCEL:
+ sig_pri_cc_link_canceled(pri, subcmd->u.cc_cancel.cc_id,
+ subcmd->u.cc_cancel.is_agent);
+ break;
+#endif /* defined(HAVE_PRI_CCSS) */
default:
ast_debug(2,
"Unknown call subcommand(%d) in %s event on channel %d/%d on span %d.\n",
@@ -1793,6 +2525,7 @@ static int sig_pri_handle_hold(struct sig_pri_pri *pri, pri_event *ev)
f.subclass.integer = AST_CONTROL_HOLD;
ast_queue_frame(owner, &f);
+ sig_pri_span_devstate_changed(pri);
retval = 0;
}
@@ -1866,6 +2599,7 @@ static void sig_pri_handle_retrieve(struct sig_pri_pri *pri, pri_event *ev)
pri_queue_frame(pri->pvts[chanpos], &f, pri);
}
sig_pri_unlock_private(pri->pvts[chanpos]);
+ sig_pri_span_devstate_changed(pri);
pri_retrieve_ack(pri->pri, ev->retrieve.call,
PVT_TO_CHANNEL(pri->pvts[chanpos]));
}
@@ -2094,10 +2828,12 @@ static void *pri_dchannel(void *vpri)
}
pri->resetting = 0;
/* Take the channels from inalarm condition */
- for (i = 0; i < pri->numchans; i++)
+ for (i = 0; i < pri->numchans; i++) {
if (pri->pvts[i]) {
pri->pvts[i]->inalarm = 0;
}
+ }
+ sig_pri_span_devstate_changed(pri);
break;
case PRI_EVENT_DCHAN_DOWN:
pri_find_dchan(pri);
@@ -2128,6 +2864,7 @@ static void *pri_dchannel(void *vpri)
p->inalarm = 1;
}
}
+ sig_pri_span_devstate_changed(pri);
}
break;
case PRI_EVENT_RESTART:
@@ -2180,6 +2917,11 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_KEYPAD_DIGIT:
+ if (sig_pri_is_cis_call(e->digit.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->digit.subcmds,
+ e->digit.call);
+ break;
+ }
chanpos = pri_find_principle(pri, e->digit.channel, e->digit.call);
if (chanpos < 0) {
ast_log(LOG_WARNING, "KEYPAD_DIGITs received on unconfigured channel %d/%d span %d\n",
@@ -2210,6 +2952,11 @@ static void *pri_dchannel(void *vpri)
break;
case PRI_EVENT_INFO_RECEIVED:
+ if (sig_pri_is_cis_call(e->ring.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->ring.subcmds,
+ e->ring.call);
+ break;
+ }
chanpos = pri_find_principle(pri, e->ring.channel, e->ring.call);
if (chanpos < 0) {
ast_log(LOG_WARNING, "INFO received on unconfigured channel %d/%d span %d\n",
@@ -2262,6 +3009,8 @@ static void *pri_dchannel(void *vpri)
snprintf(db_answer, sizeof(db_answer), "%s:%u",
SRVST_TYPE_OOS, *why);
ast_db_put(db_chan_name, SRVST_DBKEY, db_answer);
+ } else {
+ sig_pri_span_devstate_changed(pri);
}
break;
case 2: /* out-of-service */
@@ -2271,6 +3020,7 @@ static void *pri_dchannel(void *vpri)
snprintf(db_answer, sizeof(db_answer), "%s:%u", SRVST_TYPE_OOS,
*why);
ast_db_put(db_chan_name, SRVST_DBKEY, db_answer);
+ sig_pri_span_devstate_changed(pri);
break;
default:
ast_log(LOG_ERROR, "Huh? changestatus is: %d\n", e->service.changestatus);
@@ -2301,7 +3051,12 @@ static void *pri_dchannel(void *vpri)
pri_destroycall(pri->pri, e->ring.call);
break;
}
- if (e->ring.channel == -1)
+ if (sig_pri_is_cis_call(e->ring.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->ring.subcmds,
+ e->ring.call);
+ break;
+ }
+ if (e->ring.channel == -1 || PRI_CHANNEL(e->ring.channel) == 0xFF)
chanpos = pri_find_empty_chan(pri, 1);
else
chanpos = pri_find_principle(pri, e->ring.channel, e->ring.call);
@@ -2644,6 +3399,11 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_RINGING:
+ if (sig_pri_is_cis_call(e->ringing.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->ringing.subcmds,
+ e->ringing.call);
+ break;
+ }
chanpos = pri_find_principle(pri, e->ringing.channel, e->ringing.call);
if (chanpos < 0) {
ast_log(LOG_WARNING, "Ringing requested on unconfigured channel %d/%d span %d\n",
@@ -2658,6 +3418,7 @@ static void *pri_dchannel(void *vpri)
sig_pri_handle_subcmds(pri, chanpos, e->e, e->ringing.channel,
e->ringing.subcmds, e->ringing.call);
+ sig_pri_cc_generic_check(pri, chanpos, AST_CC_CCNR);
sig_pri_set_echocanceller(pri->pvts[chanpos], 1);
pri_queue_control(pri->pvts[chanpos], AST_CONTROL_RINGING, pri);
pri->pvts[chanpos]->alerting = 1;
@@ -2681,7 +3442,11 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_PROGRESS:
- /* Get chan value if e->e is not PRI_EVNT_RINGING */
+ if (sig_pri_is_cis_call(e->proceeding.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->proceeding.subcmds,
+ e->proceeding.call);
+ break;
+ }
chanpos = pri_find_principle(pri, e->proceeding.channel, e->proceeding.call);
if (chanpos > -1) {
sig_pri_lock_private(pri->pvts[chanpos]);
@@ -2731,6 +3496,11 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_PROCEEDING:
+ if (sig_pri_is_cis_call(e->proceeding.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->proceeding.subcmds,
+ e->proceeding.call);
+ break;
+ }
chanpos = pri_find_principle(pri, e->proceeding.channel, e->proceeding.call);
if (chanpos > -1) {
sig_pri_lock_private(pri->pvts[chanpos]);
@@ -2760,6 +3530,17 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_FACILITY:
+ if (!e->facility.call || sig_pri_is_cis_call(e->facility.channel)) {
+ /* Event came in on the dummy channel or a CIS call. */
+#if defined(HAVE_PRI_CALL_REROUTING)
+ sig_pri_handle_cis_subcmds(pri, e->e, e->facility.subcmds,
+ e->facility.subcall);
+#else
+ sig_pri_handle_cis_subcmds(pri, e->e, e->facility.subcmds,
+ e->facility.call);
+#endif /* !defined(HAVE_PRI_CALL_REROUTING) */
+ break;
+ }
chanpos = pri_find_principle(pri, e->facility.channel, e->facility.call);
if (chanpos < 0) {
ast_log(LOG_WARNING, "Facility requested on unconfigured channel %d/%d span %d\n",
@@ -2783,6 +3564,11 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_ANSWER:
+ if (sig_pri_is_cis_call(e->answer.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->answer.subcmds,
+ e->answer.call);
+ break;
+ }
chanpos = pri_find_principle(pri, e->answer.channel, e->answer.call);
if (chanpos < 0) {
ast_log(LOG_WARNING, "Answer on unconfigured channel %d/%d span %d\n",
@@ -2821,6 +3607,12 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_HANGUP:
+ if (sig_pri_is_cis_call(e->hangup.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->hangup.subcmds,
+ e->hangup.call);
+ pri_hangup(pri->pri, e->hangup.call, e->hangup.cause);
+ break;
+ }
chanpos = pri_find_principle(pri, e->hangup.channel, e->hangup.call);
if (chanpos < 0) {
ast_log(LOG_WARNING, "Hangup requested on unconfigured channel %d/%d span %d\n",
@@ -2834,6 +3626,14 @@ static void *pri_dchannel(void *vpri)
if (!pri->pvts[chanpos]->alreadyhungup) {
/* we're calling here dahdi_hangup so once we get there we need to clear p->call after calling pri_hangup */
pri->pvts[chanpos]->alreadyhungup = 1;
+ switch (e->hangup.cause) {
+ case PRI_CAUSE_USER_BUSY:
+ case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION:
+ sig_pri_cc_generic_check(pri, chanpos, AST_CC_CCBS);
+ break;
+ default:
+ break;
+ }
if (pri->pvts[chanpos]->owner) {
/* Queue a BUSY instead of a hangup if our cause is appropriate */
pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause;
@@ -2900,6 +3700,12 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_HANGUP_REQ:
+ if (sig_pri_is_cis_call(e->hangup.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->hangup.subcmds,
+ e->hangup.call);
+ pri_hangup(pri->pri, e->hangup.call, e->hangup.cause);
+ break;
+ }
chanpos = pri_find_principle(pri, e->hangup.channel, e->hangup.call);
if (chanpos < 0) {
ast_log(LOG_WARNING, "Hangup REQ requested on unconfigured channel %d/%d span %d\n",
@@ -2922,6 +3728,14 @@ static void *pri_dchannel(void *vpri)
sig_pri_lock_private(pri->pvts[chanpos]);
}
#endif /* defined(HAVE_PRI_CALL_HOLD) */
+ switch (e->hangup.cause) {
+ case PRI_CAUSE_USER_BUSY:
+ case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION:
+ sig_pri_cc_generic_check(pri, chanpos, AST_CC_CCBS);
+ break;
+ default:
+ break;
+ }
if (pri->pvts[chanpos]->owner) {
pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause;
switch (pri->pvts[chanpos]->owner->_state) {
@@ -2984,6 +3798,11 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_HANGUP_ACK:
+ if (sig_pri_is_cis_call(e->hangup.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->hangup.subcmds,
+ e->hangup.call);
+ break;
+ }
chanpos = pri_find_principle(pri, e->hangup.channel, e->hangup.call);
if (chanpos < 0) {
ast_log(LOG_WARNING, "Hangup ACK requested on unconfigured channel number %d/%d span %d\n",
@@ -3067,6 +3886,11 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_SETUP_ACK:
+ if (sig_pri_is_cis_call(e->setup_ack.channel)) {
+ sig_pri_handle_cis_subcmds(pri, e->e, e->setup_ack.subcmds,
+ e->setup_ack.call);
+ break;
+ }
chanpos = pri_find_principle(pri, e->setup_ack.channel, e->setup_ack.call);
if (chanpos < 0) {
ast_log(LOG_WARNING, "Received SETUP_ACKNOWLEDGE on unconfigured channel %d/%d span %d\n",
@@ -3090,6 +3914,15 @@ static void *pri_dchannel(void *vpri)
}
break;
case PRI_EVENT_NOTIFY:
+ if (sig_pri_is_cis_call(e->notify.channel)) {
+#if defined(HAVE_PRI_CALL_HOLD)
+ sig_pri_handle_cis_subcmds(pri, e->e, e->notify.subcmds,
+ e->notify.call);
+#else
+ sig_pri_handle_cis_subcmds(pri, e->e, e->notify.subcmds, NULL);
+#endif /* !defined(HAVE_PRI_CALL_HOLD) */
+ break;
+ }
#if defined(HAVE_PRI_CALL_HOLD)
chanpos = pri_find_principle(pri, e->notify.channel, e->notify.call);
#else
@@ -3130,6 +3963,7 @@ static void *pri_dchannel(void *vpri)
break;
#if defined(HAVE_PRI_CALL_HOLD)
case PRI_EVENT_HOLD:
+ /* We should not be getting any CIS calls with this message type. */
if (sig_pri_handle_hold(pri, e)) {
pri_hold_rej(pri->pri, e->hold.call,
PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED);
@@ -3150,6 +3984,7 @@ static void *pri_dchannel(void *vpri)
#endif /* defined(HAVE_PRI_CALL_HOLD) */
#if defined(HAVE_PRI_CALL_HOLD)
case PRI_EVENT_RETRIEVE:
+ /* We should not be getting any CIS calls with this message type. */
sig_pri_handle_retrieve(pri, e);
break;
#endif /* defined(HAVE_PRI_CALL_HOLD) */
@@ -3189,7 +4024,7 @@ void sig_pri_init_pri(struct sig_pri_pri *pri)
int sig_pri_hangup(struct sig_pri_chan *p, struct ast_channel *ast)
{
- int res = 0;
+ int res;
#ifdef SUPPORT_USERUSER
const char *useruser = pbx_builtin_getvar_helper(ast, "USERUSERINFO");
#endif
@@ -3213,47 +4048,43 @@ int sig_pri_hangup(struct sig_pri_chan *p, struct ast_channel *ast)
p->exten[0] = '\0';
sig_pri_set_dialing(p, 0);
- if (!p->call) {
- res = 0;
- goto exit;
- }
-
/* Make sure we have a call (or REALLY have a call in the case of a PRI) */
if (!pri_grab(p, p->pri)) {
- if (p->alreadyhungup) {
- ast_log(LOG_DEBUG, "Already hungup... Calling hangup once, and clearing call\n");
+ if (p->call) {
+ if (p->alreadyhungup) {
+ ast_log(LOG_DEBUG, "Already hungup... Calling hangup once, and clearing call\n");
#ifdef SUPPORT_USERUSER
- pri_call_set_useruser(p->call, useruser);
+ pri_call_set_useruser(p->call, useruser);
#endif
- pri_hangup(p->pri->pri, p->call, -1);
- p->call = NULL;
- } else {
- const char *cause = pbx_builtin_getvar_helper(ast,"PRI_CAUSE");
- int icause = ast->hangupcause ? ast->hangupcause : -1;
- ast_log(LOG_DEBUG, "Not yet hungup... Calling hangup once with icause, and clearing call\n");
+ pri_hangup(p->pri->pri, p->call, -1);
+ p->call = NULL;
+ } else {
+ const char *cause = pbx_builtin_getvar_helper(ast,"PRI_CAUSE");
+ int icause = ast->hangupcause ? ast->hangupcause : -1;
+ ast_log(LOG_DEBUG, "Not yet hungup... Calling hangup once with icause, and clearing call\n");
#ifdef SUPPORT_USERUSER
- pri_call_set_useruser(p->call, useruser);
+ pri_call_set_useruser(p->call, useruser);
#endif
- p->alreadyhungup = 1;
- if (cause) {
- if (atoi(cause))
- icause = atoi(cause);
+ p->alreadyhungup = 1;
+ if (cause) {
+ if (atoi(cause))
+ icause = atoi(cause);
+ }
+ pri_hangup(p->pri->pri, p->call, icause);
}
- pri_hangup(p->pri->pri, p->call, icause);
}
- if (res < 0)
- ast_log(LOG_WARNING, "pri_disconnect failed\n");
+ sig_pri_span_devstate_changed(p->pri);
pri_rel(p->pri);
+ res = 0;
} else {
ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->pri->span);
res = -1;
}
-exit:
ast->tech_pvt = NULL;
return res;
}
@@ -3356,6 +4187,7 @@ int sig_pri_call(struct sig_pri_chan *p, struct ast_channel *ast, char *rdest, i
#ifdef SUPPORT_USERUSER
const char *useruser;
#endif
+ int core_id;
int pridialplan;
int dp_strip;
int prilocaldialplan;
@@ -3672,7 +4504,41 @@ int sig_pri_call(struct sig_pri_chan *p, struct ast_channel *ast, char *rdest, i
pri_sr_set_useruser(sr, useruser);
#endif
- if (pri_setup(p->pri->pri, p->call, sr)) {
+#if defined(HAVE_PRI_CCSS)
+ if (ast_cc_is_recall(ast, &core_id, sig_pri_cc_type_name)) {
+ struct ast_cc_monitor *monitor;
+ char device_name[AST_CHANNEL_NAME];
+
+ /* This is a CC recall call. */
+ ast_channel_get_device_name(ast, device_name, sizeof(device_name));
+ monitor = ast_cc_get_monitor_by_recall_core_id(core_id, device_name);
+ if (monitor) {
+ struct sig_pri_cc_monitor_instance *instance;
+
+ instance = monitor->private_data;
+
+ /* If this fails then we have monitor instance ambiguity. */
+ ast_assert(p->pri == instance->pri);
+
+ if (pri_cc_call(p->pri->pri, instance->cc_id, p->call, sr)) {
+ /* The CC recall call failed for some reason. */
+ ast_log(LOG_WARNING, "Unable to setup CC recall call to device %s\n",
+ device_name);
+ ao2_ref(monitor, -1);
+ pri_rel(p->pri);
+ pri_sr_free(sr);
+ return -1;
+ }
+ ao2_ref(monitor, -1);
+ } else {
+ core_id = -1;
+ }
+ } else
+#endif /* defined(HAVE_PRI_CCSS) */
+ {
+ core_id = -1;
+ }
+ if (core_id == -1 && pri_setup(p->pri->pri, p->call, sr)) {
ast_log(LOG_WARNING, "Unable to setup call to %s (using %s)\n",
c + p->stripmsd + dp_strip, dialplan2str(p->pri->dialplan));
pri_rel(p->pri);
@@ -3935,12 +4801,6 @@ int sig_pri_start_pri(struct sig_pri_pri *pri)
#ifdef HAVE_PRI_INBANDDISCONNECT
pri_set_inbanddisconnect(pri->dchans[i], pri->inbanddisconnect);
#endif
-#if defined(HAVE_PRI_CALL_HOLD)
- pri_hold_enable(pri->dchans[i], 1);
-#endif /* defined(HAVE_PRI_CALL_HOLD) */
-#if defined(HAVE_PRI_CALL_REROUTING)
- pri_reroute_enable(pri->dchans[i], 1);
-#endif /* defined(HAVE_PRI_CALL_REROUTING) */
/* Enslave to master if appropriate */
if (i)
pri_enslave(pri->dchans[0], pri->dchans[i]);
@@ -3951,7 +4811,7 @@ int sig_pri_start_pri(struct sig_pri_pri *pri)
ast_log(LOG_ERROR, "Unable to create PRI structure\n");
return -1;
}
- pri_set_debug(pri->dchans[i], DEFAULT_PRI_DEBUG);
+ pri_set_debug(pri->dchans[i], SIG_PRI_DEBUG_DEFAULT);
pri_set_nsf(pri->dchans[i], pri->nsf);
#ifdef PRI_GETSET_TIMERS
for (x = 0; x < PRI_MAX_TIMERS; x++) {
@@ -3960,8 +4820,23 @@ int sig_pri_start_pri(struct sig_pri_pri *pri)
}
#endif
}
+
/* Assume primary is the one we use */
pri->pri = pri->dchans[0];
+
+#if defined(HAVE_PRI_CALL_HOLD)
+ pri_hold_enable(pri->pri, 1);
+#endif /* defined(HAVE_PRI_CALL_HOLD) */
+#if defined(HAVE_PRI_CALL_REROUTING)
+ pri_reroute_enable(pri->pri, 1);
+#endif /* defined(HAVE_PRI_CALL_REROUTING) */
+#if defined(HAVE_PRI_CCSS)
+ pri_cc_enable(pri->pri, 1);
+ pri_cc_recall_mode(pri->pri, pri->cc_ptmp_recall_mode);
+ pri_cc_retain_signaling_req(pri->pri, pri->cc_qsig_signaling_link_req);
+ pri_cc_retain_signaling_rsp(pri->pri, pri->cc_qsig_signaling_link_rsp);
+#endif /* defined(HAVE_PRI_CCSS) */
+
pri->resetpos = -1;
if (ast_pthread_create_background(&pri->master, NULL, pri_dchannel, pri)) {
for (i = 0; i < NUM_DCHANS; i++) {
@@ -4160,4 +5035,601 @@ void sig_pri_fixup(struct ast_channel *oldchan, struct ast_channel *newchan, str
}
}
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief PRI CC agent initialization.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ * \param pvt_chan Original channel the agent will attempt to recall.
+ *
+ * \details
+ * This callback is called when the CC core is initialized. Agents should allocate
+ * any private data necessary for the call and assign it to the private_data
+ * on the agent. Additionally, if any ast_cc_agent_flags are pertinent to the
+ * specific agent type, they should be set in this function as well.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int sig_pri_cc_agent_init(struct ast_cc_agent *agent, struct sig_pri_chan *pvt_chan)
+{
+ struct sig_pri_cc_agent_prv *cc_pvt;
+
+ cc_pvt = ast_calloc(1, sizeof(*cc_pvt));
+ if (!cc_pvt) {
+ return -1;
+ }
+
+ ast_mutex_lock(&pvt_chan->pri->lock);
+ cc_pvt->pri = pvt_chan->pri;
+ cc_pvt->cc_id = pri_cc_available(pvt_chan->pri->pri, pvt_chan->call);
+ ast_mutex_unlock(&pvt_chan->pri->lock);
+ if (cc_pvt->cc_id == -1) {
+ ast_free(cc_pvt);
+ return -1;
+ }
+ agent->private_data = cc_pvt;
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Start the offer timer.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * This is called by the core when the caller hangs up after
+ * a call for which CC may be requested. The agent should
+ * begin the timer as configured.
+ *
+ * The primary reason why this functionality is left to
+ * the specific agent implementations is due to the differing
+ * use of schedulers throughout the code. Some channel drivers
+ * may already have a scheduler context they wish to use, and
+ * amongst those, some may use the ast_sched API while others
+ * may use the ast_sched_thread API, which are incompatible.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int sig_pri_cc_agent_start_offer_timer(struct ast_cc_agent *agent)
+{
+ /* libpri maintains it's own offer timer in the form of T_RETENTION. */
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Stop the offer timer.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * This callback is called by the CC core when the caller
+ * has requested CC.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int sig_pri_cc_agent_stop_offer_timer(struct ast_cc_agent *agent)
+{
+ /* libpri maintains it's own offer timer in the form of T_RETENTION. */
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Acknowledge CC request.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * When the core receives knowledge that a called
+ * party has accepted a CC request, it will call
+ * this callback.
+ *
+ * The duty of this is to accept a CC request from
+ * the caller by acknowledging receipt of that request.
+ *
+ * \return Nothing
+ */
+void sig_pri_cc_agent_req_ack(struct ast_cc_agent *agent)
+{
+ struct sig_pri_cc_agent_prv *cc_pvt;
+ int res;
+
+ cc_pvt = agent->private_data;
+ ast_mutex_lock(&cc_pvt->pri->lock);
+ if (cc_pvt->cc_request_response_pending) {
+ cc_pvt->cc_request_response_pending = 0;
+ res = pri_cc_req_rsp(cc_pvt->pri->pri, cc_pvt->cc_id, 0/* success */);
+ } else {
+ res = 0;
+ }
+ ast_mutex_unlock(&cc_pvt->pri->lock);
+ if (res) {
+ ast_cc_failed(agent->core_id, "%s agent failed to send the CC request ack.",
+ sig_pri_cc_type_name);
+ }
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Request the status of the agent's device.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * Asynchronous request for the status of any caller
+ * which may be a valid caller for the CC transaction.
+ * Status responses should be made using the
+ * ast_cc_status_response function.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int sig_pri_cc_agent_status_req(struct ast_cc_agent *agent)
+{
+ struct sig_pri_cc_agent_prv *cc_pvt;
+
+ cc_pvt = agent->private_data;
+ ast_mutex_lock(&cc_pvt->pri->lock);
+ pri_cc_status_req(cc_pvt->pri->pri, cc_pvt->cc_id);
+ ast_mutex_unlock(&cc_pvt->pri->lock);
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Request for an agent's phone to stop ringing.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * The usefulness of this is quite limited. The only specific
+ * known case for this is if Asterisk requests CC over an ISDN
+ * PTMP link as the TE side. If other phones are in the same
+ * recall group as the Asterisk server, and one of those phones
+ * picks up the recall notice, then Asterisk will receive a
+ * "stop ringing" notification from the NT side of the PTMP
+ * link. This indication needs to be passed to the phone
+ * on the other side of the Asterisk server which originally
+ * placed the call so that it will stop ringing. Since the
+ * phone may be of any type, it is necessary to have a callback
+ * that the core can know about.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int sig_pri_cc_agent_stop_ringing(struct ast_cc_agent *agent)
+{
+ struct sig_pri_cc_agent_prv *cc_pvt;
+
+ cc_pvt = agent->private_data;
+ ast_mutex_lock(&cc_pvt->pri->lock);
+ pri_cc_stop_alerting(cc_pvt->pri->pri, cc_pvt->cc_id);
+ ast_mutex_unlock(&cc_pvt->pri->lock);
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Let the caller know that the callee has become free
+ * but that the caller cannot attempt to call back because
+ * he is either busy or there is congestion on his line.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * This is something that really only affects a scenario where
+ * a phone places a call over ISDN PTMP to Asterisk, who then
+ * connects over PTMP again to the ISDN network. For most agent
+ * types, there is no need to implement this callback at all
+ * because they don't really need to actually do anything in
+ * this situation. If you're having trouble understanding what
+ * the purpose of this callback is, then you can be safe simply
+ * not implementing it.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int sig_pri_cc_agent_party_b_free(struct ast_cc_agent *agent)
+{
+ struct sig_pri_cc_agent_prv *cc_pvt;
+
+ cc_pvt = agent->private_data;
+ ast_mutex_lock(&cc_pvt->pri->lock);
+ pri_cc_b_free(cc_pvt->pri->pri, cc_pvt->cc_id);
+ ast_mutex_unlock(&cc_pvt->pri->lock);
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Begin monitoring a busy device.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * The core will call this callback if the callee becomes
+ * available but the caller has reported that he is busy.
+ * The agent should begin monitoring the caller's device.
+ * When the caller becomes available again, the agent should
+ * call ast_cc_agent_caller_available.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int sig_pri_cc_agent_start_monitoring(struct ast_cc_agent *agent)
+{
+ /* libpri already knows when and how it needs to monitor Party A. */
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Alert the caller that it is time to try recalling.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * The core will call this function when it receives notice
+ * that a monitored party has become available.
+ *
+ * The agent's job is to send a message to the caller to
+ * notify it of such a change. If the agent is able to
+ * discern that the caller is currently unavailable, then
+ * the agent should react by calling the ast_cc_caller_unavailable
+ * function.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int sig_pri_cc_agent_callee_available(struct ast_cc_agent *agent)
+{
+ struct sig_pri_cc_agent_prv *cc_pvt;
+
+ cc_pvt = agent->private_data;
+ ast_mutex_lock(&cc_pvt->pri->lock);
+ pri_cc_remote_user_free(cc_pvt->pri->pri, cc_pvt->cc_id);
+ ast_mutex_unlock(&cc_pvt->pri->lock);
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Destroy private data on the agent.
+ * \since 1.8
+ *
+ * \param agent CC core agent control.
+ *
+ * \details
+ * The core will call this function upon completion
+ * or failure of CC.
+ *
+ * \note
+ * The agent private_data pointer may be NULL if the agent
+ * constructor failed.
+ *
+ * \return Nothing
+ */
+void sig_pri_cc_agent_destructor(struct ast_cc_agent *agent)
+{
+ struct sig_pri_cc_agent_prv *cc_pvt;
+ int res;
+
+ cc_pvt = agent->private_data;
+ if (!cc_pvt) {
+ /* The agent constructor probably failed. */
+ return;
+ }
+ ast_mutex_lock(&cc_pvt->pri->lock);
+ res = -1;
+ if (cc_pvt->cc_request_response_pending) {
+ res = pri_cc_req_rsp(cc_pvt->pri->pri, cc_pvt->cc_id, 2/* short_term_denial */);
+ }
+ if (res) {
+ pri_cc_cancel(cc_pvt->pri->pri, cc_pvt->cc_id);
+ }
+ ast_mutex_unlock(&cc_pvt->pri->lock);
+ ast_free(cc_pvt);
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief Return the hash value of the given CC monitor instance object.
+ * \since 1.8
+ *
+ * \param obj pointer to the (user-defined part) of an object.
+ * \param flags flags from ao2_callback(). Ignored at the moment.
+ *
+ * \retval core_id
+ */
+static int sig_pri_cc_monitor_instance_hash_fn(const void *obj, const int flags)
+{
+ const struct sig_pri_cc_monitor_instance *monitor_instance = obj;
+
+ return monitor_instance->core_id;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \internal
+ * \brief Compere the monitor instance core_id key value.
+ * \since 1.8
+ *
+ * \param obj pointer to the (user-defined part) of an object.
+ * \param arg callback argument from ao2_callback()
+ * \param flags flags from ao2_callback()
+ *
+ * \return values are a combination of enum _cb_results.
+ */
+static int sig_pri_cc_monitor_instance_cmp_fn(void *obj, void *arg, int flags)
+{
+ struct sig_pri_cc_monitor_instance *monitor_1 = obj;
+ struct sig_pri_cc_monitor_instance *monitor_2 = arg;
+
+ return monitor_1->core_id == monitor_2->core_id ? CMP_MATCH | CMP_STOP : 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Request CCSS.
+ * \since 1.8
+ *
+ * \param monitor CC core monitor control.
+ * \param available_timer_id Where to put the available timer scheduler id.
+ * Will never be NULL for a device monitor.
+ *
+ * \details
+ * Perform whatever steps are necessary in order to request CC.
+ * In addition, the monitor implementation is responsible for
+ * starting the available timer in this callback. The scheduler
+ * ID for the callback must be stored in the parent_link's child_avail_id
+ * field.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure.
+ */
+int sig_pri_cc_monitor_req_cc(struct ast_cc_monitor *monitor, int *available_timer_id)
+{
+ struct sig_pri_cc_monitor_instance *instance;
+ int cc_mode;
+ int res;
+
+ switch (monitor->service_offered) {
+ case AST_CC_CCBS:
+ cc_mode = 0;/* CCBS */
+ break;
+ case AST_CC_CCNR:
+ cc_mode = 1;/* CCNR */
+ break;
+ default:
+ /* CC service not supported by ISDN. */
+ return -1;
+ }
+
+ instance = monitor->private_data;
+
+ /* libpri handles it's own available timer. */
+ ast_mutex_lock(&instance->pri->lock);
+ res = pri_cc_req(instance->pri->pri, instance->cc_id, cc_mode);
+ ast_mutex_unlock(&instance->pri->lock);
+
+ return res;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Suspend monitoring.
+ * \since 1.8
+ *
+ * \param monitor CC core monitor control.
+ *
+ * \details
+ * Implementers must perform the necessary steps to suspend
+ * monitoring.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure.
+ */
+int sig_pri_cc_monitor_suspend(struct ast_cc_monitor *monitor)
+{
+ struct sig_pri_cc_monitor_instance *instance;
+
+ instance = monitor->private_data;
+ ast_mutex_lock(&instance->pri->lock);
+ pri_cc_status(instance->pri->pri, instance->cc_id, 1/* busy */);
+ ast_mutex_unlock(&instance->pri->lock);
+
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Unsuspend monitoring.
+ * \since 1.8
+ *
+ * \param monitor CC core monitor control.
+ *
+ * \details
+ * Perform the necessary steps to unsuspend monitoring.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure.
+ */
+int sig_pri_cc_monitor_unsuspend(struct ast_cc_monitor *monitor)
+{
+ struct sig_pri_cc_monitor_instance *instance;
+
+ instance = monitor->private_data;
+ ast_mutex_lock(&instance->pri->lock);
+ pri_cc_status(instance->pri->pri, instance->cc_id, 0/* free */);
+ ast_mutex_unlock(&instance->pri->lock);
+
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Status response to an ast_cc_monitor_status_request().
+ * \since 1.8
+ *
+ * \param monitor CC core monitor control.
+ * \param devstate Current status of a Party A device.
+ *
+ * \details
+ * Alert a monitor as to the status of the agent for which
+ * the monitor had previously requested a status request.
+ *
+ * \note Zero or more responses may come as a result.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure.
+ */
+int sig_pri_cc_monitor_status_rsp(struct ast_cc_monitor *monitor, enum ast_device_state devstate)
+{
+ struct sig_pri_cc_monitor_instance *instance;
+ int cc_status;
+
+ switch (devstate) {
+ case AST_DEVICE_UNKNOWN:
+ case AST_DEVICE_NOT_INUSE:
+ cc_status = 0;/* free */
+ break;
+ case AST_DEVICE_BUSY:
+ case AST_DEVICE_INUSE:
+ cc_status = 1;/* busy */
+ break;
+ default:
+ /* Don't know how to interpret this device state into free/busy status. */
+ return 0;
+ }
+ instance = monitor->private_data;
+ ast_mutex_lock(&instance->pri->lock);
+ pri_cc_status_req_rsp(instance->pri->pri, instance->cc_id, cc_status);
+ ast_mutex_unlock(&instance->pri->lock);
+
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Cancel the running available timer.
+ * \since 1.8
+ *
+ * \param monitor CC core monitor control.
+ * \param sched_id Available timer scheduler id to cancel.
+ * Will never be NULL for a device monitor.
+ *
+ * \details
+ * In most cases, this function will likely consist of just a
+ * call to AST_SCHED_DEL. It might have been possible to do this
+ * within the core, but unfortunately the mixture of sched_thread
+ * and sched usage in Asterisk prevents such usage.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure.
+ */
+int sig_pri_cc_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, int *sched_id)
+{
+ /*
+ * libpri maintains it's own available timer as one of:
+ * T_CCBS2/T_CCBS5/T_CCBS6/QSIG_CCBS_T2
+ * T_CCNR2/T_CCNR5/T_CCNR6/QSIG_CCNR_T2
+ */
+ return 0;
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+#if defined(HAVE_PRI_CCSS)
+/*!
+ * \brief Destroy PRI private data on the monitor.
+ * \since 1.8
+ *
+ * \param monitor_pvt CC device monitor private data pointer.
+ *
+ * \details
+ * Implementers of this callback are responsible for destroying
+ * all heap-allocated data in the monitor's private_data pointer, including
+ * the private_data itself.
+ */
+void sig_pri_cc_monitor_destructor(void *monitor_pvt)
+{
+ struct sig_pri_cc_monitor_instance *instance;
+
+ instance = monitor_pvt;
+ if (!instance) {
+ return;
+ }
+ ao2_unlink(sig_pri_cc_monitors, instance);
+ ao2_ref(instance, -1);
+}
+#endif /* defined(HAVE_PRI_CCSS) */
+
+/*!
+ * \brief Load the sig_pri submodule.
+ * \since 1.8
+ *
+ * \param cc_type_name CC type name to use when looking up agent/monitor.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int sig_pri_load(const char *cc_type_name)
+{
+#if defined(HAVE_PRI_CCSS)
+ sig_pri_cc_type_name = cc_type_name;
+ sig_pri_cc_monitors = ao2_container_alloc(37, sig_pri_cc_monitor_instance_hash_fn,
+ sig_pri_cc_monitor_instance_cmp_fn);
+ if (!sig_pri_cc_monitors) {
+ return -1;
+ }
+#endif /* defined(HAVE_PRI_CCSS) */
+ return 0;
+}
+
+/*!
+ * \brief Unload the sig_pri submodule.
+ * \since 1.8
+ *
+ * \return Nothing
+ */
+void sig_pri_unload(void)
+{
+#if defined(HAVE_PRI_CCSS)
+ if (sig_pri_cc_monitors) {
+ ao2_ref(sig_pri_cc_monitors, -1);
+ sig_pri_cc_monitors = NULL;
+ }
+#endif /* defined(HAVE_PRI_CCSS) */
+}
+
#endif /* HAVE_PRI */