libpri/pri_cc.c

7887 lines
227 KiB
C
Raw Normal View History

/*
* libpri: An implementation of Primary Rate ISDN
*
* Copyright (C) 2009 Digium, Inc.
*
* Richard Mudgett <rmudgett@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2 as published by the
* Free Software Foundation. See the LICENSE file included with
* this program for more details.
*
* In addition, when this program is distributed with Asterisk in
* any form that would qualify as a 'combined work' or as a
* 'derivative work' (but not mere aggregation), you can redistribute
* and/or modify the combination under the terms of the license
* provided with that copy of Asterisk, instead of the license
* terms granted here.
*/
/*!
* \file
* \brief Call Completion controller
*
* \author Richard Mudgett <rmudgett@digium.com>
*/
#include "compat.h"
#include "libpri.h"
#include "pri_internal.h"
#include "pri_facility.h"
#include <stdlib.h>
/* Define CC_SANITY_CHECKS to add some consistency sanity checking. */
//#define CC_SANITY_CHECKS 1
#define CC_SANITY_CHECKS 1// BUGBUG
/*! Maximum times CCBSStatusRequest can have no response before canceling CC. */
#define RAW_STATUS_COUNT_MAX 3
/* ------------------------------------------------------------------- */
/*!
* \brief Find a cc_record by the PTMP reference_id.
*
* \param ctrl D channel controller.
* \param reference_id CCBS reference ID to look for in cc_record pool.
*
* \retval cc_record on success.
* \retval NULL on error.
*/
struct pri_cc_record *pri_cc_find_by_reference(struct pri *ctrl, unsigned reference_id)
{
struct pri_cc_record *cc_record;
for (cc_record = ctrl->cc.pool; cc_record; cc_record = cc_record->next) {
if (cc_record->ccbs_reference_id == reference_id) {
/* Found the record */
break;
}
}
return cc_record;
}
/*!
* \brief Find a cc_record by the PTMP linkage_id.
*
* \param ctrl D channel controller.
* \param linkage_id Call linkage ID to look for in cc_record pool.
*
* \retval cc_record on success.
* \retval NULL on error.
*/
struct pri_cc_record *pri_cc_find_by_linkage(struct pri *ctrl, unsigned linkage_id)
{
struct pri_cc_record *cc_record;
for (cc_record = ctrl->cc.pool; cc_record; cc_record = cc_record->next) {
if (cc_record->call_linkage_id == linkage_id) {
/* Found the record */
break;
}
}
return cc_record;
}
/*!
* \internal
* \brief Find a cc_record by the cc_id.
*
* \param ctrl D channel controller.
* \param cc_id ID to look for in cc_record pool.
*
* \retval cc_record on success.
* \retval NULL on error.
*/
static struct pri_cc_record *pri_cc_find_by_id(struct pri *ctrl, long cc_id)
{
struct pri_cc_record *cc_record;
for (cc_record = ctrl->cc.pool; cc_record; cc_record = cc_record->next) {
if (cc_record->record_id == cc_id) {
/* Found the record */
break;
}
}
return cc_record;
}
/*!
* \internal
* \brief Find the given ie_type in the string of q931 ies.
*
* \param ie_type Q.931 ie type to find in q931_ies.
* \param length Length of the given q931_ies
* \param q931_ies Given q931_ies
*
* \retval found-ie on success.
* \retval NULL on error.
*/
static const struct q931_ie *pri_cc_find_ie(unsigned ie_type, unsigned length, const unsigned char *q931_ies)
{
const unsigned char *pos;
const unsigned char *end;
const unsigned char *next;
const struct q931_ie *cur;
end = q931_ies + length;
for (pos = q931_ies; pos < end; pos = next) {
cur = (const struct q931_ie *) pos;
if (cur->ie & 0x80) {
/* Single octet ie. */
next = pos + 1;
} else {
/* Variable length ie. */
next = cur->data + cur->len;
}
if (cur->ie == ie_type && next <= end) {
/* Found the ie and it is within the given q931_ies body. */
return cur;
}
}
return NULL;
}
/*!
* \internal
* \brief Compare the specified ie type in the CC record q931_ies to the given q931_ies.
*
* \details
* The individual q931 ie is compared with memcmp().
*
* \param ie_type Q.931 ie type to compare.
* \param record_ies CC record q931_ies
* \param length Length of the given q931_ies
* \param q931_ies Given q931_ies
*
* \retval == 0 when record_ies == q931_ies.
* \retval != 0 when record_ies != q931_ies.
*/
static int pri_cc_cmp_ie(unsigned ie_type, const struct q931_saved_ie_contents *record_ies, unsigned length, const unsigned char *q931_ies)
{
const struct q931_ie *left;
const struct q931_ie *right;
left = pri_cc_find_ie(ie_type, record_ies->length, record_ies->data);
right = pri_cc_find_ie(ie_type, length, q931_ies);
if (!left && !right) {
/* Neither has the requested ie to compare so they match. */
return 0;
}
if (!left || !right) {
/* One or the other does not have the requested ie to compare. */
return 1;
}
if (left->len != right->len) {
/* They are not the same length. */
return 1;
}
return memcmp(left->data, right->data, right->len);
}
/*!
* \internal
* \brief Compare the CC record q931_ies to the given q931_ies.
*
* \note
* Only the first BC, HLC, and LLC ies in the given q931_ies are compared.
*
* \param record_ies CC record q931_ies
* \param length Length of the given q931_ies
* \param q931_ies Given q931_ies
*
* \retval == 0 when record_ies == q931_ies.
* \retval != 0 when record_ies != q931_ies.
*/
static int pri_cc_cmp_q931_ies(const struct q931_saved_ie_contents *record_ies, unsigned length, const unsigned char *q931_ies)
{
return pri_cc_cmp_ie(Q931_BEARER_CAPABILITY, record_ies, length, q931_ies)
|| pri_cc_cmp_ie(Q931_HIGH_LAYER_COMPAT, record_ies, length, q931_ies)
|| pri_cc_cmp_ie(Q931_LOW_LAYER_COMPAT, record_ies, length, q931_ies);
}
/*!
* \brief Find a cc_record by an incoming call addressing data.
*
* \param ctrl D channel controller.
* \param party_a Party A address.
* \param party_b Party B address.
* \param length Length of the given q931_ies.
* \param q931_ies BC, HLC, LLC ies to compare with CC records.
*
* \retval cc_record on success.
* \retval NULL on error.
*/
struct pri_cc_record *pri_cc_find_by_addressing(struct pri *ctrl, const struct q931_party_address *party_a, const struct q931_party_address *party_b, unsigned length, const unsigned char *q931_ies)
{
struct pri_cc_record *cc_record;
struct q931_party_address addr_a;
struct q931_party_address addr_b;
addr_a = *party_a;
addr_b = *party_b;
for (cc_record = ctrl->cc.pool; cc_record; cc_record = cc_record->next) {
/* Do not compare the number presentation. */
addr_a.number.presentation = cc_record->party_a.number.presentation;
addr_b.number.presentation = cc_record->party_b.number.presentation;
if (!q931_cmp_party_id_to_address(&cc_record->party_a, &addr_a)
&& !q931_party_address_cmp(&cc_record->party_b, &addr_b)
&& !pri_cc_cmp_q931_ies(&cc_record->saved_ie_contents, length, q931_ies)) {
/* Found the record */
break;
}
}
return cc_record;
}
/*!
* \internal
* \brief Allocate a new cc_record reference id.
*
* \param ctrl D channel controller.
*
* \retval reference_id on success.
* \retval CC_PTMP_INVALID_ID on error.
*/
static int pri_cc_new_reference_id(struct pri *ctrl)
{
long reference_id;
long first_id;
ctrl->cc.last_reference_id = (ctrl->cc.last_reference_id + 1) & 0x7F;
reference_id = ctrl->cc.last_reference_id;
first_id = reference_id;
while (pri_cc_find_by_reference(ctrl, reference_id)) {
ctrl->cc.last_reference_id = (ctrl->cc.last_reference_id + 1) & 0x7F;
reference_id = ctrl->cc.last_reference_id;
if (reference_id == first_id) {
/* We probably have a resource leak. */
pri_error(ctrl, "PTMP call completion reference id exhaustion!\n");
reference_id = CC_PTMP_INVALID_ID;
break;
}
}
return reference_id;
}
/*!
* \internal
* \brief Allocate a new cc_record linkage id.
*
* \param ctrl D channel controller.
*
* \retval linkage_id on success.
* \retval CC_PTMP_INVALID_ID on error.
*/
static int pri_cc_new_linkage_id(struct pri *ctrl)
{
long linkage_id;
long first_id;
ctrl->cc.last_linkage_id = (ctrl->cc.last_linkage_id + 1) & 0x7F;
linkage_id = ctrl->cc.last_linkage_id;
first_id = linkage_id;
while (pri_cc_find_by_linkage(ctrl, linkage_id)) {
ctrl->cc.last_linkage_id = (ctrl->cc.last_linkage_id + 1) & 0x7F;
linkage_id = ctrl->cc.last_linkage_id;
if (linkage_id == first_id) {
/* We probably have a resource leak. */
pri_error(ctrl, "PTMP call completion linkage id exhaustion!\n");
linkage_id = CC_PTMP_INVALID_ID;
break;
}
}
return linkage_id;
}
/*!
* \internal
* \brief Allocate a new cc_record id.
*
* \param ctrl D channel controller.
*
* \retval cc_id on success.
* \retval -1 on error.
*/
static long pri_cc_new_id(struct pri *ctrl)
{
long record_id;
long first_id;
record_id = ++ctrl->cc.last_record_id;
first_id = record_id;
while (pri_cc_find_by_id(ctrl, record_id)) {
record_id = ++ctrl->cc.last_record_id;
if (record_id == first_id) {
/*
* We have a resource leak.
* We should never need to allocate 64k records on a D channel.
*/
pri_error(ctrl, "Too many call completion records!\n");
record_id = -1;
break;
}
}
return record_id;
}
/*!
* \internal
* \brief Disassociate the signaling link call from the cc_record.
*
* \param cc_record CC record to disassociate from the signaling link call.
*
* \return Nothing
*/
static void pri_cc_disassociate_signaling_link(struct pri_cc_record *cc_record)
{
if (cc_record->signaling) {
cc_record->signaling->cc.record = NULL;
cc_record->signaling = NULL;
}
}
/*!
* \internal
* \brief Delete the given call completion record
*
* \param ctrl D channel controller.
* \param doomed Call completion record to destroy
*
* \return Nothing
*/
static void pri_cc_delete_record(struct pri *ctrl, struct pri_cc_record *doomed)
{
struct pri_cc_record **prev;
struct pri_cc_record *current;
/* Unlink CC signaling link associations. */
if (doomed->original_call) {
doomed->original_call->cc.record = NULL;
doomed->original_call = NULL;
}
pri_cc_disassociate_signaling_link(doomed);
for (prev = &ctrl->cc.pool, current = ctrl->cc.pool; current;
prev = &current->next, current = current->next) {
if (current == doomed) {
*prev = current->next;
free(doomed);
return;
}
}
/* The doomed node is not in the call completion database */
}
/*!
* \brief Allocate a new cc_record.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
*
* \retval pointer to new call completion record
* \retval NULL if failed
*/
struct pri_cc_record *pri_cc_new_record(struct pri *ctrl, q931_call *call)
{
struct pri_cc_record *cc_record;
long record_id;
record_id = pri_cc_new_id(ctrl);
if (record_id < 0) {
return NULL;
}
cc_record = calloc(1, sizeof(*cc_record));
if (!cc_record) {
return NULL;
}
/* Initialize the new record */
cc_record->ctrl = ctrl;
cc_record->record_id = record_id;
cc_record->call_linkage_id = CC_PTMP_INVALID_ID;/* So it will never be found this way */
cc_record->ccbs_reference_id = CC_PTMP_INVALID_ID;/* So it will never be found this way */
cc_record->party_a = call->cc.party_a;
cc_record->party_b = call->called;
cc_record->saved_ie_contents = call->cc.saved_ie_contents;
cc_record->bc = call->bc;
cc_record->option.recall_mode = ctrl->cc.option.recall_mode;
/*
* Append the new record to the end of the list so they are in
* cronological order for interrogations.
*/
if (ctrl->cc.pool) {
struct pri_cc_record *cur;
for (cur = ctrl->cc.pool; cur->next; cur = cur->next) {
}
cur->next = cc_record;
} else {
ctrl->cc.pool = cc_record;
}
return cc_record;
}
/*!
* \internal
* \brief Encode ETSI PTP call completion event operation message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param operation PTP call completion event operation to encode.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_etsi_ptp_cc_operation(struct pri *ctrl, unsigned char *pos,
unsigned char *end, enum rose_operation operation)
{
struct rose_msg_invoke msg;
pos = facility_encode_header(ctrl, pos, end, NULL);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = get_invokeid(ctrl);
msg.operation = operation;
pos = rose_encode_invoke(ctrl, pos, end, &msg);
return pos;
}
/*!
* \internal
* \brief Encode ETSI PTMP call completion available message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param cc_record Call completion record to process event.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_etsi_ptmp_cc_available(struct pri *ctrl, unsigned char *pos,
unsigned char *end, struct pri_cc_record *cc_record)
{
struct rose_msg_invoke msg;
pos = facility_encode_header(ctrl, pos, end, NULL);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = get_invokeid(ctrl);
msg.operation = ROSE_ETSI_CallInfoRetain;
msg.args.etsi.CallInfoRetain.call_linkage_id = cc_record->call_linkage_id;
pos = rose_encode_invoke(ctrl, pos, end, &msg);
return pos;
}
/*!
* \internal
* \brief Encode and queue a cc-available message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode call completion available.
* \param cc_record Call completion record to process event.
* \param msgtype Q.931 message type to put facility ie in.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int rose_cc_available_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, int msgtype)
{
unsigned char buffer[256];
unsigned char *end;
switch (ctrl->switchtype) {
case PRI_SWITCH_EUROISDN_E1:
case PRI_SWITCH_EUROISDN_T1:
if (PTMP_MODE(ctrl)) {
end =
enc_etsi_ptmp_cc_available(ctrl, buffer, buffer + sizeof(buffer),
cc_record);
} else {
end =
enc_etsi_ptp_cc_operation(ctrl, buffer, buffer + sizeof(buffer),
ROSE_ETSI_CCBS_T_Available);
}
break;
case PRI_SWITCH_QSIG:
/* Q.SIG does not have a cc-available type message. */
return 0;
default:
return -1;
}
if (!end) {
return -1;
}
return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, NULL);
}
/*!
* \internal
* \brief Encode ETSI PTMP EraseCallLinkageID message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param cc_record Call completion record to process event.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_etsi_ptmp_erase_call_linkage(struct pri *ctrl,
unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record)
{
struct rose_msg_invoke msg;
pos = facility_encode_header(ctrl, pos, end, NULL);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = get_invokeid(ctrl);
msg.operation = ROSE_ETSI_EraseCallLinkageID;
msg.args.etsi.EraseCallLinkageID.call_linkage_id = cc_record->call_linkage_id;
pos = rose_encode_invoke(ctrl, pos, end, &msg);
return pos;
}
/*!
* \internal
* \brief Encode and queue an EraseCallLinkageID message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode EraseCallLinkageID.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int rose_erase_call_linkage_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
{
unsigned char buffer[256];
unsigned char *end;
end =
enc_etsi_ptmp_erase_call_linkage(ctrl, buffer, buffer + sizeof(buffer),
cc_record);
if (!end) {
return -1;
}
return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL);
}
/*!
* \internal
* \brief Encode and send an EraseCallLinkageID message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode EraseCallLinkageID.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int send_erase_call_linkage_id(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
{
if (rose_erase_call_linkage_encode(ctrl, call, cc_record)
|| q931_facility(ctrl, call)) {
pri_message(ctrl,
"Could not schedule facility message for EraseCallLinkageID.\n");
return -1;
}
return 0;
}
/*!
* \internal
* \brief Encode ETSI PTMP CCBSErase message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param cc_record Call completion record to process event.
* \param reason CCBS Erase reason
* normal-unspecified(0), t-CCBS2-timeout(1), t-CCBS3-timeout(2), basic-call-failed(3)
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_etsi_ptmp_ccbs_erase(struct pri *ctrl,
unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record, int reason)
{
struct rose_msg_invoke msg;
pos = facility_encode_header(ctrl, pos, end, NULL);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = get_invokeid(ctrl);
msg.operation = ROSE_ETSI_CCBSErase;
if (cc_record->saved_ie_contents.length
<= sizeof(msg.args.etsi.CCBSErase.q931ie_contents)) {
/* Saved BC, HLC, and LLC from initial SETUP */
msg.args.etsi.CCBSErase.q931ie.length = cc_record->saved_ie_contents.length;
memcpy(msg.args.etsi.CCBSErase.q931ie.contents, cc_record->saved_ie_contents.data,
cc_record->saved_ie_contents.length);
} else {
pri_error(ctrl, "CCBSErase q931 ie contents did not fit.\n");
}
q931_copy_address_to_rose(ctrl, &msg.args.etsi.CCBSErase.address_of_b,
&cc_record->party_b);
msg.args.etsi.CCBSErase.recall_mode = cc_record->option.recall_mode;
msg.args.etsi.CCBSErase.ccbs_reference = cc_record->ccbs_reference_id;
msg.args.etsi.CCBSErase.reason = reason;
pos = rose_encode_invoke(ctrl, pos, end, &msg);
return pos;
}
/*!
* \internal
* \brief Encode and queue an CCBSErase message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode CCBSErase.
* \param cc_record Call completion record to process event.
* \param reason CCBS Erase reason
* normal-unspecified(0), t-CCBS2-timeout(1), t-CCBS3-timeout(2), basic-call-failed(3)
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int rose_ccbs_erase_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, int reason)
{
unsigned char buffer[256];
unsigned char *end;
end =
enc_etsi_ptmp_ccbs_erase(ctrl, buffer, buffer + sizeof(buffer), cc_record,
reason);
if (!end) {
return -1;
}
return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL);
}
/*!
* \internal
* \brief Encode and send an CCBSErase message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode EraseCallLinkageID.
* \param cc_record Call completion record to process event.
* \param reason CCBS Erase reason
* normal-unspecified(0), t-CCBS2-timeout(1), t-CCBS3-timeout(2), basic-call-failed(3)
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int send_ccbs_erase(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, int reason)
{
/*
* XXX May need to add called-party-ie with Party A number in FACILITY message. (CCBSErase)
* ETSI EN 300-195-1 Section 5.41 MSN interaction.
*/
if (rose_ccbs_erase_encode(ctrl, call, cc_record, reason)
|| q931_facility(ctrl, call)) {
pri_message(ctrl,
"Could not schedule facility message for CCBSErase.\n");
return -1;
}
return 0;
}
/*!
* \internal
* \brief Encode ETSI PTMP CCBSStatusRequest result message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param cc_record Call completion record to process event.
* \param is_free TRUE if the Party A status is available.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_etsi_ptmp_ccbs_status_request_rsp(struct pri *ctrl,
unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record, int is_free)
{
struct rose_msg_result msg;
pos = facility_encode_header(ctrl, pos, end, NULL);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = cc_record->response.invoke_id;
msg.operation = ROSE_ETSI_CCBSStatusRequest;
msg.args.etsi.CCBSStatusRequest.free = is_free;
pos = rose_encode_result(ctrl, pos, end, &msg);
return pos;
}
/*!
* \internal
* \brief Encode ETSI PTMP CCBSStatusRequest message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param cc_record Call completion record to process event.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_etsi_ptmp_ccbs_status_request(struct pri *ctrl,
unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record)
{
struct rose_msg_invoke msg;
pos = facility_encode_header(ctrl, pos, end, NULL);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = get_invokeid(ctrl);
msg.operation = ROSE_ETSI_CCBSStatusRequest;
if (cc_record->saved_ie_contents.length
<= sizeof(msg.args.etsi.CCBSStatusRequest.q931ie_contents)) {
/* Saved BC, HLC, and LLC from initial SETUP */
msg.args.etsi.CCBSStatusRequest.q931ie.length =
cc_record->saved_ie_contents.length;
memcpy(msg.args.etsi.CCBSStatusRequest.q931ie.contents,
cc_record->saved_ie_contents.data, cc_record->saved_ie_contents.length);
} else {
pri_error(ctrl, "CCBSStatusRequest q931 ie contents did not fit.\n");
}
msg.args.etsi.CCBSStatusRequest.recall_mode = cc_record->option.recall_mode;
msg.args.etsi.CCBSStatusRequest.ccbs_reference = cc_record->ccbs_reference_id;
pos = rose_encode_invoke(ctrl, pos, end, &msg);
return pos;
}
/*!
* \internal
* \brief Encode ETSI PTMP CCBSRequest/CCNRRequest message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param cc_record Call completion record to process event.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_etsi_ptmp_cc_request(struct pri *ctrl,
unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record)
{
struct rose_msg_invoke msg;
pos = facility_encode_header(ctrl, pos, end, NULL);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = get_invokeid(ctrl);
msg.operation = cc_record->is_ccnr ? ROSE_ETSI_CCNRRequest : ROSE_ETSI_CCBSRequest;
msg.args.etsi.CCBSRequest.call_linkage_id = cc_record->call_linkage_id;
pos = rose_encode_invoke(ctrl, pos, end, &msg);
return pos;
}
/*!
* \internal
* \brief Encode ETSI PTP CCBS_T_Request/CCNR_T_Request message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param cc_record Call completion record to process event.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_etsi_ptp_cc_request(struct pri *ctrl,
unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record)
{
struct rose_msg_invoke msg;
pos = facility_encode_header(ctrl, pos, end, NULL);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = get_invokeid(ctrl);
msg.operation = cc_record->is_ccnr
? ROSE_ETSI_CCNR_T_Request : ROSE_ETSI_CCBS_T_Request;
if (cc_record->saved_ie_contents.length
<= sizeof(msg.args.etsi.CCBS_T_Request.q931ie_contents)) {
/* Saved BC, HLC, and LLC from initial SETUP */
msg.args.etsi.CCBS_T_Request.q931ie.length = cc_record->saved_ie_contents.length;
memcpy(msg.args.etsi.CCBS_T_Request.q931ie.contents,
cc_record->saved_ie_contents.data, cc_record->saved_ie_contents.length);
} else {
pri_error(ctrl, "CCBS_T_Request q931 ie contents did not fit.\n");
}
q931_copy_address_to_rose(ctrl, &msg.args.etsi.CCBS_T_Request.destination,
&cc_record->party_b);
if (cc_record->party_a.number.valid && cc_record->party_a.number.str[0]) {
q931_copy_id_address_to_rose(ctrl, &msg.args.etsi.CCBS_T_Request.originating,
&cc_record->party_a);
msg.args.etsi.CCBS_T_Request.presentation_allowed_indicator_present = 1;
if ((cc_record->party_a.number.presentation & PRI_PRES_RESTRICTION)
== PRI_PRES_ALLOWED) {
msg.args.etsi.CCBS_T_Request.presentation_allowed_indicator = 1;
}
}
//msg.args.etsi.CCBS_T_Request.retention_supported = 0;
pos = rose_encode_invoke(ctrl, pos, end, &msg);
return pos;
}
/*!
* \internal
* \brief Encode Q.SIG ccbsRequest/ccnrRequest message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param cc_record Call completion record to process event.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_qsig_cc_request(struct pri *ctrl,
unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record)
{
struct fac_extension_header header;
struct rose_msg_invoke msg;
memset(&header, 0, sizeof(header));
header.nfe_present = 1;
header.nfe.source_entity = 0; /* endPINX */
header.nfe.destination_entity = 0; /* endPINX */
header.interpretation_present = 1;
header.interpretation = 1; /* clearCallIfAnyInvokePduNotRecognised */
pos = facility_encode_header(ctrl, pos, end, &header);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = get_invokeid(ctrl);
msg.operation = cc_record->is_ccnr
? ROSE_QSIG_CcnrRequest : ROSE_QSIG_CcbsRequest;
/* Fill in Party B address. */
q931_copy_number_to_rose(ctrl, &msg.args.qsig.CcbsRequest.number_b,
&cc_record->party_b.number);
q931_copy_subaddress_to_rose(ctrl, &msg.args.qsig.CcbsRequest.subaddr_b,
&cc_record->party_b.subaddress);
/* Fill in Party A address. */
q931_copy_presented_number_unscreened_to_rose(ctrl,
&msg.args.qsig.CcbsRequest.number_a, &cc_record->party_a.number);
q931_copy_subaddress_to_rose(ctrl, &msg.args.qsig.CcbsRequest.subaddr_a,
&cc_record->party_a.subaddress);
/* Fill in service Q.931 ie information. */
if (cc_record->saved_ie_contents.length
<= sizeof(msg.args.qsig.CcbsRequest.q931ie_contents)) {
/* Saved BC, HLC, and LLC from initial SETUP */
msg.args.qsig.CcbsRequest.q931ie.length = cc_record->saved_ie_contents.length;
memcpy(msg.args.qsig.CcbsRequest.q931ie.contents,
cc_record->saved_ie_contents.data, cc_record->saved_ie_contents.length);
} else {
pri_error(ctrl, "CcbsRequest q931 ie contents did not fit.\n");
}
//msg.args.qsig.CcbsRequest.can_retain_service = 0;
switch (ctrl->cc.option.signaling_retention_req) {
case 0:/* Want release signaling link. */
cc_record->option.retain_signaling_link = 0;
msg.args.qsig.CcbsRequest.retain_sig_connection_present = 1;
msg.args.qsig.CcbsRequest.retain_sig_connection = 0;
break;
case 1:/* Demand retain signaling link. */
cc_record->option.retain_signaling_link = 1;
msg.args.qsig.CcbsRequest.retain_sig_connection_present = 1;
msg.args.qsig.CcbsRequest.retain_sig_connection = 1;
break;
case 2:/* Don't care about signaling link retention. */
default:
cc_record->option.retain_signaling_link = 0;
break;
}
if (!cc_record->party_a.number.valid || cc_record->party_a.number.str[0] == '\0') {
/*
* Party A number is not available for the other end to initiate
* a signaling link to us. We must require that the signaling link
* be retained.
*/
cc_record->option.retain_signaling_link = 1;
msg.args.qsig.CcbsRequest.retain_sig_connection_present = 1;
msg.args.qsig.CcbsRequest.retain_sig_connection = 1;
}
pos = rose_encode_invoke(ctrl, pos, end, &msg);
return pos;
}
/*!
* \internal
* \brief Encode Q.SIG ccSuspend/ccResume/ccPathReserve/ccRingout message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param operation Q.SIG call completion event operation to encode.
* \param interpretation Component interpretation:
* discardAnyUnrecognisedInvokePdu(0),
* clearCallIfAnyInvokePduNotRecognised(1),
* rejectAnyUnrecognisedInvokePdu(2)
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_qsig_cc_extension_event(struct pri *ctrl,
unsigned char *pos, unsigned char *end, enum rose_operation operation,
int interpretation)
{
struct fac_extension_header header;
struct rose_msg_invoke msg;
memset(&header, 0, sizeof(header));
header.nfe_present = 1;
header.nfe.source_entity = 0; /* endPINX */
header.nfe.destination_entity = 0; /* endPINX */
header.interpretation_present = 1;
header.interpretation = interpretation;
pos = facility_encode_header(ctrl, pos, end, &header);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = get_invokeid(ctrl);
msg.operation = operation;
pos = rose_encode_invoke(ctrl, pos, end, &msg);
return pos;
}
/*!
* \internal
* \brief Encode ETSI PTMP CCBSDeactivate message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param cc_record Call completion record to process event.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_etsi_ptmp_cc_deactivate(struct pri *ctrl,
unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record)
{
struct rose_msg_invoke msg;
pos = facility_encode_header(ctrl, pos, end, NULL);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = get_invokeid(ctrl);
msg.operation = ROSE_ETSI_CCBSDeactivate;
msg.args.etsi.CCBSDeactivate.ccbs_reference = cc_record->ccbs_reference_id;
pos = rose_encode_invoke(ctrl, pos, end, &msg);
return pos;
}
/*!
* \internal
* \brief Encode and queue an CCBSDeactivate message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode CCBSDeactivate.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int rose_cc_deactivate_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
{
unsigned char buffer[256];
unsigned char *end;
end =
enc_etsi_ptmp_cc_deactivate(ctrl, buffer, buffer + sizeof(buffer), cc_record);
if (!end) {
return -1;
}
return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL);
}
/*!
* \internal
* \brief Encode and send an CCBSDeactivate message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode CCBSDeactivate.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int send_cc_deactivate_req(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
{
if (rose_cc_deactivate_encode(ctrl, call, cc_record)
|| q931_facility(ctrl, call)) {
pri_message(ctrl,
"Could not schedule facility message for CCBSDeactivate.\n");
return -1;
}
return 0;
}
/*!
* \internal
* \brief Encode ETSI PTMP CCBSBFree message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param cc_record Call completion record to process event.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_etsi_ptmp_ccbs_b_free(struct pri *ctrl,
unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record)
{
struct rose_msg_invoke msg;
pos = facility_encode_header(ctrl, pos, end, NULL);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = get_invokeid(ctrl);
msg.operation = ROSE_ETSI_CCBSBFree;
if (cc_record->saved_ie_contents.length
<= sizeof(msg.args.etsi.CCBSBFree.q931ie_contents)) {
/* Saved BC, HLC, and LLC from initial SETUP */
msg.args.etsi.CCBSBFree.q931ie.length = cc_record->saved_ie_contents.length;
memcpy(msg.args.etsi.CCBSBFree.q931ie.contents, cc_record->saved_ie_contents.data,
cc_record->saved_ie_contents.length);
} else {
pri_error(ctrl, "CCBSBFree q931 ie contents did not fit.\n");
}
q931_copy_address_to_rose(ctrl, &msg.args.etsi.CCBSBFree.address_of_b,
&cc_record->party_b);
msg.args.etsi.CCBSBFree.recall_mode = cc_record->option.recall_mode;
msg.args.etsi.CCBSBFree.ccbs_reference = cc_record->ccbs_reference_id;
pos = rose_encode_invoke(ctrl, pos, end, &msg);
return pos;
}
/*!
* \internal
* \brief Encode and queue an CCBSBFree message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode CCBSBFree.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int rose_ccbs_b_free_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
{
unsigned char buffer[256];
unsigned char *end;
end =
enc_etsi_ptmp_ccbs_b_free(ctrl, buffer, buffer + sizeof(buffer), cc_record);
if (!end) {
return -1;
}
return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL);
}
/*!
* \internal
* \brief Encode and send an CCBSBFree message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode CCBSBFree.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int send_ccbs_b_free(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
{
/*
* XXX May need to add called-party-ie with Party A number in FACILITY message. (CCBSBFree)
* ETSI EN 300-195-1 Section 5.41 MSN interaction.
*/
if (rose_ccbs_b_free_encode(ctrl, call, cc_record)
|| q931_facility(ctrl, call)) {
pri_message(ctrl,
"Could not schedule facility message for CCBSBFree.\n");
return -1;
}
return 0;
}
/*!
* \internal
* \brief Encode ETSI PTMP CCBSRemoteUserFree message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param cc_record Call completion record to process event.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_etsi_ptmp_remote_user_free(struct pri *ctrl,
unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record)
{
struct rose_msg_invoke msg;
pos = facility_encode_header(ctrl, pos, end, NULL);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = get_invokeid(ctrl);
msg.operation = ROSE_ETSI_CCBSRemoteUserFree;
if (cc_record->saved_ie_contents.length
<= sizeof(msg.args.etsi.CCBSRemoteUserFree.q931ie_contents)) {
/* Saved BC, HLC, and LLC from initial SETUP */
msg.args.etsi.CCBSRemoteUserFree.q931ie.length =
cc_record->saved_ie_contents.length;
memcpy(msg.args.etsi.CCBSRemoteUserFree.q931ie.contents,
cc_record->saved_ie_contents.data, cc_record->saved_ie_contents.length);
} else {
pri_error(ctrl, "CCBSRemoteUserFree q931 ie contents did not fit.\n");
}
q931_copy_address_to_rose(ctrl, &msg.args.etsi.CCBSRemoteUserFree.address_of_b,
&cc_record->party_b);
msg.args.etsi.CCBSRemoteUserFree.recall_mode = cc_record->option.recall_mode;
msg.args.etsi.CCBSRemoteUserFree.ccbs_reference = cc_record->ccbs_reference_id;
pos = rose_encode_invoke(ctrl, pos, end, &msg);
return pos;
}
/*!
* \internal
* \brief Encode Q.SIG CcOptionalArg for ccCancel/ccExecPossible message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param cc_record Call completion record to process event.
* \param msgtype Q.931 message type to put facility ie in.
* \param operation library encoded operation-value
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_qsig_cc_optional_arg(struct pri *ctrl,
unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record, int msgtype,
enum rose_operation operation)
{
struct fac_extension_header header;
struct rose_msg_invoke msg;
memset(&header, 0, sizeof(header));
header.nfe_present = 1;
header.nfe.source_entity = 0; /* endPINX */
header.nfe.destination_entity = 0; /* endPINX */
header.interpretation_present = 1;
header.interpretation = 1; /* clearCallIfAnyInvokePduNotRecognised */
pos = facility_encode_header(ctrl, pos, end, &header);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = get_invokeid(ctrl);
msg.operation = operation;
if (cc_record && msgtype == Q931_SETUP) {
msg.args.qsig.CcCancel.full_arg_present = 1;
/* Fill in Party A address. */
q931_copy_number_to_rose(ctrl, &msg.args.qsig.CcCancel.number_a,
&cc_record->party_a.number);
q931_copy_subaddress_to_rose(ctrl, &msg.args.qsig.CcCancel.subaddr_a,
&cc_record->party_a.subaddress);
/* Fill in Party B address. */
q931_copy_number_to_rose(ctrl, &msg.args.qsig.CcCancel.number_b,
&cc_record->party_b.number);
q931_copy_subaddress_to_rose(ctrl, &msg.args.qsig.CcCancel.subaddr_b,
&cc_record->party_b.subaddress);
/* Fill in service Q.931 ie information. */
if (cc_record->saved_ie_contents.length
<= sizeof(msg.args.qsig.CcCancel.q931ie_contents)) {
/* Saved BC, HLC, and LLC from initial SETUP */
msg.args.qsig.CcCancel.q931ie.length = cc_record->saved_ie_contents.length;
memcpy(msg.args.qsig.CcCancel.q931ie.contents,
cc_record->saved_ie_contents.data, cc_record->saved_ie_contents.length);
} else {
pri_error(ctrl, "CcOptionalArg q931 ie contents did not fit.\n");
}
}
pos = rose_encode_invoke(ctrl, pos, end, &msg);
return pos;
}
/*!
* \internal
* \brief Encode and queue a remote user free message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode remote user free message.
* \param cc_record Call completion record to process event.
* \param msgtype Q.931 message type to put facility ie in.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int rose_remote_user_free_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, int msgtype)
{
unsigned char buffer[256];
unsigned char *end;
switch (ctrl->switchtype) {
case PRI_SWITCH_EUROISDN_E1:
case PRI_SWITCH_EUROISDN_T1:
if (PTMP_MODE(ctrl)) {
end =
enc_etsi_ptmp_remote_user_free(ctrl, buffer, buffer + sizeof(buffer),
cc_record);
} else {
end =
enc_etsi_ptp_cc_operation(ctrl, buffer, buffer + sizeof(buffer),
ROSE_ETSI_CCBS_T_RemoteUserFree);
}
break;
case PRI_SWITCH_QSIG:
end = enc_qsig_cc_optional_arg(ctrl, buffer, buffer + sizeof(buffer), cc_record,
msgtype, ROSE_QSIG_CcExecPossible);
break;
default:
return -1;
}
if (!end) {
return -1;
}
return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, NULL);
}
/*!
* \internal
* \brief Encode and send a CC facility event in a SETUP message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param cc_record Call completion record to process event.
* \param encode Function to encode facility to send out in SETUP message.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int pri_cc_send_setup_encode(struct pri *ctrl, struct pri_cc_record *cc_record,
int (*encode)(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record,
int msgtype))
{
struct pri_sr req;
q931_call *call;
call = q931_new_call(ctrl);
if (!call) {
return -1;
}
/* Link the new call as the signaling link. */
cc_record->signaling = call;
call->cc.record = cc_record;
if (encode(ctrl, call, cc_record, Q931_SETUP)) {
/* Should not happen. */
q931_destroycall(ctrl, call);
return -1;
}
pri_sr_init(&req);
if (cc_record->is_agent) {
q931_party_address_to_id(&req.caller, &cc_record->party_b);
q931_party_id_to_address(&req.called, &cc_record->party_a);
} else {
req.caller = cc_record->party_a;
req.called = cc_record->party_b;
}
//req.cis_auto_disconnect = 0;
req.cis_call = 1;
if (q931_setup(ctrl, call, &req)) {
/* Should not happen. */
q931_destroycall(ctrl, call);
return -1;
}
return 0;
}
/*!
* \internal
* \brief Encode and send an remote user free message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int send_remote_user_free(struct pri *ctrl, struct pri_cc_record *cc_record)
{
int retval;
q931_call *call;
/*
* XXX May need to add called-party-ie with Party A number in FACILITY message. (CCBSRemoteUserFree)
* ETSI EN 300-195-1 Section 5.41 MSN interaction.
*/
retval = -1;
switch (ctrl->switchtype) {
case PRI_SWITCH_EUROISDN_E1:
case PRI_SWITCH_EUROISDN_T1:
call = cc_record->signaling;
retval = rose_remote_user_free_encode(ctrl, call, cc_record, Q931_FACILITY);
if (!retval) {
retval = q931_facility(ctrl, call);
}
break;
case PRI_SWITCH_QSIG:
/* ccExecPossible could be sent in FACILITY or SETUP. */
call = cc_record->signaling;
if (call) {
retval = rose_remote_user_free_encode(ctrl, call, cc_record, Q931_FACILITY);
if (!retval) {
retval = q931_facility(ctrl, call);
}
} else {
retval = pri_cc_send_setup_encode(ctrl, cc_record,
rose_remote_user_free_encode);
}
break;
default:
break;
}
if (retval) {
pri_message(ctrl, "Could not schedule message for remote user free.\n");
return -1;
}
return 0;
}
/*!
* \internal
* \brief Encode and queue a Q.SIG ccCancel message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode remote user free message.
* \param cc_record Call completion record to process event.
* \param msgtype Q.931 message type to put facility ie in.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int rose_cc_cancel(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, int msgtype)
{
unsigned char buffer[256];
unsigned char *end;
end = enc_qsig_cc_optional_arg(ctrl, buffer, buffer + sizeof(buffer), cc_record,
msgtype, ROSE_QSIG_CcCancel);
if (!end) {
return -1;
}
return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, NULL);
}
/*!
* \internal
* \brief Encode and send a Q.SIG ccCancel message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int send_cc_cancel(struct pri *ctrl, struct pri_cc_record *cc_record)
{
int retval;
q931_call *call;
/*
* ccCancel could be sent in SETUP or RELEASE.
* If ccPathReserve is supported it could also be sent in DISCONNECT.
*/
retval = -1;
call = cc_record->signaling;
if (call) {
retval = rose_cc_cancel(ctrl, call, cc_record, Q931_ANY_MESSAGE);
if (!retval) {
retval = pri_hangup(ctrl, call, -1);
}
} else {
retval = pri_cc_send_setup_encode(ctrl, cc_record, rose_cc_cancel);
}
if (retval) {
pri_message(ctrl, "Could not schedule message for ccCancel.\n");
return -1;
}
return 0;
}
/*!
* \internal
* \brief Encode and queue a CC suspend message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode CC suspend message.
* \param msgtype Q.931 message type to put facility ie in.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int rose_cc_suspend_encode(struct pri *ctrl, q931_call *call, int msgtype)
{
unsigned char buffer[256];
unsigned char *end;
switch (ctrl->switchtype) {
case PRI_SWITCH_EUROISDN_E1:
case PRI_SWITCH_EUROISDN_T1:
end =
enc_etsi_ptp_cc_operation(ctrl, buffer, buffer + sizeof(buffer),
ROSE_ETSI_CCBS_T_Suspend);
break;
case PRI_SWITCH_QSIG:
end =
enc_qsig_cc_extension_event(ctrl, buffer, buffer + sizeof(buffer),
ROSE_QSIG_CcSuspend, 0/* discardAnyUnrecognisedInvokePdu */);
break;
default:
return -1;
}
if (!end) {
return -1;
}
return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, NULL);
}
/*!
* \internal
* \brief Encode and send a CC suspend message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int send_cc_suspend(struct pri *ctrl, struct pri_cc_record *cc_record)
{
int retval;
q931_call *call;
retval = -1;
switch (ctrl->switchtype) {
case PRI_SWITCH_EUROISDN_E1:
case PRI_SWITCH_EUROISDN_T1:
call = cc_record->signaling;
retval = rose_cc_suspend_encode(ctrl, call, Q931_FACILITY);
if (!retval) {
retval = q931_facility(ctrl, call);
}
break;
case PRI_SWITCH_QSIG:
/*
* Suspend is sent in a CONNECT or FACILITY message.
* If ccPathReserve is supported, it could also be sent in
* RELEASE or DISCONNECT.
*/
call = cc_record->signaling;
if (!call) {
break;
}
retval = rose_cc_suspend_encode(ctrl, call, Q931_ANY_MESSAGE);
if (!retval) {
if (call->ourcallstate == Q931_CALL_STATE_ACTIVE) {
retval = q931_facility(ctrl, call);
} else {
retval = q931_connect(ctrl, call, 0, 0);
}
}
break;
default:
break;
}
if (retval) {
pri_message(ctrl, "Could not schedule message for CC suspend.\n");
return -1;
}
return 0;
}
/*!
* \internal
* \brief Encode and queue a CC resume message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode CC resume message.
* \param msgtype Q.931 message type to put facility ie in.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int rose_cc_resume_encode(struct pri *ctrl, q931_call *call, int msgtype)
{
unsigned char buffer[256];
unsigned char *end;
switch (ctrl->switchtype) {
case PRI_SWITCH_EUROISDN_E1:
case PRI_SWITCH_EUROISDN_T1:
end =
enc_etsi_ptp_cc_operation(ctrl, buffer, buffer + sizeof(buffer),
ROSE_ETSI_CCBS_T_Resume);
break;
case PRI_SWITCH_QSIG:
end =
enc_qsig_cc_extension_event(ctrl, buffer, buffer + sizeof(buffer),
ROSE_QSIG_CcResume, 0/* discardAnyUnrecognisedInvokePdu */);
break;
default:
return -1;
}
if (!end) {
return -1;
}
return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, NULL);
}
/*!
* \internal
* \brief Encode and send a CC resume message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int send_cc_resume(struct pri *ctrl, struct pri_cc_record *cc_record)
{
q931_call *call;
call = cc_record->signaling;
if (!call
|| rose_cc_resume_encode(ctrl, call, Q931_FACILITY)
|| q931_facility(ctrl, call)) {
pri_message(ctrl, "Could not schedule message for CC resume.\n");
return -1;
}
return 0;
}
/*!
* \internal
* \brief Encode ETSI PTMP CCBSStopAlerting message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param cc_record Call completion record to process event.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_etsi_ptmp_ccbs_stop_alerting(struct pri *ctrl,
unsigned char *pos, unsigned char *end, struct pri_cc_record *cc_record)
{
struct rose_msg_invoke msg;
pos = facility_encode_header(ctrl, pos, end, NULL);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = get_invokeid(ctrl);
msg.operation = ROSE_ETSI_CCBSStopAlerting;
msg.args.etsi.CCBSStopAlerting.ccbs_reference = cc_record->ccbs_reference_id;
pos = rose_encode_invoke(ctrl, pos, end, &msg);
return pos;
}
/*!
* \internal
* \brief Encode and queue an CCBSStopAlerting message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode CCBSStopAlerting.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int rose_ccbs_stop_alerting_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
{
unsigned char buffer[256];
unsigned char *end;
end =
enc_etsi_ptmp_ccbs_stop_alerting(ctrl, buffer, buffer + sizeof(buffer), cc_record);
if (!end) {
return -1;
}
return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL);
}
/*!
* \internal
* \brief Encode and send CCBSStopAlerting message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode remote user free.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int send_ccbs_stop_alerting(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
{
if (rose_ccbs_stop_alerting_encode(ctrl, call, cc_record)
|| q931_facility(ctrl, call)) {
pri_message(ctrl,
"Could not schedule facility message for CCBSStopAlerting.\n");
return -1;
}
return 0;
}
/*!
* \internal
* \brief Encode ETSI PTMP CCBSCall message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param cc_record Call completion record to process event.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_etsi_ptmp_cc_recall(struct pri *ctrl, unsigned char *pos,
unsigned char *end, struct pri_cc_record *cc_record)
{
struct rose_msg_invoke msg;
pos = facility_encode_header(ctrl, pos, end, NULL);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = get_invokeid(ctrl);
msg.operation = ROSE_ETSI_CCBSCall;
msg.args.etsi.CCBSCall.ccbs_reference = cc_record->ccbs_reference_id;
pos = rose_encode_invoke(ctrl, pos, end, &msg);
return pos;
}
/*!
* \internal
* \brief Encode and queue a cc-recall message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode cc-recall.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int rose_cc_recall_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
{
unsigned char buffer[256];
unsigned char *end;
switch (ctrl->switchtype) {
case PRI_SWITCH_EUROISDN_E1:
case PRI_SWITCH_EUROISDN_T1:
if (PTMP_MODE(ctrl)) {
end =
enc_etsi_ptmp_cc_recall(ctrl, buffer, buffer + sizeof(buffer), cc_record);
} else {
end =
enc_etsi_ptp_cc_operation(ctrl, buffer, buffer + sizeof(buffer),
ROSE_ETSI_CCBS_T_Call);
}
break;
case PRI_SWITCH_QSIG:
end =
enc_qsig_cc_extension_event(ctrl, buffer, buffer + sizeof(buffer),
ROSE_QSIG_CcRingout, 0/* discardAnyUnrecognisedInvokePdu */);
break;
default:
return -1;
}
if (!end) {
return -1;
}
return pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer, NULL);
}
/*!
* \internal
* \brief Copy the cc information into the ETSI ROSE call-information record.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call_information ROSE call-information record to fill in.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void q931_copy_call_information_to_etsi_rose(struct pri *ctrl, struct roseEtsiCallInformation *call_information, const struct pri_cc_record *cc_record)
{
q931_copy_address_to_rose(ctrl, &call_information->address_of_b, &cc_record->party_b);
if (cc_record->saved_ie_contents.length
<= sizeof(call_information->q931ie_contents)) {
/* Saved BC, HLC, and LLC from initial SETUP */
call_information->q931ie.length = cc_record->saved_ie_contents.length;
memcpy(call_information->q931ie.contents, cc_record->saved_ie_contents.data,
cc_record->saved_ie_contents.length);
} else {
pri_error(ctrl, "call-information q931 ie contents did not fit.\n");
}
call_information->ccbs_reference = cc_record->ccbs_reference_id;
q931_copy_subaddress_to_rose(ctrl, &call_information->subaddress_of_a,
&cc_record->party_a.subaddress);
}
/*!
* \internal
* \brief Encode ETSI PTMP specific CCBSInterrogate/CCNRInterrogate result message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param invoke Decoded ROSE invoke message contents.
* \param cc_record Call completion record to process event.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_etsi_ptmp_cc_interrogate_rsp_specific(struct pri *ctrl,
unsigned char *pos, unsigned char *end, const struct rose_msg_invoke *invoke,
const struct pri_cc_record *cc_record)
{
struct rose_msg_result msg;
pos = facility_encode_header(ctrl, pos, end, NULL);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = invoke->invoke_id;
msg.operation = invoke->operation;
msg.args.etsi.CCBSInterrogate.recall_mode = cc_record->option.recall_mode;
msg.args.etsi.CCBSInterrogate.call_details.num_records = 1;
q931_copy_call_information_to_etsi_rose(ctrl,
&msg.args.etsi.CCBSInterrogate.call_details.list[0], cc_record);
pos = rose_encode_result(ctrl, pos, end, &msg);
return pos;
}
/*!
* \internal
* \brief Encode ETSI PTMP general CCBSInterrogate/CCNRInterrogate result message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param invoke Decoded ROSE invoke message contents.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_etsi_ptmp_cc_interrogate_rsp_general(struct pri *ctrl,
unsigned char *pos, unsigned char *end, const struct rose_msg_invoke *invoke)
{
struct rose_msg_result msg;
struct q931_party_number party_a_number;
const struct pri_cc_record *cc_record;
unsigned char *new_pos;
unsigned idx;
pos = facility_encode_header(ctrl, pos, end, NULL);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = invoke->invoke_id;
msg.operation = invoke->operation;
msg.args.etsi.CCBSInterrogate.recall_mode = ctrl->cc.option.recall_mode;
/* Convert the given party A number. */
q931_party_number_init(&party_a_number);
if (invoke->args.etsi.CCBSInterrogate.a_party_number.length) {
/* The party A number was given. */
rose_copy_number_to_q931(ctrl, &party_a_number,
&invoke->args.etsi.CCBSInterrogate.a_party_number);
}
/* Build the CallDetails list. */
idx = 0;
for (cc_record = ctrl->cc.pool; cc_record; cc_record = cc_record->next) {
if (cc_record->ccbs_reference_id == CC_PTMP_INVALID_ID
|| (!cc_record->is_ccnr) != (invoke->operation == ROSE_ETSI_CCBSInterrogate)) {
/*
* Record does not have a reference id yet
* or is not for the requested CCBS/CCNR mode.
*/
continue;
}
if (party_a_number.valid) {
/* The party A number was given. */
party_a_number.presentation = cc_record->party_a.number.presentation;
if (q931_party_number_cmp(&party_a_number, &cc_record->party_a.number)) {
/* Record party A does not match. */
continue;
}
}
/* Add call information to the CallDetails list. */
q931_copy_call_information_to_etsi_rose(ctrl,
&msg.args.etsi.CCBSInterrogate.call_details.list[idx], cc_record);
++idx;
if (ARRAY_LEN(msg.args.etsi.CCBSInterrogate.call_details.list) <= idx) {
/* List is full. */
break;
}
}
msg.args.etsi.CCBSInterrogate.call_details.num_records = idx;
new_pos = rose_encode_result(ctrl, pos, end, &msg);
/* Reduce the CallDetails list until it fits into the given buffer. */
while (!new_pos && msg.args.etsi.CCBSInterrogate.call_details.num_records) {
--msg.args.etsi.CCBSInterrogate.call_details.num_records;
new_pos = rose_encode_result(ctrl, pos, end, &msg);
}
return new_pos;
}
/*!
* \internal
* \brief Encode and queue a specific CCBSInterrogate/CCNRInterrogate result message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode CCBSInterrogate/CCNRInterrogate response.
* \param invoke Decoded ROSE invoke message contents.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int rose_cc_interrogate_rsp_specific(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke, const struct pri_cc_record *cc_record)
{
unsigned char buffer[256];
unsigned char *end;
end =
enc_etsi_ptmp_cc_interrogate_rsp_specific(ctrl, buffer, buffer + sizeof(buffer),
invoke, cc_record);
if (!end) {
return -1;
}
return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL);
}
/*!
* \internal
* \brief Encode and queue a general CCBSInterrogate/CCNRInterrogate result message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode CCBSInterrogate/CCNRInterrogate response.
* \param invoke Decoded ROSE invoke message contents.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int rose_cc_interrogate_rsp_general(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke)
{
unsigned char buffer[256];
unsigned char *end;
end =
enc_etsi_ptmp_cc_interrogate_rsp_general(ctrl, buffer, buffer + sizeof(buffer),
invoke);
if (!end) {
return -1;
}
return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL);
}
/*!
* \brief Respond to the received CCBSInterrogate/CCNRInterrogate invoke message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which the message came.
* \param invoke Decoded ROSE invoke message contents.
*
* \retval 0 on success.
* \retval -1 on error.
*/
int pri_cc_interrogate_rsp(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke)
{
int encode_result;
if (!ctrl->cc_support) {
/* Call completion is disabled. */
return send_facility_error(ctrl, call, invoke->invoke_id,
ROSE_ERROR_Gen_NotSubscribed);
}
if (invoke->args.etsi.CCBSInterrogate.ccbs_reference_present) {
struct pri_cc_record *cc_record;
/* Specific CC request interrogation. */
cc_record = pri_cc_find_by_reference(ctrl,
invoke->args.etsi.CCBSInterrogate.ccbs_reference);
if (!cc_record
|| ((!cc_record->is_ccnr)
== (invoke->operation == ROSE_ETSI_CCBSInterrogate))) {
/* Record does not exist or is not for the requested CCBS/CCNR mode. */
return send_facility_error(ctrl, call, invoke->invoke_id,
ROSE_ERROR_CCBS_InvalidCCBSReference);
}
encode_result = rose_cc_interrogate_rsp_specific(ctrl, call, invoke, cc_record);
} else {
/* General CC request interrogation. */
encode_result = rose_cc_interrogate_rsp_general(ctrl, call, invoke);
}
if (encode_result || q931_facility(ctrl, call)) {
pri_message(ctrl,
"Could not schedule facility message for cc-interrogate.\n");
return -1;
}
return 0;
}
/*!
* \brief Respond to the received PTMP CCBSRequest/CCNRRequest invoke message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which the message came.
* \param invoke Decoded ROSE invoke message contents.
*
* \return Nothing
*/
void pri_cc_ptmp_request(struct pri *ctrl, q931_call *call, const struct rose_msg_invoke *invoke)
{
struct pri_cc_record *cc_record;
if (!ctrl->cc_support) {
/* Call completion is disabled. */
send_facility_error(ctrl, call, invoke->invoke_id,
ROSE_ERROR_Gen_NotSubscribed);
return;
}
cc_record = pri_cc_find_by_linkage(ctrl,
invoke->args.etsi.CCBSRequest.call_linkage_id);
if (!cc_record) {
send_facility_error(ctrl, call, invoke->invoke_id,
ROSE_ERROR_CCBS_InvalidCallLinkageID);
return;
}
if (cc_record->state != CC_STATE_AVAILABLE) {
send_facility_error(ctrl, call, invoke->invoke_id,
ROSE_ERROR_CCBS_IsAlreadyActivated);
return;
}
cc_record->ccbs_reference_id = pri_cc_new_reference_id(ctrl);
if (cc_record->ccbs_reference_id == CC_PTMP_INVALID_ID) {
/* Could not allocate a call reference id. */
send_facility_error(ctrl, call, invoke->invoke_id,
ROSE_ERROR_CCBS_OutgoingCCBSQueueFull);
return;
}
/* Save off data to know how to send back any response. */
cc_record->response.signaling = call;
cc_record->response.invoke_operation = invoke->operation;
cc_record->response.invoke_id = invoke->invoke_id;
/* Set the requested CC mode. */
cc_record->is_ccnr = (invoke->operation == ROSE_ETSI_CCNRRequest) ? 1 : 0;
pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST);
}
/*!
* \brief Respond to the received PTP CCBS_T_Request/CCNR_T_Request invoke message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which the message came.
* \param msgtype Q.931 message type ie is in.
* \param invoke Decoded ROSE invoke message contents.
*
* \return Nothing
*/
void pri_cc_ptp_request(struct pri *ctrl, q931_call *call, int msgtype, const struct rose_msg_invoke *invoke)
{
struct pri_cc_record *cc_record;
struct q931_party_address party_a;
struct q931_party_address party_b;
if (msgtype != Q931_REGISTER) {
/* Ignore CC request message since it did not come in on the correct message. */
return;
}
if (!ctrl->cc_support) {
/* Call completion is disabled. */
rose_error_msg_encode(ctrl, call, Q931_ANY_MESSAGE, invoke->invoke_id,
ROSE_ERROR_Gen_NotSubscribed);
call->cc.hangup_call = 1;
return;
}
q931_party_address_init(&party_a);
if (invoke->args.etsi.CCBS_T_Request.originating.number.length) {
/* The originating number is present. */
rose_copy_address_to_q931(ctrl, &party_a,
&invoke->args.etsi.CCBS_T_Request.originating);
}
q931_party_address_init(&party_b);
rose_copy_address_to_q931(ctrl, &party_b,
&invoke->args.etsi.CCBS_T_Request.destination);
cc_record = pri_cc_find_by_addressing(ctrl, &party_a, &party_b,
invoke->args.etsi.CCBS_T_Request.q931ie.length,
invoke->args.etsi.CCBS_T_Request.q931ie.contents);
if (!cc_record || cc_record->state != CC_STATE_AVAILABLE) {
/* Could not find the record or already activated */
rose_error_msg_encode(ctrl, call, Q931_ANY_MESSAGE, invoke->invoke_id,
ROSE_ERROR_CCBS_T_ShortTermDenial);
call->cc.hangup_call = 1;
return;
}
/*
* We already have the presentationAllowedIndicator in the cc_record
* when we saved the original call information.
*/
#if 0
if (invoke->args.etsi.CCBS_T_Request.presentation_allowed_indicator_present) {
if (invoke->args.etsi.CCBS_T_Request.presentation_allowed_indicator) {
if (party_a.number.str[0]) {
party_a.number.presentation =
PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED;
} else {
party_a.number.presentation =
PRI_PRES_UNAVAILABLE | PRI_PRES_USER_NUMBER_UNSCREENED;
}
} else {
party_a.number.presentation =
PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED;
}
}
#endif
/* Link the signaling link to the cc_record. */
call->cc.record = cc_record;
cc_record->signaling = call;
/* Save off data to know how to send back any response. */
//cc_record->response.signaling = call;
cc_record->response.invoke_operation = invoke->operation;
cc_record->response.invoke_id = invoke->invoke_id;
/* Set the requested CC mode. */
cc_record->is_ccnr = (invoke->operation == ROSE_ETSI_CCNR_T_Request) ? 1 : 0;
/* Lets keep this signaling link around for awhile. */
call->cis_recognized = 1;
pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST);
}
/*!
* \brief Respond to the received Q.SIG ccbsRequest/ccnrRequest invoke message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which the message came.
* \param msgtype Q.931 message type ie is in.
* \param invoke Decoded ROSE invoke message contents.
*
* \return Nothing
*/
void pri_cc_qsig_request(struct pri *ctrl, q931_call *call, int msgtype, const struct rose_msg_invoke *invoke)
{
struct pri_cc_record *cc_record;
struct q931_party_address party_a;
struct q931_party_address party_b;
if (msgtype != Q931_SETUP) {
/* Ignore CC request message since it did not come in on the correct message. */
return;
}
if (!ctrl->cc_support) {
/* Call completion is disabled. */
rose_error_msg_encode(ctrl, call, Q931_ANY_MESSAGE, invoke->invoke_id,
ROSE_ERROR_QSIG_LongTermRejection);
call->cc.hangup_call = 1;
return;
}
/* Extract Party A address. */
rose_copy_presented_number_unscreened_to_q931(ctrl, &party_a.number,
&invoke->args.qsig.CcbsRequest.number_a);
q931_party_subaddress_init(&party_a.subaddress);
rose_copy_subaddress_to_q931(ctrl, &party_a.subaddress,
&invoke->args.qsig.CcbsRequest.subaddr_a);
/* Extract Party B address. */
q931_party_address_init(&party_b);
rose_copy_number_to_q931(ctrl, &party_b.number,
&invoke->args.qsig.CcbsRequest.number_b);
rose_copy_subaddress_to_q931(ctrl, &party_b.subaddress,
&invoke->args.qsig.CcbsRequest.subaddr_b);
cc_record = pri_cc_find_by_addressing(ctrl, &party_a, &party_b,
invoke->args.qsig.CcbsRequest.q931ie.length,
invoke->args.qsig.CcbsRequest.q931ie.contents);
if (!cc_record || cc_record->state != CC_STATE_AVAILABLE) {
/* Could not find the record or already activated */
rose_error_msg_encode(ctrl, call, Q931_ANY_MESSAGE, invoke->invoke_id,
ROSE_ERROR_QSIG_ShortTermRejection);
call->cc.hangup_call = 1;
return;
}
/* Determine negotiated signaling retention method. */
if (invoke->args.qsig.CcbsRequest.retain_sig_connection_present) {
/* We will do what the originator desires. */
cc_record->option.retain_signaling_link =
invoke->args.qsig.CcbsRequest.retain_sig_connection;
} else {
/* The originator does not care. Do how we are configured. */
cc_record->option.retain_signaling_link =
ctrl->cc.option.signaling_retention_rsp;
}
if (!cc_record->party_a.number.valid || cc_record->party_a.number.str[0] == '\0') {
/*
* Party A number is not available for us to initiate
* a signaling link. We must retain the signaling link.
*/
cc_record->option.retain_signaling_link = 1;
}
/* Link the signaling link to the cc_record. */
call->cc.record = cc_record;
cc_record->signaling = call;
/* Save off data to know how to send back any response. */
//cc_record->response.signaling = call;
cc_record->response.invoke_operation = invoke->operation;
cc_record->response.invoke_id = invoke->invoke_id;
/* Set the requested CC mode. */
cc_record->is_ccnr = (invoke->operation == ROSE_QSIG_CcnrRequest) ? 1 : 0;
/* Lets keep this signaling link around for awhile. */
call->cis_recognized = 1;
pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST);
}
/*!
* \brief Handle the received Q.SIG ccCancel invoke message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which the message came.
* \param msgtype Q.931 message type ie is in.
* \param invoke Decoded ROSE invoke message contents.
*
* \return Nothing
*/
void pri_cc_qsig_cancel(struct pri *ctrl, q931_call *call, int msgtype, const struct rose_msg_invoke *invoke)
{
struct pri_cc_record *cc_record;
struct q931_party_address party_a;
struct q931_party_address party_b;
cc_record = call->cc.record;
if (!cc_record) {
/* The current call is not associated with the cc_record. */
if (invoke->args.qsig.CcCancel.full_arg_present) {
/* Extract Party A address. */
q931_party_address_init(&party_a);
rose_copy_number_to_q931(ctrl, &party_a.number,
&invoke->args.qsig.CcCancel.number_a);
rose_copy_subaddress_to_q931(ctrl, &party_a.subaddress,
&invoke->args.qsig.CcCancel.subaddr_a);
/* Extract Party B address. */
q931_party_address_init(&party_b);
rose_copy_number_to_q931(ctrl, &party_b.number,
&invoke->args.qsig.CcCancel.number_b);
rose_copy_subaddress_to_q931(ctrl, &party_b.subaddress,
&invoke->args.qsig.CcCancel.subaddr_b);
cc_record = pri_cc_find_by_addressing(ctrl, &party_a, &party_b,
invoke->args.qsig.CcCancel.q931ie.length,
invoke->args.qsig.CcCancel.q931ie.contents);
}
if (!cc_record) {
/*
* Could not find the cc_record
* or not enough information to look up a cc_record.
*/
if (msgtype == Q931_SETUP) {
call->cc.hangup_call = 1;
}
return;
}
}
if (msgtype == Q931_SETUP && call->cis_call) {
if (cc_record->signaling) {
/*
* We already have a signaling link.
* This could be a collision with our ccExecPossible.
* Could this be an alias match?
*/
switch (cc_record->state) {
case CC_STATE_WAIT_CALLBACK:
if (ctrl->debug & PRI_DEBUG_CC) {
pri_message(ctrl,
"-- Collision with our ccExecPossible event call. Canceling CC.\n");
}
break;
default:
pri_message(ctrl,
"-- Warning: Possible Q.SIG CC alias match. Canceling CC.\n");
break;
}
cc_record->fsm.qsig.msgtype = msgtype;
pri_cc_event(ctrl, call, cc_record, CC_EVENT_LINK_CANCEL);
call->cc.hangup_call = 1;
return;
}
/* Link the signaling link to the cc_record. */
call->cc.record = cc_record;
cc_record->signaling = call;
/* Lets keep this signaling link around for awhile. */
call->cis_recognized = 1;
}
cc_record->fsm.qsig.msgtype = msgtype;
pri_cc_event(ctrl, call, cc_record, CC_EVENT_LINK_CANCEL);
}
/*!
* \brief Handle the received Q.SIG ccExecPossible invoke message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which the message came.
* \param msgtype Q.931 message type ie is in.
* \param invoke Decoded ROSE invoke message contents.
*
* \return Nothing
*/
void pri_cc_qsig_exec_possible(struct pri *ctrl, q931_call *call, int msgtype, const struct rose_msg_invoke *invoke)
{
struct pri_cc_record *cc_record;
struct q931_party_address party_a;
struct q931_party_address party_b;
cc_record = call->cc.record;
if (!cc_record) {
/* The current call is not associated with the cc_record. */
if (invoke->args.qsig.CcExecPossible.full_arg_present) {
/* Extract Party A address. */
q931_party_address_init(&party_a);
rose_copy_number_to_q931(ctrl, &party_a.number,
&invoke->args.qsig.CcExecPossible.number_a);
rose_copy_subaddress_to_q931(ctrl, &party_a.subaddress,
&invoke->args.qsig.CcExecPossible.subaddr_a);
/* Extract Party B address. */
q931_party_address_init(&party_b);
rose_copy_number_to_q931(ctrl, &party_b.number,
&invoke->args.qsig.CcExecPossible.number_b);
rose_copy_subaddress_to_q931(ctrl, &party_b.subaddress,
&invoke->args.qsig.CcExecPossible.subaddr_b);
cc_record = pri_cc_find_by_addressing(ctrl, &party_a, &party_b,
invoke->args.qsig.CcExecPossible.q931ie.length,
invoke->args.qsig.CcExecPossible.q931ie.contents);
}
if (!cc_record) {
/*
* Could not find the cc_record
* or not enough information to look up a cc_record.
*/
rose_cc_cancel(ctrl, call, NULL, Q931_ANY_MESSAGE);
if (msgtype == Q931_SETUP) {
call->cc.hangup_call = 1;
} else {
/* msgtype should be Q931_FACILITY. */
pri_hangup(ctrl, call, -1);
}
return;
}
}
if (msgtype == Q931_SETUP && call->cis_call) {
if (cc_record->signaling) {
/*
* We already have a signaling link. This should not happen.
* Could this be an alias match?
*/
pri_message(ctrl,
"-- Warning: Possible Q.SIG CC alias match. Sending ccCancel back.\n");
rose_cc_cancel(ctrl, call, NULL, Q931_ANY_MESSAGE);
call->cc.hangup_call = 1;
return;
}
/* Link the signaling link to the cc_record. */
call->cc.record = cc_record;
cc_record->signaling = call;
/* Lets keep this signaling link around for awhile. */
call->cis_recognized = 1;
}
cc_record->fsm.qsig.msgtype = msgtype;
pri_cc_event(ctrl, call, cc_record, CC_EVENT_REMOTE_USER_FREE);
}
/*!
* \internal
* \brief Convert the given call completion state to a string.
*
* \param state CC state to convert to string.
*
* \return String version of call completion state.
*/
static const char *pri_cc_fsm_state_str(enum CC_STATES state)
{
const char *str;
str = "Unknown";
switch (state) {
case CC_STATE_IDLE:
str = "CC_STATE_IDLE";
break;
case CC_STATE_PENDING_AVAILABLE:
str = "CC_STATE_PENDING_AVAILABLE";
break;
case CC_STATE_AVAILABLE:
str = "CC_STATE_AVAILABLE";
break;
case CC_STATE_REQUESTED:
str = "CC_STATE_REQUESTED";
break;
case CC_STATE_ACTIVATED:
str = "CC_STATE_ACTIVATED";
break;
case CC_STATE_B_AVAILABLE:
str = "CC_STATE_B_AVAILABLE";
break;
case CC_STATE_SUSPENDED:
str = "CC_STATE_SUSPENDED";
break;
case CC_STATE_WAIT_CALLBACK:
str = "CC_STATE_WAIT_CALLBACK";
break;
case CC_STATE_CALLBACK:
str = "CC_STATE_CALLBACK";
break;
case CC_STATE_WAIT_DESTRUCTION:
str = "CC_STATE_WAIT_DESTRUCTION";
break;
case CC_STATE_NUM:
/* Not a real state. */
break;
}
return str;
}
/*!
* \internal
* \brief Convert the given call completion event to a string.
*
* \param event CC event to convert to string.
*
* \return String version of call completion event.
*/
static const char *pri_cc_fsm_event_str(enum CC_EVENTS event)
{
const char *str;
str = "Unknown";
switch (event) {
case CC_EVENT_AVAILABLE:
str = "CC_EVENT_AVAILABLE";
break;
case CC_EVENT_CC_REQUEST:
str = "CC_EVENT_CC_REQUEST";
break;
case CC_EVENT_CC_REQUEST_ACCEPT:
str = "CC_EVENT_CC_REQUEST_ACCEPT";
break;
case CC_EVENT_CC_REQUEST_FAIL:
str = "CC_EVENT_CC_REQUEST_FAIL";
break;
case CC_EVENT_REMOTE_USER_FREE:
str = "CC_EVENT_REMOTE_USER_FREE";
break;
case CC_EVENT_B_FREE:
str = "CC_EVENT_B_FREE";
break;
case CC_EVENT_STOP_ALERTING:
str = "CC_EVENT_STOP_ALERTING";
break;
case CC_EVENT_A_STATUS:
str = "CC_EVENT_A_STATUS";
break;
case CC_EVENT_A_FREE:
str = "CC_EVENT_A_FREE";
break;
case CC_EVENT_A_BUSY:
str = "CC_EVENT_A_BUSY";
break;
case CC_EVENT_SUSPEND:
str = "CC_EVENT_SUSPEND";
break;
case CC_EVENT_RESUME:
str = "CC_EVENT_RESUME";
break;
case CC_EVENT_RECALL:
str = "CC_EVENT_RECALL";
break;
case CC_EVENT_LINK_CANCEL:
str = "CC_EVENT_LINK_CANCEL";
break;
case CC_EVENT_CANCEL:
str = "CC_EVENT_CANCEL";
break;
case CC_EVENT_INTERNAL_CLEARING:
str = "CC_EVENT_INTERNAL_CLEARING";
break;
case CC_EVENT_SIGNALING_GONE:
str = "CC_EVENT_SIGNALING_GONE";
break;
case CC_EVENT_HANGUP_SIGNALING:
str = "CC_EVENT_HANGUP_SIGNALING";
break;
case CC_EVENT_MSG_ALERTING:
str = "CC_EVENT_MSG_ALERTING";
break;
case CC_EVENT_MSG_DISCONNECT:
str = "CC_EVENT_MSG_DISCONNECT";
break;
case CC_EVENT_MSG_RELEASE:
str = "CC_EVENT_MSG_RELEASE";
break;
case CC_EVENT_MSG_RELEASE_COMPLETE:
str = "CC_EVENT_MSG_RELEASE_COMPLETE";
break;
case CC_EVENT_TIMEOUT_T_ACTIVATE:
str = "CC_EVENT_TIMEOUT_T_ACTIVATE";
break;
#if 0
case CC_EVENT_TIMEOUT_T_DEACTIVATE:
str = "CC_EVENT_TIMEOUT_T_DEACTIVATE";
break;
case CC_EVENT_TIMEOUT_T_INTERROGATE:
str = "CC_EVENT_TIMEOUT_T_INTERROGATE";
break;
#endif
case CC_EVENT_TIMEOUT_T_RETENTION:
str = "CC_EVENT_TIMEOUT_T_RETENTION";
break;
case CC_EVENT_TIMEOUT_T_CCBS1:
str = "CC_EVENT_TIMEOUT_T_CCBS1";
break;
case CC_EVENT_TIMEOUT_EXTENDED_T_CCBS1:
str = "CC_EVENT_TIMEOUT_EXTENDED_T_CCBS1";
break;
case CC_EVENT_TIMEOUT_T_SUPERVISION:
str = "CC_EVENT_TIMEOUT_T_SUPERVISION";
break;
case CC_EVENT_TIMEOUT_T_RECALL:
str = "CC_EVENT_TIMEOUT_T_RECALL";
break;
}
return str;
}
static const char pri_cc_act_header[] = "%ld CC-Act: %s\n";
#define PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_id) \
if ((ctrl)->debug & PRI_DEBUG_CC) { \
pri_message((ctrl), pri_cc_act_header, (cc_id), __FUNCTION__); \
}
/*!
* \internal
* \brief FSM action to mark FSM for destruction.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_set_self_destruct(struct pri *ctrl, struct pri_cc_record *cc_record)
{
#if defined(CC_SANITY_CHECKS)
struct apdu_event *msg;
#endif /* defined(CC_SANITY_CHECKS) */
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
/* Abort any pending indirect events. */
pri_schedule_del(ctrl, cc_record->t_indirect);
cc_record->t_indirect = 0;
#if defined(CC_SANITY_CHECKS)
if (cc_record->t_retention) {
pri_error(ctrl, "T_RETENTION still active");
pri_schedule_del(ctrl, cc_record->t_retention);
cc_record->t_retention = 0;
}
if (cc_record->t_supervision) {
pri_error(ctrl, "T_SUPERVISION still active");
pri_schedule_del(ctrl, cc_record->t_supervision);
cc_record->t_supervision = 0;
}
if (cc_record->t_recall) {
pri_error(ctrl, "T_RECALL still active");
pri_schedule_del(ctrl, cc_record->t_recall);
cc_record->t_recall = 0;
}
if (PTMP_MODE(ctrl)) {
msg = pri_call_apdu_find(cc_record->signaling,
cc_record->fsm.ptmp.t_ccbs1_invoke_id);
if (msg) {
pri_error(ctrl, "T_CCBS1 still active");
cc_record->fsm.ptmp.t_ccbs1_invoke_id = APDU_INVALID_INVOKE_ID;
pri_call_apdu_delete(cc_record->signaling, msg);
}
if (cc_record->fsm.ptmp.extended_t_ccbs1) {
pri_error(ctrl, "Extended T_CCBS1 still active");
pri_schedule_del(ctrl, cc_record->fsm.ptmp.extended_t_ccbs1);
cc_record->fsm.ptmp.extended_t_ccbs1 = 0;
}
}
if (cc_record->signaling) {
msg = pri_call_apdu_find(cc_record->signaling, cc_record->t_activate_invoke_id);
if (msg) {
pri_error(ctrl, "T_ACTIVATE still active");
cc_record->t_activate_invoke_id = APDU_INVALID_INVOKE_ID;
pri_call_apdu_delete(cc_record->signaling, msg);
}
}
#endif /* defined(CC_SANITY_CHECKS) */
cc_record->fsm_complete = 1;
}
/*!
* \internal
* \brief FSM action to disassociate the signaling link from the cc_record.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_disassociate_signaling_link(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
pri_cc_disassociate_signaling_link(cc_record);
}
/*!
* \internal
* \brief FSM action to send CC available message.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
* \param call Q.931 call leg.
* \param msgtype Q.931 message type to put facility ie in.
*
* \return Nothing
*/
static void pri_cc_act_send_cc_available(struct pri *ctrl, struct pri_cc_record *cc_record, q931_call *call, int msgtype)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
rose_cc_available_encode(ctrl, call, cc_record, msgtype);
}
/*!
* \internal
* \brief FSM action to stop the PTMP T_RETENTION timer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_stop_t_retention(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
pri_schedule_del(ctrl, cc_record->t_retention);
cc_record->t_retention = 0;
}
/*!
* \internal
* \brief T_RETENTION timeout callback.
*
* \param data CC record pointer.
*
* \return Nothing
*/
static void pri_cc_timeout_t_retention(void *data)
{
struct pri_cc_record *cc_record = data;
cc_record->t_retention = 0;
q931_cc_timeout(cc_record->ctrl, cc_record, CC_EVENT_TIMEOUT_T_RETENTION);
}
/*!
* \internal
* \brief FSM action to start the PTMP T_RETENTION timer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_start_t_retention(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
if (cc_record->t_retention) {
pri_error(ctrl, "!! T_RETENTION is already running!");
pri_schedule_del(ctrl, cc_record->t_retention);
}
cc_record->t_retention = pri_schedule_event(ctrl,
ctrl->timers[PRI_TIMER_T_RETENTION], pri_cc_timeout_t_retention, cc_record);
}
/*!
* \internal
* \brief FSM action to stop the PTMP EXTENDED_T_CCBS1 timer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_stop_extended_t_ccbs1(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
pri_schedule_del(ctrl, cc_record->fsm.ptmp.extended_t_ccbs1);
cc_record->fsm.ptmp.extended_t_ccbs1 = 0;
}
/*!
* \internal
* \brief EXTENDED_T_CCBS1 timeout callback.
*
* \param data CC record pointer.
*
* \return Nothing
*/
static void pri_cc_timeout_extended_t_ccbs1(void *data)
{
struct pri_cc_record *cc_record = data;
cc_record->fsm.ptmp.extended_t_ccbs1 = 0;
q931_cc_timeout(cc_record->ctrl, cc_record, CC_EVENT_TIMEOUT_EXTENDED_T_CCBS1);
}
/*!
* \internal
* \brief FSM action to start the PTMP extended T_CCBS1 timer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_start_extended_t_ccbs1(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
if (cc_record->fsm.ptmp.extended_t_ccbs1) {
pri_error(ctrl, "!! Extended T_CCBS1 is already running!");
pri_schedule_del(ctrl, cc_record->fsm.ptmp.extended_t_ccbs1);
}
/* Timeout is T_CCBS1 + 2 seconds. */
cc_record->fsm.ptmp.extended_t_ccbs1 = pri_schedule_event(ctrl,
ctrl->timers[PRI_TIMER_T_CCBS1] + 2000, pri_cc_timeout_extended_t_ccbs1,
cc_record);
}
/*!
* \internal
* \brief FSM action to stop the T_SUPERVISION timer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_stop_t_supervision(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
pri_schedule_del(ctrl, cc_record->t_supervision);
cc_record->t_supervision = 0;
}
/*!
* \internal
* \brief T_SUPERVISION timeout callback.
*
* \param data CC record pointer.
*
* \return Nothing
*/
static void pri_cc_timeout_t_supervision(void *data)
{
struct pri_cc_record *cc_record = data;
cc_record->t_supervision = 0;
q931_cc_timeout(cc_record->ctrl, cc_record, CC_EVENT_TIMEOUT_T_SUPERVISION);
}
/*!
* \internal
* \brief FSM action to start the T_SUPERVISION timer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_start_t_supervision(struct pri *ctrl, struct pri_cc_record *cc_record)
{
int timer_id;
int duration;
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
if (cc_record->t_supervision) {
pri_error(ctrl, "!! A CC supervision timer is already running!");
pri_schedule_del(ctrl, cc_record->t_supervision);
}
switch (ctrl->switchtype) {
case PRI_SWITCH_EUROISDN_E1:
case PRI_SWITCH_EUROISDN_T1:
if (PTMP_MODE(ctrl)) {
/* ETSI PTMP mode. */
timer_id = cc_record->is_ccnr ? PRI_TIMER_T_CCNR2 : PRI_TIMER_T_CCBS2;
} else if (cc_record->is_agent) {
/* ETSI PTP mode network B side. */
timer_id = cc_record->is_ccnr ? PRI_TIMER_T_CCNR5 : PRI_TIMER_T_CCBS5;
} else {
/* ETSI PTP mode network A side. */
timer_id = cc_record->is_ccnr ? PRI_TIMER_T_CCNR6 : PRI_TIMER_T_CCBS6;
}
duration = ctrl->timers[timer_id];
break;
case PRI_SWITCH_QSIG:
timer_id = cc_record->is_ccnr ? PRI_TIMER_QSIG_CCNR_T2 : PRI_TIMER_QSIG_CCBS_T2;
duration = ctrl->timers[timer_id];
break;
default:
/* Timer not defined for this switch type. Should never happen. */
pri_error(ctrl, "!! A CC supervision timer is not defined!");
duration = 0;
break;
}
cc_record->t_supervision = pri_schedule_event(ctrl, duration,
pri_cc_timeout_t_supervision, cc_record);
}
/*!
* \internal
* \brief FSM action to stop the T_RECALL timer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_stop_t_recall(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
pri_schedule_del(ctrl, cc_record->t_recall);
cc_record->t_recall = 0;
}
/*!
* \internal
* \brief T_RECALL timeout callback.
*
* \param data CC record pointer.
*
* \return Nothing
*/
static void pri_cc_timeout_t_recall(void *data)
{
struct pri_cc_record *cc_record = data;
cc_record->t_recall = 0;
q931_cc_timeout(cc_record->ctrl, cc_record, CC_EVENT_TIMEOUT_T_RECALL);
}
/*!
* \internal
* \brief FSM action to start the T_RECALL timer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_start_t_recall(struct pri *ctrl, struct pri_cc_record *cc_record)
{
int duration;
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
if (cc_record->t_recall) {
pri_error(ctrl, "!! T_RECALL is already running!");
pri_schedule_del(ctrl, cc_record->t_recall);
}
switch (ctrl->switchtype) {
case PRI_SWITCH_EUROISDN_E1:
case PRI_SWITCH_EUROISDN_T1:
duration = ctrl->timers[PRI_TIMER_T_CCBS3];
break;
case PRI_SWITCH_QSIG:
duration = ctrl->timers[PRI_TIMER_QSIG_CC_T3];
break;
default:
/* Timer not defined for this switch type. Should never happen. */
pri_error(ctrl, "!! A CC recall timer is not defined!");
duration = 0;
break;
}
cc_record->t_recall = pri_schedule_event(ctrl, duration, pri_cc_timeout_t_recall,
cc_record);
}
/*!
* \internal
* \brief FSM action to send the EraseCallLinkageID message.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_send_erase_call_linkage_id(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
send_erase_call_linkage_id(ctrl, cc_record->signaling, cc_record);
}
/*!
* \internal
* \brief FSM action to send the CCBSErase message.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
* \param reason CCBS Erase reason
* normal-unspecified(0), t-CCBS2-timeout(1), t-CCBS3-timeout(2), basic-call-failed(3)
*
* \return Nothing
*/
static void pri_cc_act_send_ccbs_erase(struct pri *ctrl, struct pri_cc_record *cc_record, int reason)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
send_ccbs_erase(ctrl, cc_record->signaling, cc_record, reason);
}
/*!
* \internal
* \brief Find the T_CCBS1 timer/CCBSStatusRequest message.
*
* \param cc_record Call completion record to process event.
*
* \return Facility message pointer or NULL if not active.
*/
static struct apdu_event *pri_cc_get_t_ccbs1_status(struct pri_cc_record *cc_record)
{
return pri_call_apdu_find(cc_record->signaling,
cc_record->fsm.ptmp.t_ccbs1_invoke_id);
}
/*!
* \internal
* \brief FSM action to stop the PTMP T_CCBS1 timer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_stop_t_ccbs1(struct pri *ctrl, struct pri_cc_record *cc_record)
{
struct apdu_event *msg;
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
msg = pri_call_apdu_find(cc_record->signaling, cc_record->fsm.ptmp.t_ccbs1_invoke_id);
if (msg) {
cc_record->fsm.ptmp.t_ccbs1_invoke_id = APDU_INVALID_INVOKE_ID;
pri_call_apdu_delete(cc_record->signaling, msg);
}
}
/*!
* \internal
* \brief CCBSStatusRequest response callback function.
*
* \param reason Reason callback is called.
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param apdu APDU queued entry. Do not change!
* \param msg APDU response message data. (NULL if was not the reason called.)
*
* \return TRUE if no more responses are expected.
*/
static int pri_cc_ccbs_status_response(enum APDU_CALLBACK_REASON reason, struct pri *ctrl, struct q931_call *call, struct apdu_event *apdu, const struct apdu_msg_data *msg)
{
struct pri_cc_record *cc_record;
cc_record = apdu->response.user.ptr;
switch (reason) {
case APDU_CALLBACK_REASON_ERROR:
cc_record->fsm.ptmp.t_ccbs1_invoke_id = APDU_INVALID_INVOKE_ID;
break;
case APDU_CALLBACK_REASON_TIMEOUT:
cc_record->fsm.ptmp.t_ccbs1_invoke_id = APDU_INVALID_INVOKE_ID;
pri_cc_event(ctrl, call, cc_record, CC_EVENT_TIMEOUT_T_CCBS1);
break;
case APDU_CALLBACK_REASON_MSG_RESULT:
pri_cc_event(ctrl, call, cc_record,
msg->response.result->args.etsi.CCBSStatusRequest.free
? CC_EVENT_A_FREE : CC_EVENT_A_BUSY);
break;
default:
break;
}
return 0;
}
/*!
* \internal
* \brief Encode and queue an CCBSStatusRequest message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode CCBSStatusRequest.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int rose_ccbs_status_request(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
{
unsigned char buffer[256];
unsigned char *end;
struct apdu_callback_data response;
end =
enc_etsi_ptmp_ccbs_status_request(ctrl, buffer, buffer + sizeof(buffer),
cc_record);
if (!end) {
return -1;
}
memset(&response, 0, sizeof(response));
cc_record->fsm.ptmp.t_ccbs1_invoke_id = ctrl->last_invoke;
response.invoke_id = ctrl->last_invoke;
response.timeout_time = ctrl->timers[PRI_TIMER_T_CCBS1];
response.callback = pri_cc_ccbs_status_response;
response.user.ptr = cc_record;
return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, &response);
}
/*!
* \internal
* \brief Encode and send an CCBSStatusRequest message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode CCBSStatusRequest.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int send_ccbs_status_request(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
{
/*
* XXX May need to add called-party-ie with Party A number in FACILITY message. (CCBSStatusRequest)
* ETSI EN 300-195-1 Section 5.41 MSN interaction.
*/
if (rose_ccbs_status_request(ctrl, call, cc_record)
|| q931_facility(ctrl, call)) {
pri_message(ctrl,
"Could not schedule facility message for CCBSStatusRequest.\n");
return -1;
}
return 0;
}
/*!
* \internal
* \brief FSM action to send the CCBSStatusRequest message.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_send_ccbs_status_request(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
send_ccbs_status_request(ctrl, cc_record->signaling, cc_record);
}
/*!
* \internal
* \brief FSM action to stop the PTMP T_ACTIVATE timer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_stop_t_activate(struct pri *ctrl, struct pri_cc_record *cc_record)
{
struct apdu_event *msg;
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
if (!cc_record->signaling) {
return;
}
msg = pri_call_apdu_find(cc_record->signaling, cc_record->t_activate_invoke_id);
if (msg) {
cc_record->t_activate_invoke_id = APDU_INVALID_INVOKE_ID;
pri_call_apdu_delete(cc_record->signaling, msg);
}
}
/*!
* \internal
* \brief cc-request PTMP response callback function.
*
* \param reason Reason callback is called.
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param apdu APDU queued entry. Do not change!
* \param msg APDU response message data. (NULL if was not the reason called.)
*
* \return TRUE if no more responses are expected.
*/
static int pri_cc_req_response_ptmp(enum APDU_CALLBACK_REASON reason, struct pri *ctrl, struct q931_call *call, struct apdu_event *apdu, const struct apdu_msg_data *msg)
{
struct pri_cc_record *cc_record;
cc_record = apdu->response.user.ptr;
switch (reason) {
case APDU_CALLBACK_REASON_ERROR:
cc_record->t_activate_invoke_id = APDU_INVALID_INVOKE_ID;
break;
case APDU_CALLBACK_REASON_TIMEOUT:
cc_record->t_activate_invoke_id = APDU_INVALID_INVOKE_ID;
pri_cc_event(ctrl, call, cc_record, CC_EVENT_TIMEOUT_T_ACTIVATE);
break;
case APDU_CALLBACK_REASON_MSG_RESULT:
/*
* Since we received this facility, we will not be allocating any
* reference and linkage id's.
*/
cc_record->ccbs_reference_id =
msg->response.result->args.etsi.CCBSRequest.ccbs_reference & 0x7F;
cc_record->option.recall_mode =
msg->response.result->args.etsi.CCBSRequest.recall_mode;
pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_ACCEPT);
break;
case APDU_CALLBACK_REASON_MSG_ERROR:
cc_record->msg.cc_req_rsp.reason = reason;
cc_record->msg.cc_req_rsp.code = msg->response.error->code;
pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_FAIL);
break;
case APDU_CALLBACK_REASON_MSG_REJECT:
cc_record->msg.cc_req_rsp.reason = reason;
cc_record->msg.cc_req_rsp.code = msg->response.reject->code;
pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_FAIL);
break;
default:
break;
}
/*
* No more reponses are really expected.
* However, the FSM will be removing the apdu_event itself instead.
*/
return 0;
}
/*!
* \internal
* \brief cc-request PTP response callback function.
*
* \param reason Reason callback is called.
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param apdu APDU queued entry. Do not change!
* \param msg APDU response message data. (NULL if was not the reason called.)
*
* \return TRUE if no more responses are expected.
*/
static int pri_cc_req_response_ptp(enum APDU_CALLBACK_REASON reason, struct pri *ctrl, struct q931_call *call, struct apdu_event *apdu, const struct apdu_msg_data *msg)
{
struct pri_cc_record *cc_record;
cc_record = apdu->response.user.ptr;
switch (reason) {
case APDU_CALLBACK_REASON_ERROR:
cc_record->t_activate_invoke_id = APDU_INVALID_INVOKE_ID;
break;
case APDU_CALLBACK_REASON_TIMEOUT:
cc_record->t_activate_invoke_id = APDU_INVALID_INVOKE_ID;
pri_cc_event(ctrl, call, cc_record, CC_EVENT_TIMEOUT_T_ACTIVATE);
break;
case APDU_CALLBACK_REASON_MSG_RESULT:
pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_ACCEPT);
break;
case APDU_CALLBACK_REASON_MSG_ERROR:
cc_record->msg.cc_req_rsp.reason = reason;
cc_record->msg.cc_req_rsp.code = msg->response.error->code;
pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_FAIL);
break;
case APDU_CALLBACK_REASON_MSG_REJECT:
cc_record->msg.cc_req_rsp.reason = reason;
cc_record->msg.cc_req_rsp.code = msg->response.reject->code;
pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_FAIL);
break;
default:
break;
}
/*
* No more reponses are really expected.
* However, the FSM will be removing the apdu_event itself instead.
*/
return 0;
}
/*!
* \internal
* \brief cc-request Q.SIG response callback function.
*
* \param reason Reason callback is called.
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param apdu APDU queued entry. Do not change!
* \param msg APDU response message data. (NULL if was not the reason called.)
*
* \return TRUE if no more responses are expected.
*/
static int pri_cc_req_response_qsig(enum APDU_CALLBACK_REASON reason, struct pri *ctrl, struct q931_call *call, struct apdu_event *apdu, const struct apdu_msg_data *msg)
{
struct pri_cc_record *cc_record;
cc_record = apdu->response.user.ptr;
switch (reason) {
case APDU_CALLBACK_REASON_ERROR:
cc_record->t_activate_invoke_id = APDU_INVALID_INVOKE_ID;
break;
case APDU_CALLBACK_REASON_TIMEOUT:
cc_record->t_activate_invoke_id = APDU_INVALID_INVOKE_ID;
pri_cc_event(ctrl, call, cc_record, CC_EVENT_TIMEOUT_T_ACTIVATE);
break;
case APDU_CALLBACK_REASON_MSG_RESULT:
if (!cc_record->option.retain_signaling_link) {
/* We were ambivalent about the signaling link retention option. */
if (msg->type == Q931_CONNECT) {
/* The far end elected to retain the signaling link. */
cc_record->option.retain_signaling_link = 1;
}
}
cc_record->fsm.qsig.msgtype = msg->type;
pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_ACCEPT);
break;
case APDU_CALLBACK_REASON_MSG_ERROR:
cc_record->msg.cc_req_rsp.reason = reason;
cc_record->msg.cc_req_rsp.code = msg->response.error->code;
cc_record->fsm.qsig.msgtype = msg->type;
pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_FAIL);
break;
case APDU_CALLBACK_REASON_MSG_REJECT:
cc_record->msg.cc_req_rsp.reason = reason;
cc_record->msg.cc_req_rsp.code = msg->response.reject->code;
cc_record->fsm.qsig.msgtype = msg->type;
pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST_FAIL);
break;
default:
break;
}
/*
* No more reponses are really expected.
* However, the FSM will be removing the apdu_event itself instead.
*/
return 0;
}
/*!
* \internal
* \brief Encode and queue a cc-request message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode cc-request.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int rose_cc_request(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
{
unsigned char buffer[256];
unsigned char *end;
struct apdu_callback_data response;
int msgtype;
memset(&response, 0, sizeof(response));
switch (ctrl->switchtype) {
case PRI_SWITCH_EUROISDN_E1:
case PRI_SWITCH_EUROISDN_T1:
if (PTMP_MODE(ctrl)) {
end =
enc_etsi_ptmp_cc_request(ctrl, buffer, buffer + sizeof(buffer),
cc_record);
msgtype = Q931_FACILITY;
response.callback = pri_cc_req_response_ptmp;
} else {
end =
enc_etsi_ptp_cc_request(ctrl, buffer, buffer + sizeof(buffer), cc_record);
msgtype = Q931_REGISTER;
response.callback = pri_cc_req_response_ptp;
}
response.timeout_time = ctrl->timers[PRI_TIMER_T_ACTIVATE];
break;
case PRI_SWITCH_QSIG:
end = enc_qsig_cc_request(ctrl, buffer, buffer + sizeof(buffer), cc_record);
msgtype = Q931_SETUP;
response.callback = pri_cc_req_response_qsig;
response.timeout_time = ctrl->timers[PRI_TIMER_QSIG_CC_T1];
break;
default:
return -1;
}
if (!end) {
return -1;
}
response.user.ptr = cc_record;
response.invoke_id = ctrl->last_invoke;
cc_record->t_activate_invoke_id = ctrl->last_invoke;
return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, &response);
}
/*!
* \internal
* \brief FSM action to queue the cc-request message.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
* \param call Call leg from which to encode cc-request.
*
* \return Nothing
*/
static void pri_cc_act_queue_cc_request(struct pri *ctrl, struct pri_cc_record *cc_record, q931_call *call)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
if (rose_cc_request(ctrl, call, cc_record)) {
pri_message(ctrl, "Could not queue message for cc-request.\n");
}
}
/*!
* \internal
* \brief FSM action to send the CCBSDeactivate message.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_send_cc_deactivate_req(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
send_cc_deactivate_req(ctrl, cc_record->signaling, cc_record);
}
/*!
* \internal
* \brief FSM action to send the CCBSBFree message.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_send_ccbs_b_free(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
send_ccbs_b_free(ctrl, cc_record->signaling, cc_record);
}
/*!
* \internal
* \brief FSM action to send the remote user free message.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_send_remote_user_free(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
send_remote_user_free(ctrl, cc_record);
}
/*!
* \internal
* \brief FSM action to send the CALL_PROCEEDING message on the signaling link.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_send_call_proceeding(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
pri_proceeding(ctrl, cc_record->signaling, 0, 0);
}
/*!
* \internal
* \brief FSM action to send the CC suspend message.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_send_cc_suspend(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
send_cc_suspend(ctrl, cc_record);
}
/*!
* \internal
* \brief FSM action to send the CC resume message.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_send_cc_resume(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
send_cc_resume(ctrl, cc_record);
}
/*!
* \internal
* \brief FSM action to send the ccCancel message.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_send_cc_cancel(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
send_cc_cancel(ctrl, cc_record);
}
/*!
* \internal
* \brief FSM action to send the CCBSStopAlerting message.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_send_ccbs_stop_alerting(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
send_ccbs_stop_alerting(ctrl, cc_record->signaling, cc_record);
}
/*!
* \internal
* \brief FSM action to release the call linkage id.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_release_link_id(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
cc_record->call_linkage_id = CC_PTMP_INVALID_ID;
}
/*!
* \internal
* \brief FSM action to set the Q.SIG retain-signaling-link option.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_set_retain_signaling_link(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
cc_record->option.retain_signaling_link = 1;
}
/*!
* \internal
* \brief FSM action to reset the raw A status request no response count.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_raw_status_count_reset(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
cc_record->fsm.ptmp.party_a_status_count = 0;
}
/*!
* \internal
* \brief FSM action to increment the raw A status request no response count.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_raw_status_count_increment(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
++cc_record->fsm.ptmp.party_a_status_count;
}
/*!
* \internal
* \brief FSM action to reset raw A status.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_reset_raw_a_status(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
cc_record->fsm.ptmp.party_a_status_acc = CC_PARTY_A_AVAILABILITY_INVALID;
}
/*!
* \internal
* \brief FSM action to add raw A status with busy.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_add_raw_a_status_busy(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
if (cc_record->fsm.ptmp.party_a_status_acc != CC_PARTY_A_AVAILABILITY_FREE) {
cc_record->fsm.ptmp.party_a_status_acc = CC_PARTY_A_AVAILABILITY_BUSY;
}
}
/*!
* \internal
* \brief FSM action to set raw A status to free.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_set_raw_a_status_free(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
cc_record->fsm.ptmp.party_a_status_acc = CC_PARTY_A_AVAILABILITY_FREE;
}
/*!
* \internal
* \brief Fill in the status response party A status update event.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_fill_status_rsp_a(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
{
struct pri_subcommand *subcmd;
if (cc_record->fsm.ptmp.party_a_status_acc == CC_PARTY_A_AVAILABILITY_INVALID) {
/* Accumulated party A status is invalid so don't pass it up. */
return;
}
subcmd = q931_alloc_subcommand(ctrl);
if (!subcmd) {
return;
}
subcmd->cmd = PRI_SUBCMD_CC_STATUS_REQ_RSP;
subcmd->u.cc_status_req_rsp.cc_id = cc_record->record_id;
subcmd->u.cc_status_req_rsp.status =
(cc_record->fsm.ptmp.party_a_status_acc == CC_PARTY_A_AVAILABILITY_FREE)
? 0 /* free */ : 1 /* busy */;
}
/*!
* \internal
* \brief Pass up party A status response to upper layer (indirectly).
*
* \param data CC record pointer.
*
* \return Nothing
*/
static void pri_cc_indirect_status_rsp_a(void *data)
{
struct pri_cc_record *cc_record = data;
cc_record->t_indirect = 0;
q931_cc_indirect(cc_record->ctrl, cc_record, pri_cc_fill_status_rsp_a);
}
/*!
* \internal
* \brief FSM action to pass up party A status response to upper layer (indirectly).
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*
* \note
* Warning: Must not use this action with pri_cc_act_set_self_destruct() in the
* same event.
*/
static void pri_cc_act_pass_up_status_rsp_a_indirect(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
if (cc_record->fsm.ptmp.party_a_status_acc != CC_PARTY_A_AVAILABILITY_INVALID) {
/* Accumulated party A status is not invalid so pass it up. */
if (cc_record->t_indirect) {
pri_error(ctrl, "!! An indirect action is already active!");
pri_schedule_del(ctrl, cc_record->t_indirect);
}
cc_record->t_indirect = pri_schedule_event(ctrl, 0, pri_cc_indirect_status_rsp_a,
cc_record);
}
}
/*!
* \internal
* \brief FSM action to pass up party A status response to upper layer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_pass_up_status_rsp_a(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
pri_cc_fill_status_rsp_a(ctrl, cc_record->signaling, cc_record);
}
/*!
* \internal
* \brief FSM action to promote raw A status.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_promote_raw_a_status(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
cc_record->party_a_status = cc_record->fsm.ptmp.party_a_status_acc;
}
/*!
* \internal
* \brief FSM action to reset A status.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_reset_a_status(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
cc_record->party_a_status = CC_PARTY_A_AVAILABILITY_INVALID;
}
/*!
* \internal
* \brief FSM action to set A status as busy.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_set_a_status_busy(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
cc_record->party_a_status = CC_PARTY_A_AVAILABILITY_BUSY;
}
/*!
* \internal
* \brief FSM action to set A status as free.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_set_a_status_free(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
cc_record->party_a_status = CC_PARTY_A_AVAILABILITY_FREE;
}
/*!
* \internal
* \brief Fill in the party A status update event.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_fill_status_a(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
{
struct pri_subcommand *subcmd;
if (cc_record->party_a_status == CC_PARTY_A_AVAILABILITY_INVALID) {
/* Party A status is invalid so don't pass it up. */
return;
}
subcmd = q931_alloc_subcommand(ctrl);
if (!subcmd) {
return;
}
subcmd->cmd = PRI_SUBCMD_CC_STATUS;
subcmd->u.cc_status.cc_id = cc_record->record_id;
subcmd->u.cc_status.status =
(cc_record->party_a_status == CC_PARTY_A_AVAILABILITY_FREE)
? 0 /* free */ : 1 /* busy */;
}
/*!
* \internal
* \brief Pass up party A status to upper layer (indirectly).
*
* \param data CC record pointer.
*
* \return Nothing
*/
static void pri_cc_indirect_status_a(void *data)
{
struct pri_cc_record *cc_record = data;
cc_record->t_indirect = 0;
q931_cc_indirect(cc_record->ctrl, cc_record, pri_cc_fill_status_a);
}
/*!
* \internal
* \brief FSM action to pass up party A status to upper layer (indirectly).
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*
* \note
* Warning: Must not use this action with pri_cc_act_set_self_destruct() in the
* same event.
*/
static void pri_cc_act_pass_up_a_status_indirect(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
if (cc_record->party_a_status != CC_PARTY_A_AVAILABILITY_INVALID) {
/* Party A status is not invalid so pass it up. */
if (cc_record->t_indirect) {
pri_error(ctrl, "!! An indirect action is already active!");
pri_schedule_del(ctrl, cc_record->t_indirect);
}
cc_record->t_indirect = pri_schedule_event(ctrl, 0, pri_cc_indirect_status_a,
cc_record);
}
}
/*!
* \internal
* \brief FSM action to pass up party A status to upper layer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_pass_up_a_status(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
pri_cc_fill_status_a(ctrl, cc_record->signaling, cc_record);
}
/*!
* \internal
* \brief FSM action to pass up CC request (CCBS/CCNR) to upper layer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_pass_up_cc_request(struct pri *ctrl, struct pri_cc_record *cc_record)
{
struct pri_subcommand *subcmd;
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
subcmd = q931_alloc_subcommand(ctrl);
if (!subcmd) {
return;
}
subcmd->cmd = PRI_SUBCMD_CC_REQ;
subcmd->u.cc_request.cc_id = cc_record->record_id;
subcmd->u.cc_request.mode = cc_record->is_ccnr ? 1 /* ccnr */ : 0 /* ccbs */;
}
/*!
* \internal
* \brief FSM action to pass up CC cancel to upper layer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_pass_up_cc_cancel(struct pri *ctrl, struct pri_cc_record *cc_record)
{
struct pri_subcommand *subcmd;
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
subcmd = q931_alloc_subcommand(ctrl);
if (!subcmd) {
return;
}
subcmd->cmd = PRI_SUBCMD_CC_CANCEL;
subcmd->u.cc_cancel.cc_id = cc_record->record_id;
subcmd->u.cc_cancel.is_agent = cc_record->is_agent;
}
/*!
* \internal
* \brief FSM action to pass up CC call to upper layer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_pass_up_cc_call(struct pri *ctrl, struct pri_cc_record *cc_record)
{
struct pri_subcommand *subcmd;
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
subcmd = q931_alloc_subcommand(ctrl);
if (!subcmd) {
return;
}
subcmd->cmd = PRI_SUBCMD_CC_CALL;
subcmd->u.cc_call.cc_id = cc_record->record_id;
}
/*!
* \internal
* \brief FSM action to pass up CC available to upper layer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_pass_up_cc_available(struct pri *ctrl, struct pri_cc_record *cc_record)
{
struct pri_subcommand *subcmd;
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
subcmd = q931_alloc_subcommand(ctrl);
if (!subcmd) {
return;
}
subcmd->cmd = PRI_SUBCMD_CC_AVAILABLE;
subcmd->u.cc_available.cc_id = cc_record->record_id;
}
/*!
* \internal
* \brief FSM action to pass up CC request response is success to upper layer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_pass_up_cc_req_rsp_success(struct pri *ctrl, struct pri_cc_record *cc_record)
{
struct pri_subcommand *subcmd;
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
subcmd = q931_alloc_subcommand(ctrl);
if (!subcmd) {
return;
}
subcmd->cmd = PRI_SUBCMD_CC_REQ_RSP;
subcmd->u.cc_request_rsp.cc_id = cc_record->record_id;
subcmd->u.cc_request_rsp.status = 0;/* success */
subcmd->u.cc_request_rsp.fail_code = 0;
}
/*!
* \internal
* \brief FSM action to pass up CC request response is failed to upper layer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_pass_up_cc_req_rsp_fail(struct pri *ctrl, struct pri_cc_record *cc_record)
{
struct pri_subcommand *subcmd;
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
subcmd = q931_alloc_subcommand(ctrl);
if (!subcmd) {
return;
}
subcmd->cmd = PRI_SUBCMD_CC_REQ_RSP;
subcmd->u.cc_request_rsp.cc_id = cc_record->record_id;
subcmd->u.cc_request_rsp.status =
(cc_record->msg.cc_req_rsp.reason == APDU_CALLBACK_REASON_MSG_ERROR)
? 2 /* error */ : 3 /* reject */;
subcmd->u.cc_request_rsp.fail_code = cc_record->msg.cc_req_rsp.code;
}
/*!
* \internal
* \brief FSM action to pass up CC request response is timeout to upper layer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_pass_up_cc_req_rsp_timeout(struct pri *ctrl, struct pri_cc_record *cc_record)
{
struct pri_subcommand *subcmd;
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
subcmd = q931_alloc_subcommand(ctrl);
if (!subcmd) {
return;
}
subcmd->cmd = PRI_SUBCMD_CC_REQ_RSP;
subcmd->u.cc_request_rsp.cc_id = cc_record->record_id;
subcmd->u.cc_request_rsp.status = 1;/* timeout */
subcmd->u.cc_request_rsp.fail_code = 0;
}
/*!
* \internal
* \brief FSM action to pass up CC B free to upper layer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_pass_up_b_free(struct pri *ctrl, struct pri_cc_record *cc_record)
{
struct pri_subcommand *subcmd;
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
subcmd = q931_alloc_subcommand(ctrl);
if (!subcmd) {
return;
}
subcmd->cmd = PRI_SUBCMD_CC_B_FREE;
subcmd->u.cc_b_free.cc_id = cc_record->record_id;
}
/*!
* \internal
* \brief FSM action to pass up CC remote user free to upper layer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_pass_up_remote_user_free(struct pri *ctrl, struct pri_cc_record *cc_record)
{
struct pri_subcommand *subcmd;
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
subcmd = q931_alloc_subcommand(ctrl);
if (!subcmd) {
return;
}
subcmd->cmd = PRI_SUBCMD_CC_REMOTE_USER_FREE;
subcmd->u.cc_remote_user_free.cc_id = cc_record->record_id;
}
/*!
* \internal
* \brief FSM action to pass up stop alerting to upper layer.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_pass_up_stop_alerting(struct pri *ctrl, struct pri_cc_record *cc_record)
{
struct pri_subcommand *subcmd;
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
subcmd = q931_alloc_subcommand(ctrl);
if (!subcmd) {
return;
}
subcmd->cmd = PRI_SUBCMD_CC_STOP_ALERTING;
subcmd->u.cc_stop_alerting.cc_id = cc_record->record_id;
}
/*!
* \internal
* \brief FSM action to send error response to recall attempt.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
* \param code Error code to put in error message response.
*
* \return Nothing
*/
static void pri_cc_act_send_error_recall(struct pri *ctrl, struct pri_cc_record *cc_record, enum rose_error_code code)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
rose_error_msg_encode(ctrl, cc_record->response.signaling, Q931_ANY_MESSAGE,
cc_record->response.invoke_id, code);
}
/*!
* \internal
* \brief FSM action to queue CC recall marker.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
* \param call Q.931 call leg.
*
* \return Nothing
*/
static void pri_cc_act_queue_setup_recall(struct pri *ctrl, struct pri_cc_record *cc_record, q931_call *call)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
rose_cc_recall_encode(ctrl, call, cc_record);
}
/*!
* \internal
* \brief FSM action to request the call be hung up.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
* \param call Q.931 call leg.
*
* \return Nothing
*/
static void pri_cc_act_set_call_to_hangup(struct pri *ctrl, struct pri_cc_record *cc_record, q931_call *call)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
call->cc.hangup_call = 1;
}
/*!
* \internal
* \brief Post the CC_EVENT_HANGUP_SIGNALING event (timeout action).
*
* \param data CC record pointer.
*
* \return Nothing
*/
static void pri_cc_post_hangup_signaling(void *data)
{
struct pri_cc_record *cc_record = data;
cc_record->t_indirect = 0;
q931_cc_timeout(cc_record->ctrl, cc_record, CC_EVENT_HANGUP_SIGNALING);
}
/*!
* \internal
* \brief FSM action to post the CC_EVENT_HANGUP_SIGNALING event indirectly.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_post_hangup_signaling(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
if (cc_record->t_indirect) {
pri_error(ctrl, "!! An indirect action is already active!");
pri_schedule_del(ctrl, cc_record->t_indirect);
}
cc_record->t_indirect = pri_schedule_event(ctrl, 0, pri_cc_post_hangup_signaling,
cc_record);
}
/*!
* \internal
* \brief FSM action to immediately hangup the signaling link.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_hangup_signaling_link(struct pri *ctrl, struct pri_cc_record *cc_record)
{
PRI_CC_ACT_DEBUG_OUTPUT(ctrl, cc_record->record_id);
pri_hangup(ctrl, cc_record->signaling, -1);
}
/*!
* \internal
* \brief FSM action to set original call data into recall call.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
*
* \return Nothing
*/
static void pri_cc_act_set_original_call_parameters(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
{
call->called = cc_record->party_b;
call->remote_id = cc_record->party_a;
call->cc.saved_ie_contents = cc_record->saved_ie_contents;
call->bc = cc_record->bc;
}
/*!
* \internal
* \brief CC FSM PTMP agent CC_STATE_IDLE.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptmp_agent_idle(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_AVAILABLE:
cc_record->state = CC_STATE_PENDING_AVAILABLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_set_self_destruct(ctrl, cc_record);
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTMP agent CC_STATE_PENDING_AVAILABLE.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptmp_agent_pend_avail(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_MSG_ALERTING:
pri_cc_act_send_cc_available(ctrl, cc_record, call, Q931_ALERTING);
cc_record->state = CC_STATE_AVAILABLE;
break;
case CC_EVENT_MSG_DISCONNECT:
pri_cc_act_send_cc_available(ctrl, cc_record, call, Q931_DISCONNECT);
cc_record->state = CC_STATE_AVAILABLE;
break;
case CC_EVENT_INTERNAL_CLEARING:
pri_cc_act_release_link_id(ctrl, cc_record);
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_release_link_id(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTMP agent CC_STATE_AVAILABLE.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptmp_agent_avail(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_MSG_RELEASE:
case CC_EVENT_MSG_RELEASE_COMPLETE:
pri_cc_act_stop_t_retention(ctrl, cc_record);
pri_cc_act_start_t_retention(ctrl, cc_record);
break;
case CC_EVENT_CC_REQUEST:
pri_cc_act_pass_up_cc_request(ctrl, cc_record);
pri_cc_act_stop_t_retention(ctrl, cc_record);
cc_record->state = CC_STATE_REQUESTED;
break;
case CC_EVENT_INTERNAL_CLEARING:
pri_cc_act_stop_t_retention(ctrl, cc_record);
pri_cc_act_start_t_retention(ctrl, cc_record);
break;
case CC_EVENT_TIMEOUT_T_RETENTION:
pri_cc_act_send_erase_call_linkage_id(ctrl, cc_record);
pri_cc_act_release_link_id(ctrl, cc_record);
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_retention(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_send_erase_call_linkage_id(ctrl, cc_record);
pri_cc_act_release_link_id(ctrl, cc_record);
pri_cc_act_stop_t_retention(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTMP agent CC_STATE_REQUESTED.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptmp_agent_req(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_CC_REQUEST_ACCEPT:
pri_cc_act_send_erase_call_linkage_id(ctrl, cc_record);
pri_cc_act_release_link_id(ctrl, cc_record);
pri_cc_act_start_t_supervision(ctrl, cc_record);
pri_cc_act_reset_a_status(ctrl, cc_record);
pri_cc_act_raw_status_count_reset(ctrl, cc_record);
cc_record->state = CC_STATE_ACTIVATED;
break;
case CC_EVENT_CANCEL:
pri_cc_act_send_erase_call_linkage_id(ctrl, cc_record);
pri_cc_act_release_link_id(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTMP agent CC_STATE_ACTIVATED.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptmp_agent_activated(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_RECALL:
pri_cc_act_send_error_recall(ctrl, cc_record, ROSE_ERROR_CCBS_NotReadyForCall);
pri_cc_act_set_call_to_hangup(ctrl, cc_record, call);
break;
case CC_EVENT_B_FREE:
pri_cc_act_send_ccbs_b_free(ctrl, cc_record);
break;
case CC_EVENT_REMOTE_USER_FREE:
switch (cc_record->party_a_status) {
case CC_PARTY_A_AVAILABILITY_INVALID:
if (!pri_cc_get_t_ccbs1_status(cc_record)) {
pri_cc_act_reset_raw_a_status(ctrl, cc_record);
pri_cc_act_send_ccbs_status_request(ctrl, cc_record);
//pri_cc_act_start_t_ccbs1(ctrl, cc_record);
}
cc_record->state = CC_STATE_B_AVAILABLE;
break;
case CC_PARTY_A_AVAILABILITY_BUSY:
pri_cc_act_pass_up_a_status_indirect(ctrl, cc_record);
pri_cc_act_send_ccbs_b_free(ctrl, cc_record);
if (!pri_cc_get_t_ccbs1_status(cc_record)) {
pri_cc_act_reset_raw_a_status(ctrl, cc_record);
pri_cc_act_send_ccbs_status_request(ctrl, cc_record);
//pri_cc_act_start_t_ccbs1(ctrl, cc_record);
}
cc_record->state = CC_STATE_SUSPENDED;
break;
case CC_PARTY_A_AVAILABILITY_FREE:
//pri_cc_act_pass_up_a_status_indirect(ctrl, cc_record);
pri_cc_act_send_remote_user_free(ctrl, cc_record);
pri_cc_act_stop_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record);
pri_cc_act_start_t_recall(ctrl, cc_record);
cc_record->state = CC_STATE_WAIT_CALLBACK;
break;
default:
break;
}
break;
case CC_EVENT_A_STATUS:
if (pri_cc_get_t_ccbs1_status(cc_record)) {
pri_cc_act_pass_up_status_rsp_a_indirect(ctrl, cc_record);
} else {
pri_cc_act_reset_a_status(ctrl, cc_record);
pri_cc_act_reset_raw_a_status(ctrl, cc_record);
pri_cc_act_send_ccbs_status_request(ctrl, cc_record);
//pri_cc_act_start_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record);
pri_cc_act_start_extended_t_ccbs1(ctrl, cc_record);
}
break;
case CC_EVENT_A_FREE:
pri_cc_act_raw_status_count_reset(ctrl, cc_record);
pri_cc_act_set_raw_a_status_free(ctrl, cc_record);
pri_cc_act_promote_raw_a_status(ctrl, cc_record);
pri_cc_act_pass_up_a_status(ctrl, cc_record);
pri_cc_act_stop_t_ccbs1(ctrl, cc_record);
break;
case CC_EVENT_A_BUSY:
pri_cc_act_add_raw_a_status_busy(ctrl, cc_record);
pri_cc_act_pass_up_status_rsp_a(ctrl, cc_record);
break;
case CC_EVENT_TIMEOUT_T_CCBS1:
pri_cc_act_promote_raw_a_status(ctrl, cc_record);
if (cc_record->party_a_status != CC_PARTY_A_AVAILABILITY_INVALID) {
/* Only received User A busy. */
pri_cc_act_raw_status_count_reset(ctrl, cc_record);
} else {
/* Did not get any responses. */
pri_cc_act_raw_status_count_increment(ctrl, cc_record);
if (cc_record->fsm.ptmp.party_a_status_count >= RAW_STATUS_COUNT_MAX) {
/* User A no longer present. */
pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */);
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
}
}
break;
case CC_EVENT_TIMEOUT_EXTENDED_T_CCBS1:
pri_cc_act_reset_a_status(ctrl, cc_record);
pri_cc_act_raw_status_count_reset(ctrl, cc_record);
break;
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_send_ccbs_erase(ctrl, cc_record, 1 /* t-CCBS2-timeout */);
pri_cc_act_stop_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_LINK_CANCEL:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */);
pri_cc_act_stop_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */);
pri_cc_act_stop_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTMP agent CC_STATE_B_AVAILABLE.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptmp_agent_b_avail(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_RECALL:
pri_cc_act_send_error_recall(ctrl, cc_record, ROSE_ERROR_CCBS_NotReadyForCall);
pri_cc_act_set_call_to_hangup(ctrl, cc_record, call);
break;
case CC_EVENT_A_STATUS:
pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record);
pri_cc_act_start_extended_t_ccbs1(ctrl, cc_record);
pri_cc_act_pass_up_status_rsp_a_indirect(ctrl, cc_record);
break;
case CC_EVENT_A_FREE:
pri_cc_act_send_remote_user_free(ctrl, cc_record);
pri_cc_act_set_raw_a_status_free(ctrl, cc_record);
//pri_cc_act_promote_raw_a_status(ctrl, cc_record);
//pri_cc_act_pass_up_a_status(ctrl, cc_record);
if (cc_record->fsm.ptmp.extended_t_ccbs1) {
pri_cc_act_pass_up_status_rsp_a(ctrl, cc_record);
}
pri_cc_act_stop_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record);
pri_cc_act_start_t_recall(ctrl, cc_record);
cc_record->state = CC_STATE_WAIT_CALLBACK;
break;
case CC_EVENT_A_BUSY:
pri_cc_act_add_raw_a_status_busy(ctrl, cc_record);
if (cc_record->fsm.ptmp.extended_t_ccbs1) {
pri_cc_act_pass_up_status_rsp_a(ctrl, cc_record);
}
break;
case CC_EVENT_TIMEOUT_T_CCBS1:
if (cc_record->fsm.ptmp.party_a_status_acc != CC_PARTY_A_AVAILABILITY_INVALID) {
/* Only received User A busy. */
pri_cc_act_raw_status_count_reset(ctrl, cc_record);
pri_cc_act_send_ccbs_b_free(ctrl, cc_record);
pri_cc_act_promote_raw_a_status(ctrl, cc_record);
pri_cc_act_pass_up_a_status(ctrl, cc_record);
/* Optimization due to flattening. */
//if (!pri_cc_get_t_ccbs1_status(cc_record))
{
pri_cc_act_reset_raw_a_status(ctrl, cc_record);
pri_cc_act_send_ccbs_status_request(ctrl, cc_record);
//pri_cc_act_start_t_ccbs1(ctrl, cc_record);
}
cc_record->state = CC_STATE_SUSPENDED;
} else {
/* Did not get any responses. */
pri_cc_act_raw_status_count_increment(ctrl, cc_record);
if (cc_record->fsm.ptmp.party_a_status_count >= RAW_STATUS_COUNT_MAX) {
/* User A no longer present. */
pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */);
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
}
//pri_cc_act_reset_raw_a_status(ctrl, cc_record);
pri_cc_act_send_ccbs_status_request(ctrl, cc_record);
//pri_cc_act_start_t_ccbs1(ctrl, cc_record);
}
break;
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_send_ccbs_erase(ctrl, cc_record, 1 /* t-CCBS2-timeout */);
pri_cc_act_stop_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_LINK_CANCEL:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */);
pri_cc_act_stop_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */);
pri_cc_act_stop_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTMP agent CC_STATE_SUSPENDED.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptmp_agent_suspended(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_RECALL:
pri_cc_act_send_error_recall(ctrl, cc_record, ROSE_ERROR_CCBS_NotReadyForCall);
pri_cc_act_set_call_to_hangup(ctrl, cc_record, call);
break;
case CC_EVENT_A_STATUS:
pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record);
pri_cc_act_start_extended_t_ccbs1(ctrl, cc_record);
pri_cc_act_pass_up_status_rsp_a_indirect(ctrl, cc_record);
break;
case CC_EVENT_A_FREE:
pri_cc_act_set_raw_a_status_free(ctrl, cc_record);
pri_cc_act_promote_raw_a_status(ctrl, cc_record);
pri_cc_act_pass_up_a_status(ctrl, cc_record);
if (cc_record->fsm.ptmp.extended_t_ccbs1) {
pri_cc_act_pass_up_status_rsp_a(ctrl, cc_record);
}
pri_cc_act_stop_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record);
pri_cc_act_reset_a_status(ctrl, cc_record);
pri_cc_act_raw_status_count_reset(ctrl, cc_record);
cc_record->state = CC_STATE_ACTIVATED;
break;
case CC_EVENT_A_BUSY:
pri_cc_act_add_raw_a_status_busy(ctrl, cc_record);
if (cc_record->fsm.ptmp.extended_t_ccbs1) {
pri_cc_act_pass_up_status_rsp_a(ctrl, cc_record);
}
break;
case CC_EVENT_TIMEOUT_T_CCBS1:
if (cc_record->fsm.ptmp.party_a_status_acc != CC_PARTY_A_AVAILABILITY_INVALID) {
/* Only received User A busy. */
pri_cc_act_raw_status_count_reset(ctrl, cc_record);
} else {
/* Did not get any responses. */
pri_cc_act_raw_status_count_increment(ctrl, cc_record);
if (cc_record->fsm.ptmp.party_a_status_count >= RAW_STATUS_COUNT_MAX) {
/* User A no longer present. */
pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */);
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
}
}
pri_cc_act_reset_raw_a_status(ctrl, cc_record);
pri_cc_act_send_ccbs_status_request(ctrl, cc_record);
//pri_cc_act_start_t_ccbs1(ctrl, cc_record);
break;
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_send_ccbs_erase(ctrl, cc_record, 1 /* t-CCBS2-timeout */);
pri_cc_act_stop_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_LINK_CANCEL:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */);
pri_cc_act_stop_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */);
pri_cc_act_stop_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_extended_t_ccbs1(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTMP agent CC_STATE_WAIT_CALLBACK.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptmp_agent_wait_callback(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_TIMEOUT_T_RECALL:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_send_ccbs_erase(ctrl, cc_record, 2 /* t-CCBS3-timeout */);
pri_cc_act_stop_t_recall(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_STOP_ALERTING:
/*
* If an earlier link can send us this event then we
* really should be configured for globalRecall like
* the earlier link.
*/
if (cc_record->option.recall_mode == 0 /* globalRecall */) {
pri_cc_act_send_ccbs_stop_alerting(ctrl, cc_record);
}
pri_cc_act_stop_t_recall(ctrl, cc_record);
pri_cc_act_reset_a_status(ctrl, cc_record);
pri_cc_act_raw_status_count_reset(ctrl, cc_record);
cc_record->state = CC_STATE_ACTIVATED;
break;
case CC_EVENT_RECALL:
pri_cc_act_pass_up_cc_call(ctrl, cc_record);
pri_cc_act_set_original_call_parameters(ctrl, call, cc_record);
if (cc_record->option.recall_mode == 0 /* globalRecall */) {
pri_cc_act_send_ccbs_stop_alerting(ctrl, cc_record);
}
pri_cc_act_stop_t_recall(ctrl, cc_record);
cc_record->state = CC_STATE_CALLBACK;
break;
case CC_EVENT_A_STATUS:
pri_cc_act_set_raw_a_status_free(ctrl, cc_record);
pri_cc_act_pass_up_status_rsp_a_indirect(ctrl, cc_record);
break;
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_send_ccbs_erase(ctrl, cc_record, 1 /* t-CCBS2-timeout */);
pri_cc_act_stop_t_recall(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_LINK_CANCEL:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */);
pri_cc_act_stop_t_recall(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */);
pri_cc_act_stop_t_recall(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTMP agent CC_STATE_CALLBACK.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptmp_agent_callback(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_RECALL:
pri_cc_act_send_error_recall(ctrl, cc_record, ROSE_ERROR_CCBS_AlreadyAccepted);
pri_cc_act_set_call_to_hangup(ctrl, cc_record, call);
break;
case CC_EVENT_A_STATUS:
pri_cc_act_set_raw_a_status_free(ctrl, cc_record);
pri_cc_act_pass_up_status_rsp_a_indirect(ctrl, cc_record);
break;
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_send_ccbs_erase(ctrl, cc_record, 1 /* t-CCBS2-timeout */);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_LINK_CANCEL:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_send_ccbs_erase(ctrl, cc_record, 0 /* normal-unspecified */);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTMP monitor CC_STATE_IDLE.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptmp_monitor_idle(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_AVAILABLE:
/*
* Before event is posted:
* Received CallInfoRetain
* Created cc_record
* Saved CallLinkageID
*/
pri_cc_act_pass_up_cc_available(ctrl, cc_record);
cc_record->state = CC_STATE_AVAILABLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_set_self_destruct(ctrl, cc_record);
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTMP monitor CC_STATE_AVAILABLE.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptmp_monitor_avail(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
/*
* The upper layer is responsible for canceling the CC available
* offering as a safeguard in case the network cable is disconnected.
* The timer should be set much longer than the network T_RETENTION
* timer so normally the CC records will be cleaned up by network
* activity.
*/
switch (event) {
case CC_EVENT_CC_REQUEST:
/* cc_record->is_ccnr is set before event posted. */
pri_cc_act_queue_cc_request(ctrl, cc_record, call);
//pri_cc_act_start_t_activate(ctrl, cc_record);
cc_record->state = CC_STATE_REQUESTED;
break;
case CC_EVENT_TIMEOUT_T_RETENTION:
/*
* Received EraseCallLinkageID
* T_RETENTION expired on the network side so we will pretend
* that it expired on our side.
*/
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTMP monitor CC_STATE_REQUESTED.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptmp_monitor_req(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_CC_REQUEST_ACCEPT:
/*
* Before event is posted:
* Received CCBSRequest/CCNRRequest response
* Saved CCBSReference
*/
pri_cc_act_release_link_id(ctrl, cc_record);
pri_cc_act_pass_up_cc_req_rsp_success(ctrl, cc_record);
pri_cc_act_stop_t_activate(ctrl, cc_record);
/*
* Start T_CCBS2 or T_CCNR2 depending upon CC mode.
* For PTMP TE mode these timers are not defined. However,
* we will use them anyway to protect our resources from leaks
* caused by the network cable being disconnected. These
* timers should be set much longer than the network
* so normally the CC records will be cleaned up by network
* activity.
*/
pri_cc_act_start_t_supervision(ctrl, cc_record);
cc_record->state = CC_STATE_ACTIVATED;
break;
case CC_EVENT_CC_REQUEST_FAIL:
pri_cc_act_pass_up_cc_req_rsp_fail(ctrl, cc_record);
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_activate(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_TIMEOUT_T_ACTIVATE:
pri_cc_act_pass_up_cc_req_rsp_timeout(ctrl, cc_record);
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_activate(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_LINK_CANCEL:
/* Received CCBSErase */
/* Claim it was a timeout */
pri_cc_act_pass_up_cc_req_rsp_timeout(ctrl, cc_record);
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_activate(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
cc_record->state = CC_STATE_WAIT_DESTRUCTION;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTMP monitor CC_STATE_WAIT_DESTRUCTION.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptmp_monitor_wait_destruction(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
/* We were in the middle of a cc-request when we were asked to cancel. */
switch (event) {
case CC_EVENT_CC_REQUEST_ACCEPT:
/*
* Before event is posted:
* Received CCBSRequest/CCNRRequest response
* Saved CCBSReference
*/
pri_cc_act_send_cc_deactivate_req(ctrl, cc_record);
pri_cc_act_stop_t_activate(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CC_REQUEST_FAIL:
pri_cc_act_stop_t_activate(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_TIMEOUT_T_ACTIVATE:
pri_cc_act_stop_t_activate(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_LINK_CANCEL:
/* Received CCBSErase */
pri_cc_act_stop_t_activate(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTMP monitor CC_STATE_ACTIVATED.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptmp_monitor_activated(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_B_FREE:
/* Received CCBSBFree */
pri_cc_act_pass_up_b_free(ctrl, cc_record);
break;
case CC_EVENT_REMOTE_USER_FREE:
/* Received CCBSRemoteUserFree */
pri_cc_act_pass_up_remote_user_free(ctrl, cc_record);
cc_record->state = CC_STATE_WAIT_CALLBACK;
break;
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_send_cc_deactivate_req(ctrl, cc_record);
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_LINK_CANCEL:
/* Received CCBSErase */
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_send_cc_deactivate_req(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTMP monitor CC_STATE_WAIT_CALLBACK.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptmp_monitor_wait_callback(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_STOP_ALERTING:
pri_cc_act_pass_up_stop_alerting(ctrl, cc_record);
cc_record->state = CC_STATE_ACTIVATED;
break;
case CC_EVENT_RECALL:
/* The original call parameters have already been set. */
pri_cc_act_queue_setup_recall(ctrl, cc_record, call);
cc_record->state = CC_STATE_CALLBACK;
break;
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_send_cc_deactivate_req(ctrl, cc_record);
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_LINK_CANCEL:
/* Received CCBSErase */
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_send_cc_deactivate_req(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTMP monitor CC_STATE_CALLBACK.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptmp_monitor_callback(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
/*
* We are waiting for the CC records to be torn down because
* CC is complete.
* This state is mainly to block CC_EVENT_STOP_ALERTING since
* we are the one doing the CC recall so we do not need to stop
* alerting.
*/
switch (event) {
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_send_cc_deactivate_req(ctrl, cc_record);
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_LINK_CANCEL:
/* Received CCBSErase */
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_send_cc_deactivate_req(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTP agent CC_STATE_IDLE.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptp_agent_idle(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_AVAILABLE:
cc_record->state = CC_STATE_PENDING_AVAILABLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_set_self_destruct(ctrl, cc_record);
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTP agent CC_STATE_PENDING_AVAILABLE.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptp_agent_pend_avail(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_MSG_ALERTING:
pri_cc_act_send_cc_available(ctrl, cc_record, call, Q931_ALERTING);
cc_record->state = CC_STATE_AVAILABLE;
break;
case CC_EVENT_MSG_DISCONNECT:
pri_cc_act_send_cc_available(ctrl, cc_record, call, Q931_DISCONNECT);
cc_record->state = CC_STATE_AVAILABLE;
break;
case CC_EVENT_INTERNAL_CLEARING:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTP agent CC_STATE_AVAILABLE.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptp_agent_avail(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
/*
* For PTP mode the T_RETENTION timer is not defined. However,
* we will use it anyway in this state to protect our resources
* from leaks caused by user A not requesting CC. This timer
* should be set much longer than the PTMP network link to
* allow for variations in user A's CC offer timer.
*/
switch (event) {
case CC_EVENT_MSG_RELEASE:
case CC_EVENT_MSG_RELEASE_COMPLETE:
pri_cc_act_stop_t_retention(ctrl, cc_record);
pri_cc_act_start_t_retention(ctrl, cc_record);
break;
case CC_EVENT_CC_REQUEST:
pri_cc_act_pass_up_cc_request(ctrl, cc_record);
pri_cc_act_stop_t_retention(ctrl, cc_record);
cc_record->state = CC_STATE_REQUESTED;
break;
case CC_EVENT_INTERNAL_CLEARING:
pri_cc_act_stop_t_retention(ctrl, cc_record);
pri_cc_act_start_t_retention(ctrl, cc_record);
break;
case CC_EVENT_TIMEOUT_T_RETENTION:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_retention(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_stop_t_retention(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTP agent CC_STATE_REQUESTED.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptp_agent_req(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_CC_REQUEST_ACCEPT:
/* Start T_CCBS5/T_CCNR5 depending upon CC mode. */
pri_cc_act_start_t_supervision(ctrl, cc_record);
pri_cc_act_reset_a_status(ctrl, cc_record);
cc_record->state = CC_STATE_ACTIVATED;
break;
case CC_EVENT_SIGNALING_GONE:
/* Signaling link cleared. */
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTP agent CC_STATE_ACTIVATED.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptp_agent_activated(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_REMOTE_USER_FREE:
pri_cc_act_pass_up_a_status_indirect(ctrl, cc_record);
if (cc_record->party_a_status == CC_PARTY_A_AVAILABILITY_BUSY) {
cc_record->state = CC_STATE_SUSPENDED;
} else {
pri_cc_act_send_remote_user_free(ctrl, cc_record);
cc_record->state = CC_STATE_WAIT_CALLBACK;
}
break;
case CC_EVENT_SUSPEND:
/* Received CCBS_T_Suspend */
pri_cc_act_set_a_status_busy(ctrl, cc_record);
break;
case CC_EVENT_RESUME:
/* Received CCBS_T_Resume */
pri_cc_act_reset_a_status(ctrl, cc_record);
break;
case CC_EVENT_RECALL:
/* Received CCBS_T_Call */
pri_cc_act_pass_up_cc_call(ctrl, cc_record);
pri_cc_act_set_original_call_parameters(ctrl, call, cc_record);
break;
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_SIGNALING_GONE:
/* Signaling link cleared. */
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTP agent CC_STATE_WAIT_CALLBACK.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptp_agent_wait_callback(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_SUSPEND:
/* Received CCBS_T_Suspend */
pri_cc_act_set_a_status_busy(ctrl, cc_record);
pri_cc_act_pass_up_a_status(ctrl, cc_record);
cc_record->state = CC_STATE_SUSPENDED;
break;
case CC_EVENT_RECALL:
/* Received CCBS_T_Call */
pri_cc_act_pass_up_cc_call(ctrl, cc_record);
pri_cc_act_set_original_call_parameters(ctrl, call, cc_record);
break;
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_SIGNALING_GONE:
/* Signaling link cleared. */
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTP agent CC_STATE_SUSPENDED.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptp_agent_suspended(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_RESUME:
/* Received CCBS_T_Resume */
pri_cc_act_set_a_status_free(ctrl, cc_record);
pri_cc_act_pass_up_a_status(ctrl, cc_record);
pri_cc_act_reset_a_status(ctrl, cc_record);
cc_record->state = CC_STATE_ACTIVATED;
break;
case CC_EVENT_RECALL:
/* Received CCBS_T_Call */
pri_cc_act_pass_up_cc_call(ctrl, cc_record);
pri_cc_act_set_original_call_parameters(ctrl, call, cc_record);
break;
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_SIGNALING_GONE:
/* Signaling link cleared. */
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTP monitor CC_STATE_IDLE.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptp_monitor_idle(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_AVAILABLE:
/* Received CCBS-T-Aailable */
pri_cc_act_pass_up_cc_available(ctrl, cc_record);
cc_record->state = CC_STATE_AVAILABLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_set_self_destruct(ctrl, cc_record);
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTP monitor CC_STATE_AVAILABLE.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptp_monitor_avail(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
/* The upper layer is responsible for canceling the CC available offering. */
switch (event) {
case CC_EVENT_CC_REQUEST:
/*
* Before event is posted:
* cc_record->is_ccnr is set.
* The signaling connection call record is created.
*/
pri_cc_act_queue_cc_request(ctrl, cc_record, call);
/*
* For PTP mode the T_ACTIVATE timer is not defined. However,
* we will use it to protect our resources from leaks caused
* by the network cable being disconnected.
* This timer should be set longer than normal so the
* CC records will normally be cleaned up by network activity.
*/
//pri_cc_act_start_t_activate(ctrl, cc_record);
cc_record->state = CC_STATE_REQUESTED;
break;
case CC_EVENT_CANCEL:
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTP monitor CC_STATE_REQUESTED.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptp_monitor_req(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_CC_REQUEST_ACCEPT:
/*
* Received CCBS-T-Request/CCNR-T-Request response
* Before event is posted:
* Negotiated CC retention setting saved
*/
pri_cc_act_pass_up_cc_req_rsp_success(ctrl, cc_record);
pri_cc_act_stop_t_activate(ctrl, cc_record);
/* Start T_CCBS6/T_CCNR6 depending upon CC mode. */
pri_cc_act_start_t_supervision(ctrl, cc_record);
pri_cc_act_reset_a_status(ctrl, cc_record);
cc_record->state = CC_STATE_ACTIVATED;
break;
case CC_EVENT_CC_REQUEST_FAIL:
pri_cc_act_pass_up_cc_req_rsp_fail(ctrl, cc_record);
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
/*
* If this request fail comes in with the RELEASE_COMPLETE
* message then the post action will never get a chance to
* run. It will be aborted because the CC_EVENT_SIGNALING_GONE
* will be processed first.
*/
pri_cc_act_post_hangup_signaling(ctrl, cc_record);
pri_cc_act_stop_t_activate(ctrl, cc_record);
cc_record->state = CC_STATE_WAIT_DESTRUCTION;
break;
case CC_EVENT_TIMEOUT_T_ACTIVATE:
pri_cc_act_pass_up_cc_req_rsp_timeout(ctrl, cc_record);
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_stop_t_activate(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_SIGNALING_GONE:
/* Claim it was a timeout */
pri_cc_act_pass_up_cc_req_rsp_timeout(ctrl, cc_record);
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_activate(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_stop_t_activate(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTP monitor CC_STATE_WAIT_DESTRUCTION.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptp_monitor_wait_destruction(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
/*
* Delayed disconnect of the signaling link to allow subcmd events
* from the signaling link to be passed up.
*/
switch (event) {
case CC_EVENT_SIGNALING_GONE:
/* Signaling link cleared. */
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_HANGUP_SIGNALING:
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTP monitor CC_STATE_ACTIVATED.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptp_monitor_activated(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_REMOTE_USER_FREE:
/* Received CCBS_T_RemoteUserFree */
pri_cc_act_pass_up_remote_user_free(ctrl, cc_record);
if (cc_record->party_a_status == CC_PARTY_A_AVAILABILITY_BUSY) {
pri_cc_act_send_cc_suspend(ctrl, cc_record);
cc_record->state = CC_STATE_SUSPENDED;
} else {
cc_record->state = CC_STATE_WAIT_CALLBACK;
}
break;
case CC_EVENT_SUSPEND:
pri_cc_act_set_a_status_busy(ctrl, cc_record);
break;
case CC_EVENT_RESUME:
pri_cc_act_reset_a_status(ctrl, cc_record);
break;
case CC_EVENT_RECALL:
/* The original call parameters have already been set. */
pri_cc_act_queue_setup_recall(ctrl, cc_record, call);
break;
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_SIGNALING_GONE:
/* Signaling link cleared. */
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTP monitor CC_STATE_WAIT_CALLBACK.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptp_monitor_wait_callback(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_SUSPEND:
pri_cc_act_send_cc_suspend(ctrl, cc_record);
cc_record->state = CC_STATE_SUSPENDED;
break;
case CC_EVENT_RECALL:
/* The original call parameters have already been set. */
pri_cc_act_queue_setup_recall(ctrl, cc_record, call);
break;
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_SIGNALING_GONE:
/* Signaling link cleared. */
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM PTP monitor CC_STATE_SUSPENDED.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_ptp_monitor_suspended(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_RESUME:
pri_cc_act_send_cc_resume(ctrl, cc_record);
pri_cc_act_reset_a_status(ctrl, cc_record);
cc_record->state = CC_STATE_ACTIVATED;
break;
case CC_EVENT_RECALL:
/* The original call parameters have already been set. */
pri_cc_act_queue_setup_recall(ctrl, cc_record, call);
break;
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_SIGNALING_GONE:
/* Signaling link cleared. */
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM Q.SIG agent CC_STATE_IDLE.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_qsig_agent_idle(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_AVAILABLE:
cc_record->state = CC_STATE_AVAILABLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_set_self_destruct(ctrl, cc_record);
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM Q.SIG agent CC_STATE_AVAILABLE.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_qsig_agent_avail(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
/*
* For Q.SIG mode the T_RETENTION timer is not defined. However,
* we will use it anyway in this state to protect our resources
* from leaks caused by user A not requesting CC. This timer
* should be set much longer than the PTMP network link to
* allow for variations in user A's CC offer timer.
*/
switch (event) {
case CC_EVENT_MSG_RELEASE:
case CC_EVENT_MSG_RELEASE_COMPLETE:
pri_cc_act_stop_t_retention(ctrl, cc_record);
pri_cc_act_start_t_retention(ctrl, cc_record);
break;
case CC_EVENT_CC_REQUEST:
pri_cc_act_pass_up_cc_request(ctrl, cc_record);
/* Send Q931_CALL_PROCEEDING message on signaling link. */
pri_cc_act_send_call_proceeding(ctrl, cc_record);
pri_cc_act_stop_t_retention(ctrl, cc_record);
cc_record->state = CC_STATE_REQUESTED;
break;
case CC_EVENT_INTERNAL_CLEARING:
pri_cc_act_stop_t_retention(ctrl, cc_record);
pri_cc_act_start_t_retention(ctrl, cc_record);
break;
case CC_EVENT_TIMEOUT_T_RETENTION:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_retention(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_stop_t_retention(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM Q.SIG agent CC_STATE_REQUESTED.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_qsig_agent_req(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_CC_REQUEST_ACCEPT:
/* Start QSIG_CCBS_T2/QSIG_CCNR_T2 depending upon CC mode. */
pri_cc_act_start_t_supervision(ctrl, cc_record);
cc_record->state = CC_STATE_ACTIVATED;
break;
case CC_EVENT_SIGNALING_GONE:
/* Signaling link cleared. */
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM Q.SIG agent CC_STATE_WAIT_DESTRUCTION.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_qsig_agent_wait_destruction(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
/*
* Delayed disconnect of the signaling link to allow subcmd events
* from the signaling link to be passed up.
*/
switch (event) {
case CC_EVENT_SIGNALING_GONE:
/* Signaling link cleared. */
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_HANGUP_SIGNALING:
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM Q.SIG agent CC_STATE_ACTIVATED.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_qsig_agent_activated(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_REMOTE_USER_FREE:
/* Send ccExecPossible in FACILITY or SETUP. */
pri_cc_act_send_remote_user_free(ctrl, cc_record);
cc_record->state = CC_STATE_WAIT_CALLBACK;
break;
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_send_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_SIGNALING_GONE:
/* Signaling link cleared. */
pri_cc_act_disassociate_signaling_link(ctrl, cc_record);
break;
case CC_EVENT_LINK_CANCEL:
/* Received ccCancel */
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_post_hangup_signaling(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
cc_record->state = CC_STATE_WAIT_DESTRUCTION;
break;
case CC_EVENT_CANCEL:
pri_cc_act_send_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM Q.SIG agent CC_STATE_WAIT_CALLBACK.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_qsig_agent_wait_callback(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_SUSPEND:
/* Received ccSuspend */
pri_cc_act_set_a_status_busy(ctrl, cc_record);
pri_cc_act_pass_up_a_status(ctrl, cc_record);
cc_record->state = CC_STATE_SUSPENDED;
break;
case CC_EVENT_RECALL:
/* Received ccRingout */
pri_cc_act_pass_up_cc_call(ctrl, cc_record);
pri_cc_act_set_original_call_parameters(ctrl, call, cc_record);
break;
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_send_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_SIGNALING_GONE:
/* Signaling link cleared. */
pri_cc_act_disassociate_signaling_link(ctrl, cc_record);
break;
case CC_EVENT_LINK_CANCEL:
/* Received ccCancel */
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_post_hangup_signaling(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
cc_record->state = CC_STATE_WAIT_DESTRUCTION;
break;
case CC_EVENT_CANCEL:
pri_cc_act_send_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM Q.SIG agent CC_STATE_SUSPENDED.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_qsig_agent_suspended(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_RESUME:
/* Received ccResume */
pri_cc_act_set_a_status_free(ctrl, cc_record);
pri_cc_act_pass_up_a_status(ctrl, cc_record);
cc_record->state = CC_STATE_ACTIVATED;
break;
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_send_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_SIGNALING_GONE:
/* Signaling link cleared. */
pri_cc_act_disassociate_signaling_link(ctrl, cc_record);
break;
case CC_EVENT_LINK_CANCEL:
/* Received ccCancel */
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_post_hangup_signaling(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
cc_record->state = CC_STATE_WAIT_DESTRUCTION;
break;
case CC_EVENT_CANCEL:
pri_cc_act_send_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM Q.SIG monitor CC_STATE_IDLE.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_qsig_monitor_idle(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_AVAILABLE:
/*
* LibPRI will determine if CC will be offered based upon
* if it is even possible.
* Essentially:
* 1) The call must not have been redirected in this link's
* setup.
* 2) Received an ALERTING or received a
* DISCONNECT(busy/congestion).
*/
pri_cc_act_pass_up_cc_available(ctrl, cc_record);
cc_record->state = CC_STATE_AVAILABLE;
break;
case CC_EVENT_CANCEL:
pri_cc_act_set_self_destruct(ctrl, cc_record);
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM Q.SIG monitor CC_STATE_AVAILABLE.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_qsig_monitor_avail(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
/* The upper layer is responsible for canceling the CC available offering. */
switch (event) {
case CC_EVENT_CC_REQUEST:
/*
* Before event is posted:
* cc_record->is_ccnr is set.
* The signaling connection call record is created.
*/
pri_cc_act_queue_cc_request(ctrl, cc_record, call);
/* Start QSIG_CC_T1. */
//pri_cc_act_start_t_activate(ctrl, cc_record);
cc_record->state = CC_STATE_REQUESTED;
break;
case CC_EVENT_CANCEL:
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM Q.SIG monitor CC_STATE_REQUESTED.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_qsig_monitor_req(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_CC_REQUEST_ACCEPT:
/*
* Received ccbsRequest/ccnrRequest response
* Before event is posted:
* Negotiated CC retention setting saved
* Negotiated signaling link retention setting saved
*/
pri_cc_act_stop_t_activate(ctrl, cc_record);
if (cc_record->fsm.qsig.msgtype == Q931_RELEASE) {
pri_cc_act_disassociate_signaling_link(ctrl, cc_record);
if (cc_record->option.retain_signaling_link) {
/*
* The far end did not honor the
* signaling link retention requirement.
* ECMA-186 Section 6.5.2.2.1
*/
pri_cc_act_pass_up_cc_req_rsp_timeout(ctrl, cc_record);
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_send_cc_cancel(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
}
}
pri_cc_act_pass_up_cc_req_rsp_success(ctrl, cc_record);
/* Start QSIG_CCBS_T2/QSIG_CCNR_T2 depending upon CC mode. */
pri_cc_act_start_t_supervision(ctrl, cc_record);
pri_cc_act_reset_a_status(ctrl, cc_record);
cc_record->state = CC_STATE_ACTIVATED;
break;
case CC_EVENT_CC_REQUEST_FAIL:
pri_cc_act_stop_t_activate(ctrl, cc_record);
pri_cc_act_pass_up_cc_req_rsp_fail(ctrl, cc_record);
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
/*
* If this request fail comes in with the RELEASE message
* then the post action will never get a chance to run.
* It will be aborted because the CC_EVENT_SIGNALING_GONE
* will be processed first.
*/
pri_cc_act_post_hangup_signaling(ctrl, cc_record);
cc_record->state = CC_STATE_WAIT_DESTRUCTION;
break;
case CC_EVENT_TIMEOUT_T_ACTIVATE:
pri_cc_act_stop_t_activate(ctrl, cc_record);
pri_cc_act_pass_up_cc_req_rsp_timeout(ctrl, cc_record);
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_SIGNALING_GONE:
pri_cc_act_stop_t_activate(ctrl, cc_record);
/* Claim it was a timeout */
pri_cc_act_pass_up_cc_req_rsp_timeout(ctrl, cc_record);
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CANCEL:
cc_record->state = CC_STATE_WAIT_DESTRUCTION;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM Q.SIG monitor CC_STATE_WAIT_DESTRUCTION.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_qsig_monitor_wait_destruction(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
/*
* Delayed disconnect of the signaling link to allow subcmd events
* from the signaling link to be passed up.
*/
switch (event) {
case CC_EVENT_CC_REQUEST_ACCEPT:
/*
* Received ccbsRequest/ccnrRequest response
* Before event is posted:
* Negotiated CC retention setting saved
* Negotiated signaling link retention setting saved
*/
pri_cc_act_stop_t_activate(ctrl, cc_record);
if (cc_record->fsm.qsig.msgtype == Q931_RELEASE) {
pri_cc_act_disassociate_signaling_link(ctrl, cc_record);
}
pri_cc_act_send_cc_cancel(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_CC_REQUEST_FAIL:
pri_cc_act_stop_t_activate(ctrl, cc_record);
/*
* If this request fail comes in with the RELEASE message
* then the post action will never get a chance to run.
* It will be aborted because the CC_EVENT_SIGNALING_GONE
* will be processed first.
*/
pri_cc_act_post_hangup_signaling(ctrl, cc_record);
break;
case CC_EVENT_TIMEOUT_T_ACTIVATE:
pri_cc_act_stop_t_activate(ctrl, cc_record);
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_SIGNALING_GONE:
/* Signaling link cleared. */
pri_cc_act_stop_t_activate(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_HANGUP_SIGNALING:
//pri_cc_act_stop_t_activate(ctrl, cc_record);
pri_cc_act_hangup_signaling_link(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM Q.SIG monitor CC_STATE_ACTIVATED.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_qsig_monitor_activated(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_REMOTE_USER_FREE:
/* Received ccExecPossible */
pri_cc_act_pass_up_remote_user_free(ctrl, cc_record);
/*
* ECMA-186 Section 6.5.2.1.7
* Implied switch to retain-signaling-link.
*/
pri_cc_act_set_retain_signaling_link(ctrl, cc_record);
if (cc_record->fsm.qsig.msgtype == Q931_SETUP) {
/* Send Q931_CALL_PROCEEDING message on signaling link. */
pri_cc_act_send_call_proceeding(ctrl, cc_record);
}
if (cc_record->party_a_status == CC_PARTY_A_AVAILABILITY_BUSY) {
/*
* The ccSuspend will be sent in a FACILITY or CONNECT
* message depending upon the CIS call state.
*/
pri_cc_act_send_cc_suspend(ctrl, cc_record);
cc_record->state = CC_STATE_SUSPENDED;
} else {
/* Start QSIG_CC_T3 */
pri_cc_act_start_t_recall(ctrl, cc_record);
cc_record->state = CC_STATE_WAIT_CALLBACK;
}
break;
case CC_EVENT_SUSPEND:
pri_cc_act_set_a_status_busy(ctrl, cc_record);
break;
case CC_EVENT_RESUME:
pri_cc_act_reset_a_status(ctrl, cc_record);
break;
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_send_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_SIGNALING_GONE:
/* Signaling link cleared. */
pri_cc_act_disassociate_signaling_link(ctrl, cc_record);
break;
case CC_EVENT_LINK_CANCEL:
/* Received ccCancel */
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_post_hangup_signaling(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
cc_record->state = CC_STATE_WAIT_DESTRUCTION;
break;
case CC_EVENT_CANCEL:
pri_cc_act_send_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM Q.SIG monitor CC_STATE_WAIT_CALLBACK.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_qsig_monitor_wait_callback(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_RECALL:
/* The original call parameters have already been set. */
pri_cc_act_queue_setup_recall(ctrl, cc_record, call);
pri_cc_act_stop_t_recall(ctrl, cc_record);
cc_record->state = CC_STATE_CALLBACK;
break;
case CC_EVENT_SUSPEND:
pri_cc_act_stop_t_recall(ctrl, cc_record);
/*
* The ccSuspend will be sent in a FACILITY or CONNECT
* message depending upon the CIS call state.
*/
pri_cc_act_send_cc_suspend(ctrl, cc_record);
cc_record->state = CC_STATE_SUSPENDED;
break;
case CC_EVENT_TIMEOUT_T_RECALL:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_send_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_recall(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_send_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_recall(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_SIGNALING_GONE:
/* Signaling link cleared. */
pri_cc_act_disassociate_signaling_link(ctrl, cc_record);
break;
case CC_EVENT_LINK_CANCEL:
/* Received ccCancel */
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_post_hangup_signaling(ctrl, cc_record);
pri_cc_act_stop_t_recall(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
cc_record->state = CC_STATE_WAIT_DESTRUCTION;
break;
case CC_EVENT_CANCEL:
pri_cc_act_send_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_recall(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM Q.SIG monitor CC_STATE_CALLBACK.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_qsig_monitor_callback(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_send_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_SIGNALING_GONE:
/* Signaling link cleared. */
pri_cc_act_disassociate_signaling_link(ctrl, cc_record);
break;
case CC_EVENT_LINK_CANCEL:
/* Received ccCancel */
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_post_hangup_signaling(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
cc_record->state = CC_STATE_WAIT_DESTRUCTION;
break;
case CC_EVENT_CANCEL:
pri_cc_act_send_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM Q.SIG monitor CC_STATE_SUSPENDED.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
static void pri_cc_fsm_qsig_monitor_suspended(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
switch (event) {
case CC_EVENT_RESUME:
pri_cc_act_send_cc_resume(ctrl, cc_record);
pri_cc_act_reset_a_status(ctrl, cc_record);
cc_record->state = CC_STATE_ACTIVATED;
break;
case CC_EVENT_TIMEOUT_T_SUPERVISION:
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_send_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
case CC_EVENT_SIGNALING_GONE:
/* Signaling link cleared. */
pri_cc_act_disassociate_signaling_link(ctrl, cc_record);
break;
case CC_EVENT_LINK_CANCEL:
/* Received ccCancel */
pri_cc_act_pass_up_cc_cancel(ctrl, cc_record);
pri_cc_act_post_hangup_signaling(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
cc_record->state = CC_STATE_WAIT_DESTRUCTION;
break;
case CC_EVENT_CANCEL:
pri_cc_act_send_cc_cancel(ctrl, cc_record);
pri_cc_act_stop_t_supervision(ctrl, cc_record);
pri_cc_act_set_self_destruct(ctrl, cc_record);
cc_record->state = CC_STATE_IDLE;
break;
default:
break;
}
}
/*!
* \internal
* \brief CC FSM state function type.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \return Nothing
*/
typedef void (*pri_cc_fsm_state)(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event);
/*! CC FSM PTMP agent state table. */
static const pri_cc_fsm_state pri_cc_fsm_ptmp_agent[CC_STATE_NUM] = {
/* *INDENT-OFF* */
[CC_STATE_IDLE] = pri_cc_fsm_ptmp_agent_idle,
[CC_STATE_PENDING_AVAILABLE] = pri_cc_fsm_ptmp_agent_pend_avail,
[CC_STATE_AVAILABLE] = pri_cc_fsm_ptmp_agent_avail,
[CC_STATE_REQUESTED] = pri_cc_fsm_ptmp_agent_req,
[CC_STATE_ACTIVATED] = pri_cc_fsm_ptmp_agent_activated,
[CC_STATE_B_AVAILABLE] = pri_cc_fsm_ptmp_agent_b_avail,
[CC_STATE_SUSPENDED] = pri_cc_fsm_ptmp_agent_suspended,
[CC_STATE_WAIT_CALLBACK] = pri_cc_fsm_ptmp_agent_wait_callback,
[CC_STATE_CALLBACK] = pri_cc_fsm_ptmp_agent_callback,
/* *INDENT-ON* */
};
/*! CC FSM PTMP monitor state table. */
static const pri_cc_fsm_state pri_cc_fsm_ptmp_monitor[CC_STATE_NUM] = {
/* *INDENT-OFF* */
[CC_STATE_IDLE] = pri_cc_fsm_ptmp_monitor_idle,
[CC_STATE_AVAILABLE] = pri_cc_fsm_ptmp_monitor_avail,
[CC_STATE_REQUESTED] = pri_cc_fsm_ptmp_monitor_req,
[CC_STATE_WAIT_DESTRUCTION] = pri_cc_fsm_ptmp_monitor_wait_destruction,
[CC_STATE_ACTIVATED] = pri_cc_fsm_ptmp_monitor_activated,
[CC_STATE_WAIT_CALLBACK] = pri_cc_fsm_ptmp_monitor_wait_callback,
[CC_STATE_CALLBACK] = pri_cc_fsm_ptmp_monitor_callback,
/* *INDENT-ON* */
};
/*! CC FSM PTP agent state table. */
static const pri_cc_fsm_state pri_cc_fsm_ptp_agent[CC_STATE_NUM] = {
/* *INDENT-OFF* */
[CC_STATE_IDLE] = pri_cc_fsm_ptp_agent_idle,
[CC_STATE_PENDING_AVAILABLE] = pri_cc_fsm_ptp_agent_pend_avail,
[CC_STATE_AVAILABLE] = pri_cc_fsm_ptp_agent_avail,
[CC_STATE_REQUESTED] = pri_cc_fsm_ptp_agent_req,
[CC_STATE_ACTIVATED] = pri_cc_fsm_ptp_agent_activated,
[CC_STATE_WAIT_CALLBACK] = pri_cc_fsm_ptp_agent_wait_callback,
[CC_STATE_SUSPENDED] = pri_cc_fsm_ptp_agent_suspended,
/* *INDENT-ON* */
};
/*! CC FSM PTP monitor state table. */
static const pri_cc_fsm_state pri_cc_fsm_ptp_monitor[CC_STATE_NUM] = {
/* *INDENT-OFF* */
[CC_STATE_IDLE] = pri_cc_fsm_ptp_monitor_idle,
[CC_STATE_AVAILABLE] = pri_cc_fsm_ptp_monitor_avail,
[CC_STATE_REQUESTED] = pri_cc_fsm_ptp_monitor_req,
[CC_STATE_WAIT_DESTRUCTION] = pri_cc_fsm_ptp_monitor_wait_destruction,
[CC_STATE_ACTIVATED] = pri_cc_fsm_ptp_monitor_activated,
[CC_STATE_WAIT_CALLBACK] = pri_cc_fsm_ptp_monitor_wait_callback,
[CC_STATE_SUSPENDED] = pri_cc_fsm_ptp_monitor_suspended,
/* *INDENT-ON* */
};
/*! CC FSM Q.SIG agent state table. */
static const pri_cc_fsm_state pri_cc_fsm_qsig_agent[CC_STATE_NUM] = {
/* *INDENT-OFF* */
[CC_STATE_IDLE] = pri_cc_fsm_qsig_agent_idle,
[CC_STATE_AVAILABLE] = pri_cc_fsm_qsig_agent_avail,
[CC_STATE_REQUESTED] = pri_cc_fsm_qsig_agent_req,
[CC_STATE_WAIT_DESTRUCTION] = pri_cc_fsm_qsig_agent_wait_destruction,
[CC_STATE_ACTIVATED] = pri_cc_fsm_qsig_agent_activated,
[CC_STATE_WAIT_CALLBACK] = pri_cc_fsm_qsig_agent_wait_callback,
[CC_STATE_SUSPENDED] = pri_cc_fsm_qsig_agent_suspended,
/* *INDENT-ON* */
};
/*! CC FSM Q.SIG monitor state table. */
static const pri_cc_fsm_state pri_cc_fsm_qsig_monitor[CC_STATE_NUM] = {
/* *INDENT-OFF* */
[CC_STATE_IDLE] = pri_cc_fsm_qsig_monitor_idle,
[CC_STATE_AVAILABLE] = pri_cc_fsm_qsig_monitor_avail,
[CC_STATE_REQUESTED] = pri_cc_fsm_qsig_monitor_req,
[CC_STATE_WAIT_DESTRUCTION] = pri_cc_fsm_qsig_monitor_wait_destruction,
[CC_STATE_ACTIVATED] = pri_cc_fsm_qsig_monitor_activated,
[CC_STATE_WAIT_CALLBACK] = pri_cc_fsm_qsig_monitor_wait_callback,
[CC_STATE_CALLBACK] = pri_cc_fsm_qsig_monitor_callback,
[CC_STATE_SUSPENDED] = pri_cc_fsm_qsig_monitor_suspended,
/* *INDENT-ON* */
};
/*!
* \brief Send an event to the cc state machine.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* (May be NULL if it is supposed to be the signaling connection
* for Q.SIG or PTP and it is not established yet.)
* \param cc_record Call completion record to process event.
* \param event Event to process.
*
* \retval nonzero if cc record destroyed because FSM completed.
*/
int pri_cc_event(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, enum CC_EVENTS event)
{
const pri_cc_fsm_state *cc_fsm;
enum CC_STATES orig_state;
switch (ctrl->switchtype) {
case PRI_SWITCH_QSIG:
if (cc_record->is_agent) {
cc_fsm = pri_cc_fsm_qsig_agent;
} else {
cc_fsm = pri_cc_fsm_qsig_monitor;
}
break;
case PRI_SWITCH_EUROISDN_E1:
case PRI_SWITCH_EUROISDN_T1:
if (PTMP_MODE(ctrl)) {
if (cc_record->is_agent) {
cc_fsm = pri_cc_fsm_ptmp_agent;
} else {
cc_fsm = pri_cc_fsm_ptmp_monitor;
}
} else {
if (cc_record->is_agent) {
cc_fsm = pri_cc_fsm_ptp_agent;
} else {
cc_fsm = pri_cc_fsm_ptp_monitor;
}
}
break;
default:
/* CC not supported on this switch type. */
cc_fsm = NULL;
break;
}
if (!cc_fsm) {
/* No FSM available. */
pri_cc_delete_record(ctrl, cc_record);
return 1;
}
orig_state = cc_record->state;
if (ctrl->debug & PRI_DEBUG_CC) {
pri_message(ctrl, "%ld CC-Event: %s in state %s\n", cc_record->record_id,
pri_cc_fsm_event_str(event), pri_cc_fsm_state_str(orig_state));
}
if (orig_state < CC_STATE_IDLE || CC_STATE_NUM <= orig_state || !cc_fsm[orig_state]) {
/* Programming error: State not implemented. */
pri_error(ctrl, "!! CC state not implemented: %s(%d)\n",
pri_cc_fsm_state_str(orig_state), orig_state);
return 0;
}
/* Execute the state. */
cc_fsm[orig_state](ctrl, call, cc_record, event);
if (ctrl->debug & PRI_DEBUG_CC) {
pri_message(ctrl, "%ld CC-Next-State: %s\n", cc_record->record_id,
(orig_state == cc_record->state)
? "$" : pri_cc_fsm_state_str(cc_record->state));
}
if (cc_record->fsm_complete) {
pri_cc_delete_record(ctrl, cc_record);
return 1;
} else {
return 0;
}
}
/*!
* \brief Indicate to the far end that CCBS/CCNR is available.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
*
* \details
* The CC available indication will go out with the next
* DISCONNECT(busy/congested)/ALERTING message.
*
* \retval cc_id on success for subsequent reference.
* \retval -1 on error.
*/
long pri_cc_available(struct pri *ctrl, q931_call *call)
{
struct pri_cc_record *cc_record;
long cc_id;
if (!ctrl || !pri_is_call_valid(ctrl, call)) {
return -1;
}
if (call->cc.record) {
/* This call is already associated with call completion. */
return -1;
}
cc_record = NULL;
switch (ctrl->switchtype) {
case PRI_SWITCH_QSIG:
cc_record = pri_cc_new_record(ctrl, call);
if (!cc_record) {
break;
}
/*
* Q.SIG has no message to send when CC is available.
* Q.SIG assumes CC is always available and is denied when
* requested if CC is not possible or allowed.
*/
cc_record->original_call = call;
cc_record->is_agent = 1;
break;
case PRI_SWITCH_EUROISDN_E1:
case PRI_SWITCH_EUROISDN_T1:
if (PTMP_MODE(ctrl)) {
int linkage_id;
if (!BRI_NT_PTMP(ctrl)) {
/*
* No CC agent protocol defined for this mode.
* i.e., A device acting like a phone cannot be a CC agent.
*/
break;
}
linkage_id = pri_cc_new_linkage_id(ctrl);
if (linkage_id == CC_PTMP_INVALID_ID) {
break;
}
cc_record = pri_cc_new_record(ctrl, call);
if (!cc_record) {
break;
}
cc_record->call_linkage_id = linkage_id;
cc_record->signaling = ctrl->link.dummy_call;
} else {
cc_record = pri_cc_new_record(ctrl, call);
if (!cc_record) {
break;
}
}
cc_record->original_call = call;
cc_record->is_agent = 1;
break;
default:
break;
}
call->cc.record = cc_record;
if (cc_record && !pri_cc_event(ctrl, call, cc_record, CC_EVENT_AVAILABLE)) {
cc_id = cc_record->record_id;
} else {
cc_id = -1;
}
return cc_id;
}
/*!
* \brief Determine if CC is available for Q.SIG outgoing call.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
*
* \return Nothing
*/
void pri_cc_qsig_determine_available(struct pri *ctrl, q931_call *call)
{
struct pri_cc_record *cc_record;
if (!call->cc.originated || call->cc.initially_redirected) {
/*
* The call is not suitable for us to consider CC:
* The call was not originated by us.
* The call was originally redirected.
*/
return;
}
if (!ctrl->cc_support) {
/*
* Blocking the cc-available event effectively
* disables call completion for outgoing calls.
*/
return;
}
if (call->cc.record) {
/* Already made available. */
return;
}
cc_record = pri_cc_new_record(ctrl, call);
if (!cc_record) {
return;
}
cc_record->original_call = call;
call->cc.record = cc_record;
pri_cc_event(ctrl, call, cc_record, CC_EVENT_AVAILABLE);
}
/*!
* \brief Request to activate CC.
*
* \param ctrl D channel controller.
* \param cc_id CC record ID to activate.
* \param mode Which CC mode to use CCBS(0)/CCNR(1)
*
* \note
* Will always get a reply from libpri. libpri will start a timer to guarantee
* that a reply will be passed back to the upper layer.
* \note
* If you cancel with pri_cc_cancel() you are indicating that you do not need
* the request reply and the cc_id will no longer be valid anyway.
* \note
* Allow for the possibility that the reply may come in before this
* function returns.
*
* \retval 0 on success.
* \retval -1 on error.
*/
int pri_cc_req(struct pri *ctrl, long cc_id, int mode)
{
struct pri_sr req;
q931_call *call;
struct pri_cc_record *cc_record;
if (!ctrl) {
return -1;
}
cc_record = pri_cc_find_by_id(ctrl, cc_id);
if (!cc_record) {
return -1;
}
if (cc_record->is_agent || cc_record->state != CC_STATE_AVAILABLE) {
/* CC is an agent or already requested. */
return -1;
}
/* Set the requested CC mode. */
cc_record->is_ccnr = mode ? 1 : 0;
switch (ctrl->switchtype) {
case PRI_SWITCH_QSIG:
if (cc_record->signaling) {
/* We should not have a signaling link at this point. */
return -1;
}
call = q931_new_call(ctrl);
if (!call) {
return -1;
}
/* Link the new call as the signaling link. */
cc_record->signaling = call;
call->cc.record = cc_record;
if (pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST)) {
/* Should not happen. */
q931_destroycall(ctrl, call);
break;
}
pri_sr_init(&req);
req.caller = cc_record->party_a;
req.called = cc_record->party_b;
//req.cis_auto_disconnect = 0;
req.cis_call = 1;
if (q931_setup(ctrl, call, &req)) {
/* Should not happen. */
q931_destroycall(ctrl, call);
return -1;
}
break;
case PRI_SWITCH_EUROISDN_E1:
case PRI_SWITCH_EUROISDN_T1:
if (PTMP_MODE(ctrl)) {
/* ETSI PTMP */
if (pri_cc_event(ctrl, cc_record->signaling, cc_record, CC_EVENT_CC_REQUEST)) {
/* Should not happen. */
break;
}
q931_facility(ctrl, cc_record->signaling);
break;
}
/* ETSI PTP */
if (cc_record->signaling) {
/* We should not have a signaling link at this point. */
return -1;
}
call = q931_new_call(ctrl);
if (!call) {
return -1;
}
cc_record->signaling = call;
call->cc.record = cc_record;
if (pri_cc_event(ctrl, call, cc_record, CC_EVENT_CC_REQUEST)) {
/* Should not happen. */
q931_destroycall(ctrl, call);
break;
}
if (q931_register(ctrl, call)) {
/* Should not happen. */
q931_destroycall(ctrl, call);
return -1;
}
break;
default:
return -1;
}
return 0;
}
/*!
* \internal
* \brief Encode a PTMP cc-request reply message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param operation CCBS/CCNR operation code.
* \param invoke_id Invoke id to put in error message response.
* \param recall_mode Configured PTMP recall mode.
* \param reference_id Active CC reference id.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_cc_etsi_ptmp_req_rsp(struct pri *ctrl, unsigned char *pos,
unsigned char *end, enum rose_operation operation, int invoke_id, int recall_mode,
int reference_id)
{
struct rose_msg_result msg;
pos = facility_encode_header(ctrl, pos, end, NULL);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = invoke_id;
msg.operation = operation;
/* CCBS/CCNR reply */
msg.args.etsi.CCBSRequest.recall_mode = recall_mode;
msg.args.etsi.CCBSRequest.ccbs_reference = reference_id;
pos = rose_encode_result(ctrl, pos, end, &msg);
return pos;
}
/*!
* \internal
* \brief Encode and queue PTMP a cc-request reply message.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param msgtype Q.931 message type to put facility ie in.
* \param operation CCBS/CCNR operation code.
* \param invoke_id Invoke id to put in error message response.
* \param recall_mode Configured PTMP recall mode.
* \param reference_id Active CC reference id.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int rose_cc_etsi_ptmp_req_rsp_encode(struct pri *ctrl, q931_call *call, int msgtype, enum rose_operation operation, int invoke_id, int recall_mode, int reference_id)
{
unsigned char buffer[256];
unsigned char *end;
end = enc_cc_etsi_ptmp_req_rsp(ctrl, buffer, buffer + sizeof(buffer), operation,
invoke_id, recall_mode, reference_id);
if (!end) {
return -1;
}
return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, NULL);
}
/*!
* \internal
* \brief Send the CC activation request result PTMP.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param operation CCBS/CCNR operation code.
* \param invoke_id Invoke id to put in error message response.
* \param recall_mode Configured PTMP recall mode.
* \param reference_id Active CC reference id.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int send_cc_etsi_ptmp_req_rsp(struct pri *ctrl, q931_call *call, enum rose_operation operation, int invoke_id, int recall_mode, int reference_id)
{
if (rose_cc_etsi_ptmp_req_rsp_encode(ctrl, call, Q931_FACILITY, operation, invoke_id,
recall_mode, reference_id)
|| q931_facility(ctrl, call)) {
pri_message(ctrl, "Could not schedule CC request result message.\n");
return -1;
}
return 0;
}
/*!
* \internal
* \brief Response to an incoming CC activation request PTMP.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
* \param status success(0)/timeout(1)/
* short_term_denial(2)/long_term_denial(3)/not_subscribed(4)/queue_full(5)
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int pri_cc_req_rsp_ptmp(struct pri *ctrl, struct pri_cc_record *cc_record, int status)
{
int fail;
switch (cc_record->response.invoke_operation) {
case ROSE_ETSI_CCBSRequest:
case ROSE_ETSI_CCNRRequest:
break;
default:
/* We no longer know how to send the response. Should not happen. */
return -1;
}
fail = 0;
if (status) {
enum rose_error_code code;
switch (status) {
default:
case 1:/* timeout */
case 2:/* short_term_denial */
code = ROSE_ERROR_CCBS_ShortTermDenial;
break;
case 3:/* long_term_denial */
code = ROSE_ERROR_CCBS_LongTermDenial;
break;
case 4:/* not_subscribed */
code = ROSE_ERROR_Gen_NotSubscribed;
break;
case 5:/* queue_full */
code = ROSE_ERROR_CCBS_OutgoingCCBSQueueFull;
break;
}
send_facility_error(ctrl, cc_record->response.signaling,
cc_record->response.invoke_id, code);
pri_cc_event(ctrl, cc_record->response.signaling, cc_record,
CC_EVENT_CANCEL);
} else {
/* Successful CC activation. */
if (send_cc_etsi_ptmp_req_rsp(ctrl, cc_record->response.signaling,
cc_record->response.invoke_operation, cc_record->response.invoke_id,
cc_record->option.recall_mode, cc_record->ccbs_reference_id)) {
fail = -1;
}
pri_cc_event(ctrl, cc_record->response.signaling, cc_record,
CC_EVENT_CC_REQUEST_ACCEPT);
}
return fail;
}
/*!
* \internal
* \brief Encode a PTP cc-request reply message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param cc_record Call completion record to process event.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_cc_etsi_ptp_req_rsp(struct pri *ctrl, unsigned char *pos,
unsigned char *end, struct pri_cc_record *cc_record)
{
struct rose_msg_result msg;
pos = facility_encode_header(ctrl, pos, end, NULL);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = cc_record->response.invoke_id;
msg.operation = cc_record->response.invoke_operation;
/* CCBS/CCNR reply */
//msg.args.etsi.CCBS_T_Request.retention_supported = 0;
pos = rose_encode_result(ctrl, pos, end, &msg);
return pos;
}
/*!
* \internal
* \brief Encode and queue PTP a cc-request reply message.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int rose_cc_etsi_ptp_req_rsp_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
{
unsigned char buffer[256];
unsigned char *end;
end = enc_cc_etsi_ptp_req_rsp(ctrl, buffer, buffer + sizeof(buffer), cc_record);
if (!end) {
return -1;
}
return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL);
}
/*!
* \internal
* \brief Send the CC activation request result PTP.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int send_cc_etsi_ptp_req_rsp(struct pri *ctrl, struct pri_cc_record *cc_record)
{
if (rose_cc_etsi_ptp_req_rsp_encode(ctrl, cc_record->signaling, cc_record)
|| q931_facility(ctrl, cc_record->signaling)) {
pri_message(ctrl, "Could not schedule CC request result message.\n");
return -1;
}
return 0;
}
/*!
* \internal
* \brief Response to an incoming CC activation request PTP.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
* \param status success(0)/timeout(1)/
* short_term_denial(2)/long_term_denial(3)/not_subscribed(4)/queue_full(5)
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int pri_cc_req_rsp_ptp(struct pri *ctrl, struct pri_cc_record *cc_record, int status)
{
int fail;
switch (cc_record->response.invoke_operation) {
case ROSE_ETSI_CCBS_T_Request:
case ROSE_ETSI_CCNR_T_Request:
break;
default:
/* We no longer know how to send the response. Should not happen. */
return -1;
}
if (!cc_record->signaling) {
return -1;
}
fail = 0;
if (status) {
enum rose_error_code code;
switch (status) {
default:
case 1:/* timeout */
case 5:/* queue_full */
case 2:/* short_term_denial */
code = ROSE_ERROR_CCBS_T_ShortTermDenial;
break;
case 3:/* long_term_denial */
code = ROSE_ERROR_CCBS_T_LongTermDenial;
break;
case 4:/* not_subscribed */
code = ROSE_ERROR_Gen_NotSubscribed;
break;
}
rose_error_msg_encode(ctrl, cc_record->signaling, Q931_ANY_MESSAGE,
cc_record->response.invoke_id, code);
pri_cc_event(ctrl, cc_record->signaling, cc_record, CC_EVENT_CANCEL);
} else {
/* Successful CC activation. */
if (send_cc_etsi_ptp_req_rsp(ctrl, cc_record)) {
fail = -1;
}
pri_cc_event(ctrl, cc_record->signaling, cc_record, CC_EVENT_CC_REQUEST_ACCEPT);
}
return fail;
}
/*!
* \internal
* \brief Encode a Q.SIG cc-request reply message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param cc_record Call completion record to process event.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *enc_cc_qsig_req_rsp(struct pri *ctrl, unsigned char *pos,
unsigned char *end, struct pri_cc_record *cc_record)
{
struct fac_extension_header header;
struct rose_msg_result msg;
memset(&header, 0, sizeof(header));
header.nfe_present = 1;
header.nfe.source_entity = 0; /* endPINX */
header.nfe.destination_entity = 0; /* endPINX */
header.interpretation_present = 1;
header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */
pos = facility_encode_header(ctrl, pos, end, &header);
if (!pos) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
msg.invoke_id = cc_record->response.invoke_id;
msg.operation = cc_record->response.invoke_operation;
/* CCBS/CCNR reply */
/* We do not support ccPathReserve */
msg.args.qsig.CcbsRequest.no_path_reservation = 1;
//msg.args.qsig.CcbsRequest.retain_service = 0;
pos = rose_encode_result(ctrl, pos, end, &msg);
return pos;
}
/*!
* \internal
* \brief Encode and queue Q.SIG a cc-request reply message.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int rose_cc_qsig_req_rsp_encode(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record)
{
unsigned char buffer[256];
unsigned char *end;
end = enc_cc_qsig_req_rsp(ctrl, buffer, buffer + sizeof(buffer), cc_record);
if (!end) {
return -1;
}
return pri_call_apdu_queue(call, Q931_ANY_MESSAGE, buffer, end - buffer, NULL);
}
/*!
* \internal
* \brief Send the CC activation request result Q.SIG.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int send_cc_qsig_req_rsp(struct pri *ctrl, struct pri_cc_record *cc_record)
{
struct q931_call *call;
int retval;
/* The cc-request response goes out on either a CONNECT or RELEASE message. */
call = cc_record->signaling;
retval = rose_cc_qsig_req_rsp_encode(ctrl, call, cc_record);
if (!retval) {
if (cc_record->option.retain_signaling_link) {
retval = q931_connect(ctrl, call, 0, 0);
} else {
pri_cc_disassociate_signaling_link(cc_record);
retval = pri_hangup(ctrl, call, -1);
}
}
if (retval) {
pri_message(ctrl, "Could not schedule CC request result message.\n");
return -1;
}
return 0;
}
/*!
* \internal
* \brief Response to an incoming CC activation request Q.SIG.
*
* \param ctrl D channel controller.
* \param cc_record Call completion record to process event.
* \param status success(0)/timeout(1)/
* short_term_denial(2)/long_term_denial(3)/not_subscribed(4)/queue_full(5)
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int pri_cc_req_rsp_qsig(struct pri *ctrl, struct pri_cc_record *cc_record, int status)
{
int fail;
switch (cc_record->response.invoke_operation) {
case ROSE_QSIG_CcbsRequest:
case ROSE_QSIG_CcnrRequest:
break;
default:
/* We no longer know how to send the response. Should not happen. */
return -1;
}
if (!cc_record->signaling) {
return -1;
}
fail = 0;
if (status) {
enum rose_error_code code;
switch (status) {
default:
case 1:/* timeout */
case 5:/* queue_full */
case 2:/* short_term_denial */
code = ROSE_ERROR_QSIG_ShortTermRejection;
break;
case 4:/* not_subscribed */
case 3:/* long_term_denial */
code = ROSE_ERROR_QSIG_LongTermRejection;
break;
}
rose_error_msg_encode(ctrl, cc_record->signaling, Q931_ANY_MESSAGE,
cc_record->response.invoke_id, code);
pri_cc_event(ctrl, cc_record->signaling, cc_record, CC_EVENT_CANCEL);
} else {
/* Successful CC activation. */
if (send_cc_qsig_req_rsp(ctrl, cc_record)) {
fail = -1;
}
pri_cc_event(ctrl, cc_record->signaling, cc_record, CC_EVENT_CC_REQUEST_ACCEPT);
}
return fail;
}
/*!
* \brief Response to an incoming CC activation request.
*
* \param ctrl D channel controller.
* \param cc_id CC record ID to activate.
* \param status success(0)/timeout(1)/
* short_term_denial(2)/long_term_denial(3)/not_subscribed(4)/queue_full(5)
*
* \note
* If the given status was failure, then the cc_id is no longer valid.
* \note
* The caller should cancel CC if error is returned.
*
* \retval 0 on success.
* \retval -1 on error.
*/
int pri_cc_req_rsp(struct pri *ctrl, long cc_id, int status)
{
struct pri_cc_record *cc_record;
int fail;
if (!ctrl) {
return -1;
}
cc_record = pri_cc_find_by_id(ctrl, cc_id);
if (!cc_record) {
return -1;
}
if (!cc_record->is_agent) {
/* CC is a monitor and does not send this response event. */
return -1;
}
fail = -1;
switch (ctrl->switchtype) {
case PRI_SWITCH_QSIG:
if (!pri_cc_req_rsp_qsig(ctrl, cc_record, status)) {
fail = 0;
}
break;
case PRI_SWITCH_EUROISDN_E1:
case PRI_SWITCH_EUROISDN_T1:
if (PTMP_MODE(ctrl)) {
if (!pri_cc_req_rsp_ptmp(ctrl, cc_record, status)) {
fail = 0;
}
} else {
if (!pri_cc_req_rsp_ptp(ctrl, cc_record, status)) {
fail = 0;
}
}
break;
default:
break;
}
return fail;
}
/*!
* \brief Indicate that the remote user (Party B) is free to call.
* The upper layer considers Party A is free.
*
* \param ctrl D channel controller.
* \param cc_id CC record ID to activate.
*
* \return Nothing
*/
void pri_cc_remote_user_free(struct pri *ctrl, long cc_id)
{
struct pri_cc_record *cc_record;
if (!ctrl) {
return;
}
cc_record = pri_cc_find_by_id(ctrl, cc_id);
if (!cc_record) {
return;
}
if (!cc_record->is_agent) {
/* CC is a monitor and does not send this event. */
return;
}
pri_cc_event(ctrl, cc_record->signaling, cc_record, CC_EVENT_REMOTE_USER_FREE);
}
/*!
* \brief Indicate that the remote user (Party B) is free to call.
* However, the upper layer considers Party A is busy.
*
* \details
* Party B is free, but Party A is considered busy for some reason.
* This is mainly due to the upper layer experiencing congestion.
* The upper layer will be monitoring Party A until it considers
* Party A free again.
*
* \param ctrl D channel controller.
* \param cc_id CC record ID to activate.
*
* \return Nothing
*/
void pri_cc_b_free(struct pri *ctrl, long cc_id)
{
struct pri_cc_record *cc_record;
if (!ctrl) {
return;
}
cc_record = pri_cc_find_by_id(ctrl, cc_id);
if (!cc_record) {
return;
}
if (!cc_record->is_agent) {
/* CC is a monitor and does not send this event. */
return;
}
pri_cc_event(ctrl, cc_record->signaling, cc_record, CC_EVENT_B_FREE);
}
/*!
* \brief Indicate that some other Party A has responed to the CC recall.
*
* \param ctrl D channel controller.
* \param cc_id CC record ID to activate.
*
* \return Nothing
*/
void pri_cc_stop_alerting(struct pri *ctrl, long cc_id)
{
struct pri_cc_record *cc_record;
if (!ctrl) {
return;
}
cc_record = pri_cc_find_by_id(ctrl, cc_id);
if (!cc_record) {
return;
}
if (!cc_record->is_agent) {
/* CC is a monitor and does not send this event. */
return;
}
pri_cc_event(ctrl, cc_record->signaling, cc_record, CC_EVENT_STOP_ALERTING);
}
/*!
* \brief Poll/Ping for the status of CC party A.
*
* \param ctrl D channel controller.
* \param cc_id CC record ID to activate.
*
* \note
* There could be zero, one, or more PRI_SUBCMD_CC_STATUS_REQ_RSP responses to
* the status request depending upon how many endpoints respond to the request.
* \note
* This is expected to be called only if there are two PTMP links between
* party A and the network. (e.g., A --> * --> PSTN)
*
* \return Nothing
*/
void pri_cc_status_req(struct pri *ctrl, long cc_id)
{
struct pri_cc_record *cc_record;
if (!ctrl) {
return;
}
cc_record = pri_cc_find_by_id(ctrl, cc_id);
if (!cc_record) {
return;
}
if (!cc_record->is_agent) {
/* CC is a monitor and does not send this event. */
return;
}
pri_cc_event(ctrl, cc_record->signaling, cc_record, CC_EVENT_A_STATUS);
}
/*!
* \internal
* \brief Encode and queue an CCBSStatusRequest result message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode CCBSStatusRequest.
* \param cc_record Call completion record to process event.
* \param is_free TRUE if the Party A status is available.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int rose_ccbs_status_request_rsp(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, int is_free)
{
unsigned char buffer[256];
unsigned char *end;
end =
enc_etsi_ptmp_ccbs_status_request_rsp(ctrl, buffer, buffer + sizeof(buffer),
cc_record, is_free);
if (!end) {
return -1;
}
return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL);
}
/*!
* \internal
* \brief Encode and send an CCBSStatusRequest result message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param call Call leg from which to encode CCBSStatusRequest.
* \param cc_record Call completion record to process event.
* \param is_free TRUE if the Party A status is available.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int send_ccbs_status_request_rsp(struct pri *ctrl, q931_call *call, struct pri_cc_record *cc_record, int is_free)
{
if (rose_ccbs_status_request_rsp(ctrl, call, cc_record, is_free)
|| q931_facility(ctrl, call)) {
pri_message(ctrl,
"Could not schedule facility message for CCBSStatusRequest result.\n");
return -1;
}
return 0;
}
/*!
* \brief Update the busy status of CC party A.
*
* \param ctrl D channel controller.
* \param cc_id CC record ID to activate.
* \param status Updated party A status free(0)/busy(1)
*
* \note
* This is expected to be called only if there are two PTMP links between
* party A and the network. (e.g., A --> * --> PSTN)
*
* \return Nothing
*/
void pri_cc_status_req_rsp(struct pri *ctrl, long cc_id, int status)
{
struct pri_cc_record *cc_record;
if (!ctrl) {
return;
}
cc_record = pri_cc_find_by_id(ctrl, cc_id);
if (!cc_record) {
return;
}
if (cc_record->is_agent) {
/* CC is an agent and does not send this response event. */
return;
}
switch (ctrl->switchtype) {
case PRI_SWITCH_QSIG:
/* Does not apply. */
break;
case PRI_SWITCH_EUROISDN_E1:
case PRI_SWITCH_EUROISDN_T1:
if (PTMP_MODE(ctrl)) {
if (cc_record->response.invoke_operation != ROSE_ETSI_CCBSStatusRequest) {
/* We no longer know how to send the response. */
break;
}
send_ccbs_status_request_rsp(ctrl, cc_record->signaling, cc_record,
status ? 0 /* busy */ : 1 /* free */);
}
break;
default:
break;
}
}
/*!
* \brief Update the busy status of CC party A.
*
* \param ctrl D channel controller.
* \param cc_id CC record ID to activate.
* \param status Updated party A status free(0)/busy(1)
*
* \note
* Party A status is used to suspend/resume monitoring party B.
*
* \return Nothing
*/
void pri_cc_status(struct pri *ctrl, long cc_id, int status)
{
struct pri_cc_record *cc_record;
if (!ctrl) {
return;
}
cc_record = pri_cc_find_by_id(ctrl, cc_id);
if (!cc_record) {
return;
}
if (cc_record->is_agent) {
/* CC is an agent and does not send this event. */
return;
}
pri_cc_event(ctrl, cc_record->signaling, cc_record,
status ? CC_EVENT_SUSPEND : CC_EVENT_RESUME);
}
/*!
* \brief Initiate the CC callback call.
*
* \param ctrl D channel controller.
* \param cc_id CC record ID to activate.
* \param call Q.931 call leg.
* \param req SETUP request parameters. Parameters saved by CC will override.
*
* \retval 0 on success.
* \retval -1 on error.
*/
int pri_cc_call(struct pri *ctrl, long cc_id, q931_call *call, struct pri_sr *req)
{
struct pri_cc_record *cc_record;
if (!ctrl || !pri_is_call_valid(ctrl, call) || !req) {
return -1;
}
cc_record = pri_cc_find_by_id(ctrl, cc_id);
if (!cc_record) {
return -1;
}
if (cc_record->is_agent) {
/* CC is an agent and does not initiate callbacks. */
return -1;
}
/* Override parameters for sending recall. */
req->caller = cc_record->party_a;
req->called = cc_record->party_b;
req->transmode = cc_record->bc.transcapability;
req->userl1 = cc_record->bc.userl1;
/*
* The caller is allowed to send different user-user information.
*
* It makes no sense for the caller to supply redirecting information
* but we'll allow it to pass anyway.
*/
//q931_party_redirecting_init(&req->redirecting);
/* Add switch specific recall APDU to call. */
pri_cc_event(ctrl, call, cc_record, CC_EVENT_RECALL);
if (q931_setup(ctrl, call, req)) {
return -1;
}
return 0;
}
/*!
* \brief Unsolicited indication that CC is cancelled.
*
* \param ctrl D channel controller.
* \param cc_id CC record ID to deactivate.
*
* \return Nothing. The cc_id is no longer valid.
*/
void pri_cc_cancel(struct pri *ctrl, long cc_id)
{
struct pri_cc_record *cc_record;
if (!ctrl) {
return;
}
cc_record = pri_cc_find_by_id(ctrl, cc_id);
if (!cc_record) {
return;
}
pri_cc_event(ctrl, cc_record->signaling, cc_record, CC_EVENT_CANCEL);
}
/* ------------------------------------------------------------------- */
/* end pri_cc.c */