6829faae06
Call Completion Supplementary Service (CCSS) added for the following switch types: ETSI PTMP, ETSI PTP, Q.SIG. Specifications: ETS 300 359 CCBS for PTMP and PTP ETS 301 065 CCNR for PTMP and PTP ECMA-186 Call Completion for Q.SIG Several support services were added to support CC: Dummy Call Reference. Q.931 REGISTER message. Dynamic expansion of the number of available timers (up to 8192). Enhanced facility message handling. Current implementation limitations preclude the following: CC service retention is not supported. Q.SIG path reservation is not supported. (closes issue #14292) Reported by: tomaso Tested by: rmudgett JIRA SWP-1493 Review: https://reviewboard.asterisk.org/r/522/ git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@1714 2fbb986a-6c06-0410-b554-c9c1f0a7f128
7873 lines
226 KiB
C
7873 lines
226 KiB
C
/*
|
|
* 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;
|
|
|
|
ctrl = PRI_MASTER(ctrl);
|
|
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;
|
|
|
|
ctrl = PRI_MASTER(ctrl);
|
|
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;
|
|
|
|
ctrl = PRI_MASTER(ctrl);
|
|
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;
|
|
|
|
ctrl = PRI_MASTER(ctrl);
|
|
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 = PRI_MASTER(ctrl);
|
|
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 = PRI_MASTER(ctrl);
|
|
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;
|
|
|
|
ctrl = PRI_MASTER(ctrl);
|
|
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);
|
|
|
|
ctrl = PRI_MASTER(ctrl);
|
|
for (prev = &ctrl->cc.pool, current = ctrl->cc.pool; current;
|
|
prev = ¤t->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;
|
|
|
|
ctrl = PRI_MASTER(ctrl);
|
|
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->master = 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 (PRI_MASTER(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;
|
|
struct pri *master;
|
|
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;
|
|
|
|
master = PRI_MASTER(ctrl);
|
|
msg.args.etsi.CCBSInterrogate.recall_mode = master->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 = master->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 (!PRI_MASTER(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 (!PRI_MASTER(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 (!PRI_MASTER(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 *master;
|
|
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;
|
|
}
|
|
master = PRI_MASTER(ctrl);
|
|
if (!master->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(master, &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 =
|
|
master->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_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->master, 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->master, 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->master, 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->master, 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->master, 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->master, 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->master, 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_CANCEL:
|
|
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_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_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_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_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 || !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 = PRI_MASTER(ctrl)->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 (!PRI_MASTER(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 || !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 */
|