diff options
Diffstat (limited to 'channels/sig_pri.c')
-rw-r--r-- | channels/sig_pri.c | 1552 |
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 */ |