libpri/q931.c
Richard Mudgett 618acf9d53 Unknown IE 50 (cs5, Unknown Information Element)
Add code to recognize the code set 5 ie 50 (calling party category) to
suppress the unknown IE message.

(closes issue #13828)
Reported by: fdecher
Patches:
      libpri_ie50_cs5-trunk.diff3.txt uploaded by alecdavis (license 585)
Tested by: alecdavis


git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@1261 2fbb986a-6c06-0410-b554-c9c1f0a7f128
2009-11-03 17:19:20 +00:00

7295 lines
218 KiB
C

/*
* libpri: An implementation of Primary Rate ISDN
*
* Written by Mark Spencer <markster@digium.com>
*
* Copyright (C) 2001-2005, Digium, Inc.
* All Rights Reserved.
*/
/*
* 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.
*/
#include "compat.h"
#include "libpri.h"
#include "pri_internal.h"
#include "pri_q921.h"
#include "pri_q931.h"
#include "pri_facility.h"
#include "rose.h"
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <limits.h>
#define MAX_MAND_IES 10
struct msgtype {
int msgnum;
char *name;
int mandies[MAX_MAND_IES];
};
static struct msgtype msgs[] = {
/* Call establishment messages */
{ Q931_ALERTING, "ALERTING" },
{ Q931_CALL_PROCEEDING, "CALL PROCEEDING" },
{ Q931_CONNECT, "CONNECT" },
{ Q931_CONNECT_ACKNOWLEDGE, "CONNECT ACKNOWLEDGE" },
{ Q931_PROGRESS, "PROGRESS", { Q931_PROGRESS_INDICATOR } },
{ Q931_SETUP, "SETUP", { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT } },
{ Q931_SETUP_ACKNOWLEDGE, "SETUP ACKNOWLEDGE" },
/* Call disestablishment messages */
{ Q931_DISCONNECT, "DISCONNECT", { Q931_CAUSE } },
{ Q931_RELEASE, "RELEASE" },
{ Q931_RELEASE_COMPLETE, "RELEASE COMPLETE" },
{ Q931_RESTART, "RESTART", { Q931_RESTART_INDICATOR } },
{ Q931_RESTART_ACKNOWLEDGE, "RESTART ACKNOWLEDGE", { Q931_RESTART_INDICATOR } },
/* Miscellaneous */
{ Q931_STATUS, "STATUS", { Q931_CAUSE, Q931_IE_CALL_STATE } },
{ Q931_STATUS_ENQUIRY, "STATUS ENQUIRY" },
{ Q931_USER_INFORMATION, "USER_INFORMATION" },
{ Q931_SEGMENT, "SEGMENT" },
{ Q931_CONGESTION_CONTROL, "CONGESTION CONTROL" },
{ Q931_INFORMATION, "INFORMATION" },
{ Q931_FACILITY, "FACILITY" },
{ Q931_NOTIFY, "NOTIFY", { Q931_IE_NOTIFY_IND } },
/* Call Management */
{ Q931_HOLD, "HOLD" },
{ Q931_HOLD_ACKNOWLEDGE, "HOLD ACKNOWLEDGE" },
{ Q931_HOLD_REJECT, "HOLD REJECT", { Q931_CAUSE } },
{ Q931_RETRIEVE, "RETRIEVE" },
{ Q931_RETRIEVE_ACKNOWLEDGE, "RETRIEVE ACKNOWLEDGE" },
{ Q931_RETRIEVE_REJECT, "RETRIEVE REJECT", { Q931_CAUSE } },
{ Q931_RESUME, "RESUME" },
{ Q931_RESUME_ACKNOWLEDGE, "RESUME ACKNOWLEDGE", { Q931_CHANNEL_IDENT } },
{ Q931_RESUME_REJECT, "RESUME REJECT", { Q931_CAUSE } },
{ Q931_SUSPEND, "SUSPEND" },
{ Q931_SUSPEND_ACKNOWLEDGE, "SUSPEND ACKNOWLEDGE" },
{ Q931_SUSPEND_REJECT, "SUSPEND REJECT" },
};
static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *c, int missingmand);
static void nt_ptmp_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *c, int *allow_event, int *allow_posthandle);
struct msgtype att_maintenance_msgs[] = {
{ ATT_SERVICE, "SERVICE", { Q931_CHANNEL_IDENT } },
{ ATT_SERVICE_ACKNOWLEDGE, "SERVICE ACKNOWLEDGE", { Q931_CHANNEL_IDENT } },
};
struct msgtype national_maintenance_msgs[] = {
{ NATIONAL_SERVICE, "SERVICE", { Q931_CHANNEL_IDENT } },
{ NATIONAL_SERVICE_ACKNOWLEDGE, "SERVICE ACKNOWLEDGE", { Q931_CHANNEL_IDENT } },
};
static int post_handle_maintenance_message(struct pri *ctrl, int protodisc, struct q931_mh *mh, struct q931_call *c);
static struct msgtype causes[] = {
{ PRI_CAUSE_UNALLOCATED, "Unallocated (unassigned) number" },
{ PRI_CAUSE_NO_ROUTE_TRANSIT_NET, "No route to specified transmit network" },
{ PRI_CAUSE_NO_ROUTE_DESTINATION, "No route to destination" },
{ PRI_CAUSE_CHANNEL_UNACCEPTABLE, "Channel unacceptable" },
{ PRI_CAUSE_CALL_AWARDED_DELIVERED, "Call awarded and being delivered in an established channel" },
{ PRI_CAUSE_NORMAL_CLEARING, "Normal Clearing" },
{ PRI_CAUSE_USER_BUSY, "User busy" },
{ PRI_CAUSE_NO_USER_RESPONSE, "No user responding" },
{ PRI_CAUSE_NO_ANSWER, "User alerting, no answer" },
{ PRI_CAUSE_CALL_REJECTED, "Call Rejected" },
{ PRI_CAUSE_NUMBER_CHANGED, "Number changed" },
{ PRI_CAUSE_NONSELECTED_USER_CLEARING, "Non-selected user clearing" },
{ PRI_CAUSE_DESTINATION_OUT_OF_ORDER, "Destination out of order" },
{ PRI_CAUSE_INVALID_NUMBER_FORMAT, "Invalid number format" },
{ PRI_CAUSE_FACILITY_REJECTED, "Facility rejected" },
{ PRI_CAUSE_RESPONSE_TO_STATUS_ENQUIRY, "Response to STATus ENQuiry" },
{ PRI_CAUSE_NORMAL_UNSPECIFIED, "Normal, unspecified" },
{ PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION, "Circuit/channel congestion" },
{ PRI_CAUSE_NETWORK_OUT_OF_ORDER, "Network out of order" },
{ PRI_CAUSE_NORMAL_TEMPORARY_FAILURE, "Temporary failure" },
{ PRI_CAUSE_SWITCH_CONGESTION, "Switching equipment congestion" },
{ PRI_CAUSE_ACCESS_INFO_DISCARDED, "Access information discarded" },
{ PRI_CAUSE_REQUESTED_CHAN_UNAVAIL, "Requested channel not available" },
{ PRI_CAUSE_PRE_EMPTED, "Pre-empted" },
{ PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED, "Resource unavailable, unspecified" },
{ PRI_CAUSE_FACILITY_NOT_SUBSCRIBED, "Facility not subscribed" },
{ PRI_CAUSE_OUTGOING_CALL_BARRED, "Outgoing call barred" },
{ PRI_CAUSE_INCOMING_CALL_BARRED, "Incoming call barred" },
{ PRI_CAUSE_BEARERCAPABILITY_NOTAUTH, "Bearer capability not authorized" },
{ PRI_CAUSE_BEARERCAPABILITY_NOTAVAIL, "Bearer capability not available" },
{ PRI_CAUSE_SERVICEOROPTION_NOTAVAIL, "Service or option not available, unspecified" },
{ PRI_CAUSE_BEARERCAPABILITY_NOTIMPL, "Bearer capability not implemented" },
{ PRI_CAUSE_CHAN_NOT_IMPLEMENTED, "Channel not implemented" },
{ PRI_CAUSE_FACILITY_NOT_IMPLEMENTED, "Facility not implemented" },
{ PRI_CAUSE_INVALID_CALL_REFERENCE, "Invalid call reference value" },
{ PRI_CAUSE_IDENTIFIED_CHANNEL_NOTEXIST, "Identified channel does not exist" },
{ PRI_CAUSE_INCOMPATIBLE_DESTINATION, "Incompatible destination" },
{ PRI_CAUSE_INVALID_MSG_UNSPECIFIED, "Invalid message unspecified" },
{ PRI_CAUSE_MANDATORY_IE_MISSING, "Mandatory information element is missing" },
{ PRI_CAUSE_MESSAGE_TYPE_NONEXIST, "Message type nonexist." },
{ PRI_CAUSE_WRONG_MESSAGE, "Wrong message" },
{ PRI_CAUSE_IE_NONEXIST, "Info. element nonexist or not implemented" },
{ PRI_CAUSE_INVALID_IE_CONTENTS, "Invalid information element contents" },
{ PRI_CAUSE_WRONG_CALL_STATE, "Message not compatible with call state" },
{ PRI_CAUSE_RECOVERY_ON_TIMER_EXPIRE, "Recover on timer expiry" },
{ PRI_CAUSE_MANDATORY_IE_LENGTH_ERROR, "Mandatory IE length error" },
{ PRI_CAUSE_PROTOCOL_ERROR, "Protocol error, unspecified" },
{ PRI_CAUSE_INTERWORKING, "Interworking, unspecified" },
};
static struct msgtype facilities[] = {
{ PRI_NSF_SID_PREFERRED, "CPN (SID) preferred" },
{ PRI_NSF_ANI_PREFERRED, "BN (ANI) preferred" },
{ PRI_NSF_SID_ONLY, "CPN (SID) only" },
{ PRI_NSF_ANI_ONLY, "BN (ANI) only" },
{ PRI_NSF_CALL_ASSOC_TSC, "Call Associated TSC" },
{ PRI_NSF_NOTIF_CATSC_CLEARING, "Notification of CATSC Clearing or Resource Unavailable" },
{ PRI_NSF_OPERATOR, "Operator" },
{ PRI_NSF_PCCO, "Pre-subscribed Common Carrier Operator (PCCO)" },
{ PRI_NSF_SDN, "SDN (including GSDN)" },
{ PRI_NSF_TOLL_FREE_MEGACOM, "Toll Free MEGACOM" },
{ PRI_NSF_MEGACOM, "MEGACOM" },
{ PRI_NSF_ACCUNET, "ACCUNET Switched Digital Service" },
{ PRI_NSF_LONG_DISTANCE_SERVICE, "Long Distance Service" },
{ PRI_NSF_INTERNATIONAL_TOLL_FREE, "International Toll Free Service" },
{ PRI_NSF_ATT_MULTIQUEST, "AT&T MultiQuest" },
{ PRI_NSF_CALL_REDIRECTION_SERVICE, "Call Redirection Service" }
};
#define FLAG_WHOLE_INTERFACE 0x01
#define FLAG_PREFERRED 0x02
#define FLAG_EXCLUSIVE 0x04
#define RESET_INDICATOR_CHANNEL 0
#define RESET_INDICATOR_DS1 6
#define RESET_INDICATOR_PRI 7
#define TRANS_MODE_64_CIRCUIT 0x10
#define TRANS_MODE_2x64_CIRCUIT 0x11
#define TRANS_MODE_384_CIRCUIT 0x13
#define TRANS_MODE_1536_CIRCUIT 0x15
#define TRANS_MODE_1920_CIRCUIT 0x17
#define TRANS_MODE_MULTIRATE 0x18
#define TRANS_MODE_PACKET 0x40
#define RATE_ADAPT_56K 0x0f
#define LAYER_2_LAPB 0x46
#define LAYER_3_X25 0x66
/* The 4ESS uses a different audio field */
#define PRI_TRANS_CAP_AUDIO_4ESS 0x08
/* Don't forget to update PRI_PROG_xxx at libpri.h */
#define Q931_PROG_CALL_NOT_E2E_ISDN 0x01
#define Q931_PROG_CALLED_NOT_ISDN 0x02
#define Q931_PROG_CALLER_NOT_ISDN 0x03
#define Q931_PROG_CALLER_RETURNED_TO_ISDN 0x04
#define Q931_PROG_INBAND_AVAILABLE 0x08
#define Q931_PROG_DELAY_AT_INTERF 0x0a
#define Q931_PROG_INTERWORKING_WITH_PUBLIC 0x10
#define Q931_PROG_INTERWORKING_NO_RELEASE 0x11
#define Q931_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER 0x12
#define Q931_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER 0x13
#define CODE_CCITT 0x0
#define CODE_INTERNATIONAL 0x1
#define CODE_NATIONAL 0x2
#define CODE_NETWORK_SPECIFIC 0x3
#define LOC_USER 0x0
#define LOC_PRIV_NET_LOCAL_USER 0x1
#define LOC_PUB_NET_LOCAL_USER 0x2
#define LOC_TRANSIT_NET 0x3
#define LOC_PUB_NET_REMOTE_USER 0x4
#define LOC_PRIV_NET_REMOTE_USER 0x5
#define LOC_INTERNATIONAL_NETWORK 0x7
#define LOC_NETWORK_BEYOND_INTERWORKING 0xa
static char *ie2str(int ie);
static char *msg2str(int msg);
#define FUNC_DUMP(name) void (name)(int full_ie, struct pri *pri, q931_ie *ie, int len, char prefix)
#define FUNC_RECV(name) int (name)(int full_ie, struct pri *pri, q931_call *call, int msgtype, q931_ie *ie, int len)
#define FUNC_SEND(name) int (name)(int full_ie, struct pri *pri, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
#if 1
/* Update call state with transition trace. */
#define UPDATE_OURCALLSTATE(ctrl, call, newstate) \
do { \
if (((ctrl)->debug & PRI_DEBUG_Q931_STATE) && (call)->ourcallstate != (newstate)) { \
pri_message((ctrl), \
DBGHEAD "%s %d enters state %d (%s). Hold state: %s\n", \
DBGINFO, ((call) == (call)->master_call) ? "Call" : "Subcall", \
(call)->cr, (newstate), q931_call_state_str(newstate), \
q931_hold_state_str((call)->master_call->hold_state)); \
} \
(call)->ourcallstate = (newstate); \
} while (0)
#else
/* Update call state with no trace. */
#define UPDATE_OURCALLSTATE(ctrl, call, newstate) (call)->ourcallstate = (newstate)
#endif
#if 1
/* Update hold state with transition trace. */
#define UPDATE_HOLD_STATE(ctrl, master_call, newstate) \
do { \
if (((ctrl)->debug & PRI_DEBUG_Q931_STATE) \
&& (master_call)->hold_state != (newstate)) { \
pri_message((ctrl), \
DBGHEAD "Call %d in state %d (%s) enters Hold state: %s\n", \
DBGINFO, (master_call)->cr, (master_call)->ourcallstate, \
q931_call_state_str((master_call)->ourcallstate), \
q931_hold_state_str(newstate)); \
} \
(master_call)->hold_state = (newstate); \
} while (0)
#else
/* Update hold state with no trace. */
#define UPDATE_HOLD_STATE(ctrl, master_call, newstate) (master_call)->hold_state = (newstate)
#endif
struct ie {
/* Maximal count of same IEs at the message (0 - any, 1..n - limited) */
int max_count;
/* IE code */
int ie;
/* IE friendly name */
char *name;
/* Dump an IE for debugging (preceed all lines by prefix) */
FUNC_DUMP(*dump);
/* Handle IE returns 0 on success, -1 on failure */
FUNC_RECV(*receive);
/* Add IE to a message, return the # of bytes added or -1 on failure */
FUNC_SEND(*transmit);
};
/*!
* \internal
* \brief Encode the channel id information to pass to upper level.
*
* \param call Q.931 call leg
*
* \return Encoded channel value.
*/
static int q931_encode_channel(const q931_call *call)
{
int held_call;
int channelno;
int ds1no;
switch (call->master_call->hold_state) {
case Q931_HOLD_STATE_CALL_HELD:
case Q931_HOLD_STATE_RETRIEVE_REQ:
case Q931_HOLD_STATE_RETRIEVE_IND:
held_call = 1 << 18;
/* So a -1 does not wipe out the held_call flag. */
channelno = call->channelno & 0xFF;
ds1no = call->ds1no & 0xFF;
break;
default:
held_call = 0;
channelno = call->channelno;
ds1no = call->ds1no;
break;
}
return channelno | (ds1no << 8) | (call->ds1explicit << 16) | (call->cis_call << 17)
| held_call;
}
/*!
* \brief Determine if layer 2 is in PTMP mode.
*
* \param ctrl D channel controller.
*
* \retval TRUE if in PTMP mode.
* \retval FALSE otherwise.
*/
int q931_is_ptmp(const struct pri *ctrl)
{
/* Check master control structure */
for (; ctrl->master; ctrl = ctrl->master) {
}
return ctrl->tei == Q921_TEI_GROUP;
}
/*!
* \brief Initialize the given struct q931_party_name
*
* \param name Structure to initialize
*
* \return Nothing
*/
void q931_party_name_init(struct q931_party_name *name)
{
name->valid = 0;
name->presentation = PRI_PRES_UNAVAILABLE;
name->char_set = PRI_CHAR_SET_ISO8859_1;
name->str[0] = '\0';
}
/*!
* \brief Initialize the given struct q931_party_number
*
* \param number Structure to initialize
*
* \return Nothing
*/
void q931_party_number_init(struct q931_party_number *number)
{
number->valid = 0;
number->presentation = PRI_PRES_UNAVAILABLE | PRI_PRES_USER_NUMBER_UNSCREENED;
number->plan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164;
number->str[0] = '\0';
}
/*!
* \brief Initialize the given struct q931_party_subaddress
*
* \param subaddress Structure to initialize
*
* \return Nothing
*/
void q931_party_subaddress_init(struct q931_party_subaddress *subaddress)
{
subaddress->valid = 0;
subaddress->type = 0;
subaddress->odd_even_indicator = 0;
subaddress->length = 0;
subaddress->data[0] = '\0';
}
/*!
* \brief Initialize the given struct q931_party_address
*
* \param address Structure to initialize
*
* \return Nothing
*/
void q931_party_address_init(struct q931_party_address *address)
{
q931_party_number_init(&address->number);
q931_party_subaddress_init(&address->subaddress);
}
/*!
* \brief Initialize the given struct q931_party_id
*
* \param id Structure to initialize
*
* \return Nothing
*/
void q931_party_id_init(struct q931_party_id *id)
{
q931_party_name_init(&id->name);
q931_party_number_init(&id->number);
q931_party_subaddress_init(&id->subaddress);
}
/*!
* \brief Initialize the given struct q931_party_redirecting
*
* \param redirecting Structure to initialize
*
* \return Nothing
*/
void q931_party_redirecting_init(struct q931_party_redirecting *redirecting)
{
q931_party_id_init(&redirecting->from);
q931_party_id_init(&redirecting->to);
q931_party_id_init(&redirecting->orig_called);
redirecting->state = Q931_REDIRECTING_STATE_IDLE;
redirecting->count = 0;
redirecting->orig_reason = PRI_REDIR_UNKNOWN;
redirecting->reason = PRI_REDIR_UNKNOWN;
}
/*!
* \brief Compare the left and right party name.
*
* \param left Left parameter party name.
* \param right Right parameter party name.
*
* \retval < 0 when left < right.
* \retval == 0 when left == right.
* \retval > 0 when left > right.
*/
int q931_party_name_cmp(const struct q931_party_name *left, const struct q931_party_name *right)
{
int cmp;
if (!left->valid) {
if (!right->valid) {
return 0;
}
return -1;
} else if (!right->valid) {
return 1;
}
cmp = left->char_set - right->char_set;
if (cmp) {
return cmp;
}
cmp = strcmp(left->str, right->str);
if (cmp) {
return cmp;
}
cmp = left->presentation - right->presentation;
return cmp;
}
/*!
* \brief Compare the left and right party number.
*
* \param left Left parameter party number.
* \param right Right parameter party number.
*
* \retval < 0 when left < right.
* \retval == 0 when left == right.
* \retval > 0 when left > right.
*/
int q931_party_number_cmp(const struct q931_party_number *left, const struct q931_party_number *right)
{
int cmp;
if (!left->valid) {
if (!right->valid) {
return 0;
}
return -1;
} else if (!right->valid) {
return 1;
}
cmp = left->plan - right->plan;
if (cmp) {
return cmp;
}
cmp = strcmp(left->str, right->str);
if (cmp) {
return cmp;
}
cmp = left->presentation - right->presentation;
return cmp;
}
/*!
* \brief Compare the left and right party subaddress.
*
* \param left Left parameter party subaddress.
* \param right Right parameter party subaddress.
*
* \retval < 0 when left < right.
* \retval == 0 when left == right.
* \retval > 0 when left > right.
*/
int q931_party_subaddress_cmp(const struct q931_party_subaddress *left, const struct q931_party_subaddress *right)
{
int cmp;
if (!left->valid) {
if (!right->valid) {
return 0;
}
return -1;
} else if (!right->valid) {
return 1;
}
cmp = left->type - right->type;
if (cmp) {
return cmp;
}
cmp = memcmp(left->data, right->data,
(left->length < right->length) ? left->length : right->length);
if (cmp) {
return cmp;
}
cmp = left->length - right->length;
if (cmp) {
return cmp;
}
cmp = left->odd_even_indicator - right->odd_even_indicator;
return cmp;
}
/*!
* \brief Compare the left and right party id.
*
* \param left Left parameter party id.
* \param right Right parameter party id.
*
* \retval < 0 when left < right.
* \retval == 0 when left == right.
* \retval > 0 when left > right.
*/
int q931_party_id_cmp(const struct q931_party_id *left, const struct q931_party_id *right)
{
int cmp;
cmp = q931_party_number_cmp(&left->number, &right->number);
if (cmp) {
return cmp;
}
cmp = q931_party_subaddress_cmp(&left->subaddress, &right->subaddress);
if (cmp) {
return cmp;
}
cmp = q931_party_name_cmp(&left->name, &right->name);
return cmp;
}
/*!
* \brief Copy the Q.931 party name to the PRI party name structure.
*
* \param pri_name PRI party name structure
* \param q931_name Q.931 party name structure
*
* \return Nothing
*/
void q931_party_name_copy_to_pri(struct pri_party_name *pri_name, const struct q931_party_name *q931_name)
{
if (q931_name->valid) {
pri_name->valid = 1;
pri_name->presentation = q931_name->presentation;
pri_name->char_set = q931_name->char_set;
libpri_copy_string(pri_name->str, q931_name->str, sizeof(pri_name->str));
} else {
pri_name->valid = 0;
pri_name->presentation = PRI_PRES_UNAVAILABLE;
pri_name->char_set = PRI_CHAR_SET_ISO8859_1;
pri_name->str[0] = 0;
}
}
/*!
* \brief Copy the Q.931 party number to the PRI party number structure.
*
* \param pri_number PRI party number structure
* \param q931_number Q.931 party number structure
*
* \return Nothing
*/
void q931_party_number_copy_to_pri(struct pri_party_number *pri_number, const struct q931_party_number *q931_number)
{
if (q931_number->valid) {
pri_number->valid = 1;
pri_number->presentation = q931_number->presentation;
pri_number->plan = q931_number->plan;
libpri_copy_string(pri_number->str, q931_number->str, sizeof(pri_number->str));
} else {
pri_number->valid = 0;
pri_number->presentation = PRI_PRES_UNAVAILABLE | PRI_PRES_USER_NUMBER_UNSCREENED;
pri_number->plan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164;
pri_number->str[0] = 0;
}
}
/*!
* \brief Copy the Q.931 party subaddress to the PRI party subaddress structure.
*
* \param pri_subaddress PRI party subaddress structure
* \param q931_subaddress Q.931 party subaddress structure
*
* \return Nothing
*/
void q931_party_subaddress_copy_to_pri(struct pri_party_subaddress *pri_subaddress, const struct q931_party_subaddress *q931_subaddress)
{
int length;
/*
* The size of pri_subaddress->data[] is not the same as the size of
* q931_subaddress->data[].
*/
if (!q931_subaddress->valid) {
pri_subaddress->valid = 0;
pri_subaddress->type = 0;
pri_subaddress->odd_even_indicator = 0;
pri_subaddress->length = 0;
pri_subaddress->data[0] = '\0';
return;
}
pri_subaddress->valid = 1;
pri_subaddress->type = q931_subaddress->type;
pri_subaddress->odd_even_indicator = q931_subaddress->odd_even_indicator;
length = q931_subaddress->length;
pri_subaddress->length = length;
memcpy(pri_subaddress->data, q931_subaddress->data, length);
pri_subaddress->data[length] = '\0';
}
/*!
* \brief Copy the Q.931 party id to the PRI party id structure.
*
* \param pri_id PRI party id structure
* \param q931_id Q.931 party id structure
*
* \return Nothing
*/
void q931_party_id_copy_to_pri(struct pri_party_id *pri_id, const struct q931_party_id *q931_id)
{
q931_party_name_copy_to_pri(&pri_id->name, &q931_id->name);
q931_party_number_copy_to_pri(&pri_id->number, &q931_id->number);
q931_party_subaddress_copy_to_pri(&pri_id->subaddress, &q931_id->subaddress);
}
/*!
* \brief Copy the Q.931 redirecting data to the PRI redirecting structure.
*
* \param pri_redirecting PRI redirecting structure
* \param q931_redirecting Q.931 redirecting structure
*
* \return Nothing
*/
void q931_party_redirecting_copy_to_pri(struct pri_party_redirecting *pri_redirecting, const struct q931_party_redirecting *q931_redirecting)
{
q931_party_id_copy_to_pri(&pri_redirecting->from, &q931_redirecting->from);
q931_party_id_copy_to_pri(&pri_redirecting->to, &q931_redirecting->to);
q931_party_id_copy_to_pri(&pri_redirecting->orig_called,
&q931_redirecting->orig_called);
pri_redirecting->count = q931_redirecting->count;
pri_redirecting->orig_reason = q931_redirecting->orig_reason;
pri_redirecting->reason = q931_redirecting->reason;
}
/*!
* \brief Fixup some values in the q931_party_id that may be objectionable by switches.
*
* \param ctrl D channel controller.
* \param id Party ID to tweak.
*
* \return Nothing
*/
void q931_party_id_fixup(const struct pri *ctrl, struct q931_party_id *id)
{
switch (ctrl->switchtype) {
case PRI_SWITCH_DMS100:
case PRI_SWITCH_ATT4ESS:
/* Doesn't like certain presentation types */
if (id->number.valid && !(id->number.presentation & 0x7c)) {
/* i.e., If presentation is allowed it must be a network number */
id->number.presentation = PRES_ALLOWED_NETWORK_NUMBER;
}
break;
default:
break;
}
}
/*!
* \brief Determine the overall presentation value for the given party.
*
* \param id Party to determine the overall presentation value.
*
* \return Overall presentation value for the given party.
*/
int q931_party_id_presentation(const struct q931_party_id *id)
{
int number_priority;
int number_value;
int number_screening;
int name_priority;
int name_value;
/* Determine name presentation priority. */
if (!id->name.valid) {
name_value = PRI_PRES_UNAVAILABLE;
name_priority = 3;
} else {
name_value = id->name.presentation & PRI_PRES_RESTRICTION;
switch (name_value) {
case PRI_PRES_RESTRICTED:
name_priority = 0;
break;
case PRI_PRES_ALLOWED:
name_priority = 1;
break;
case PRI_PRES_UNAVAILABLE:
name_priority = 2;
break;
default:
name_value = PRI_PRES_UNAVAILABLE;
name_priority = 3;
break;
}
}
/* Determine number presentation priority. */
if (!id->number.valid) {
number_screening = PRI_PRES_USER_NUMBER_UNSCREENED;
number_value = PRI_PRES_UNAVAILABLE;
number_priority = 3;
} else {
number_screening = id->number.presentation & PRI_PRES_NUMBER_TYPE;
number_value = id->number.presentation & PRI_PRES_RESTRICTION;
switch (number_value) {
case PRI_PRES_RESTRICTED:
number_priority = 0;
break;
case PRI_PRES_ALLOWED:
number_priority = 1;
break;
case PRI_PRES_UNAVAILABLE:
number_priority = 2;
break;
default:
number_screening = PRI_PRES_USER_NUMBER_UNSCREENED;
number_value = PRI_PRES_UNAVAILABLE;
number_priority = 3;
break;
}
}
/* Select the wining presentation value. */
if (name_priority < number_priority) {
number_value = name_value;
}
return number_value | number_screening;
}
static void q931_clr_subcommands(struct pri *ctrl)
{
ctrl->subcmds.counter_subcmd = 0;
}
struct pri_subcommand *q931_alloc_subcommand(struct pri *ctrl)
{
if (ctrl->subcmds.counter_subcmd < PRI_MAX_SUBCOMMANDS) {
return &ctrl->subcmds.subcmd[ctrl->subcmds.counter_subcmd++];
}
return NULL;
}
static char *code2str(int code, struct msgtype *codes, int max)
{
int x;
for (x=0;x<max; x++)
if (codes[x].msgnum == code)
return codes[x].name;
return "Unknown";
}
static char *pritype(int type)
{
switch (type) {
case PRI_CPE:
return "CPE";
break;
case PRI_NETWORK:
return "NET";
break;
default:
return "UNKNOWN";
}
}
static char *binary(int b, int len) {
static char res[33];
int x;
memset(res, 0, sizeof(res));
if (len > 32)
len = 32;
for (x=1;x<=len;x++)
res[x-1] = b & (1 << (len - x)) ? '1' : '0';
return res;
}
static int receive_channel_id(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
int x;
int pos = 0;
int need_extended_channel_octets;/*!< TRUE if octets 3.2 and 3.3 need to be present. */
if (ie->data[0] & 0x08) {
call->chanflags = FLAG_EXCLUSIVE;
} else {
call->chanflags = FLAG_PREFERRED;
}
need_extended_channel_octets = 0;
if (ie->data[0] & 0x20) {
/* PRI encoded interface type */
switch (ie->data[0] & 0x03) {
case 0x00:
/* No channel */
call->channelno = 0;
call->chanflags = FLAG_PREFERRED;
break;
case 0x01:
/* As indicated in following octets */
need_extended_channel_octets = 1;
break;
case 0x03:
/* Any channel */
call->chanflags = FLAG_PREFERRED;
break;
default:
pri_error(ctrl, "!! Unexpected Channel selection %d\n", ie->data[0] & 0x03);
return -1;
}
} else {
/* BRI encoded interface type */
switch (ie->data[0] & 0x03) {
case 0x00:
/* No channel */
call->channelno = 0;
call->chanflags = FLAG_PREFERRED;
break;
case 0x03:
/* Any channel */
call->chanflags = FLAG_PREFERRED;
break;
default:
/* Specified B channel (B1 or B2) */
call->channelno = ie->data[0] & 0x03;
break;
}
}
pos++;
if (ie->data[0] & 0x40) {
/* DS1 specified -- stop here */
call->ds1no = ie->data[1] & 0x7f;
call->ds1explicit = 1;
pos++;
} else {
call->ds1explicit = 0;
}
if (ie->data[0] & 0x04) {
/* D channel call. Signaling only. */
call->cis_call = 1;
call->chanflags = FLAG_EXCLUSIVE;/* For safety mark this channel as exclusive. */
call->channelno = 0;
return 0;
}
if (need_extended_channel_octets && pos + 2 < len) {
/* More coming */
if ((ie->data[pos] & 0x0f) != 3) {
/* Channel type/mapping is not for B channel units. */
pri_error(ctrl, "!! Unexpected Channel Type %d\n", ie->data[1] & 0x0f);
return -1;
}
if ((ie->data[pos] & 0x60) != 0) {
pri_error(ctrl, "!! Invalid CCITT coding %d\n", (ie->data[1] & 0x60) >> 5);
return -1;
}
if (ie->data[pos] & 0x10) {
/* Expect Slot Map */
call->slotmap = 0;
pos++;
for (x=0;x<3;x++) {
call->slotmap <<= 8;
call->slotmap |= ie->data[x + pos];
}
} else {
pos++;
/* Only expect a particular channel */
call->channelno = ie->data[pos] & 0x7f;
if (ctrl->chan_mapping_logical && call->channelno > 15)
call->channelno++;
}
}
return 0;
}
static int transmit_channel_id(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
int pos = 0;
/* We are ready to transmit single IE only */
if (order > 1)
return 0;
if (call->cis_call) {
/*
* Read the standards docs to figure this out.
* Q.SIG ECMA-165 section 7.3
* ITU Q.931 section 4.5.13
*/
ie->data[pos++] = ctrl->bri ? 0x8c : 0xac;
return pos + 2;
}
/* Start with standard stuff */
if (ctrl->switchtype == PRI_SWITCH_GR303_TMC)
ie->data[pos] = 0x69;
else if (ctrl->bri) {
ie->data[pos] = 0x80;
ie->data[pos] |= (call->channelno & 0x3);
} else {
/* PRI */
if (call->slotmap != -1 || (call->chanflags & FLAG_WHOLE_INTERFACE)) {
/* Specified channel */
ie->data[pos] = 0xa1;
} else if (call->channelno < 0 || call->channelno == 0xff) {
/* Any channel */
ie->data[pos] = 0xa3;
} else if (!call->channelno) {
/* No channel */
ie->data[pos] = 0xa0;
} else {
/* Specified channel */
ie->data[pos] = 0xa1;
}
}
if (call->chanflags & FLAG_EXCLUSIVE) {
/* Channel is exclusive */
ie->data[pos] |= 0x08;
} else if (!call->chanflags) {
/* Don't need this IE */
return 0;
}
if (!ctrl->bri && (((ctrl->switchtype != PRI_SWITCH_QSIG) && (call->ds1no > 0)) || call->ds1explicit)) {
/* We are specifying the interface. Octet 3.1 */
ie->data[pos++] |= 0x40;
ie->data[pos++] = 0x80 | call->ds1no;
} else {
++pos;
}
if (!ctrl->bri && (ie->data[0] & 0x03) == 0x01 /* Specified channel */
&& !(call->chanflags & FLAG_WHOLE_INTERFACE)) {
/* The 3.2 and 3.3 octets need to be present */
ie->data[pos] = 0x83;
if (call->slotmap != -1) {
int octet;
/* We have to send a channel map */
ie->data[pos++] |= 0x10;
for (octet = 3; octet--;) {
ie->data[pos++] = (call->slotmap >> (8 * octet)) & 0xff;
}
} else {
/* Channel number specified */
++pos;
if (ctrl->chan_mapping_logical && call->channelno > 16) {
ie->data[pos++] = 0x80 | (call->channelno - 1);
} else {
ie->data[pos++] = 0x80 | call->channelno;
}
}
}
return pos + 2;
}
static void dump_channel_id(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
int pos;
int x;
int res;
static const char *msg_chan_sel[] = {
"No channel selected", "B1 channel", "B2 channel", "Any channel selected",
"No channel selected", "As indicated in following octets", "Reserved", "Any channel selected"
};
pri_message(ctrl,
"%c Channel ID (len=%2d) [ Ext: %d IntID: %s %s Spare: %d %s Dchan: %d\n",
prefix, len,
(ie->data[0] & 0x80) ? 1 : 0,
(ie->data[0] & 0x40) ? "Explicit" : "Implicit",
(ie->data[0] & 0x20) ? "Other(PRI)" : "BRI",
(ie->data[0] & 0x10) ? 1 : 0,
(ie->data[0] & 0x08) ? "Exclusive" : "Preferred",
(ie->data[0] & 0x04) ? 1 : 0);
pri_message(ctrl, "%c ChanSel: %s\n",
prefix, msg_chan_sel[(ie->data[0] & 0x03) | ((ie->data[0] >> 3) & 0x04)]);
pos = 1;
len -= 2;
if (ie->data[0] & 0x40) {
/* Explicitly defined DS1 */
do {
pri_message(ctrl, "%c Ext: %d DS1 Identifier: %d \n",
prefix, (ie->data[pos] & 0x80) >> 7, ie->data[pos] & 0x7f);
++pos;
} while (!(ie->data[pos - 1] & 0x80) && pos < len);
} else {
/* Implicitly defined DS1 */
}
if (pos < len) {
/* Still more information here */
pri_message(ctrl,
"%c Ext: %d Coding: %d %s Specified Channel Type: %d\n",
prefix, (ie->data[pos] & 0x80) >> 7, (ie->data[pos] & 60) >> 5,
(ie->data[pos] & 0x10) ? "Slot Map" : "Number", ie->data[pos] & 0x0f);
++pos;
}
if (pos < len) {
if (!(ie->data[pos - 1] & 0x10)) {
/* Number specified */
do {
pri_message(ctrl,
"%c Ext: %d Channel: %d Type: %s%c\n",
prefix, (ie->data[pos] & 0x80) >> 7,
(ie->data[pos]) & 0x7f, pritype(ctrl->localtype),
(pos + 1 < len) ? ' ' : ']');
++pos;
} while (pos < len);
} else {
/* Map specified */
res = 0;
x = 0;
do {
res <<= 8;
res |= ie->data[pos++];
++x;
} while (pos < len);
pri_message(ctrl, "%c Map len: %d Map: %s ]\n", prefix,
x, binary(res, x << 3));
}
} else {
pri_message(ctrl, " ]\n");
}
}
static char *ri2str(int ri)
{
static struct msgtype ris[] = {
{ 0, "Indicated Channel" },
{ 6, "Single DS1 Facility" },
{ 7, "All DS1 Facilities" },
};
return code2str(ri, ris, sizeof(ris) / sizeof(ris[0]));
}
static void dump_restart_indicator(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
pri_message(ctrl, "%c Restart Indentifier (len=%2d) [ Ext: %d Spare: %d Resetting %s (%d) ]\n",
prefix, len, (ie->data[0] & 0x80) >> 7, (ie->data[0] & 0x78) >> 3, ri2str(ie->data[0] & 0x7), ie->data[0] & 0x7);
}
static int receive_restart_indicator(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
/* Pretty simple */
call->ri = ie->data[0] & 0x7;
return 0;
}
static int transmit_restart_indicator(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
/* Pretty simple */
switch(call->ri) {
case 0:
case 6:
case 7:
ie->data[0] = 0x80 | (call->ri & 0x7);
break;
case 5:
/* Switch compatibility */
ie->data[0] = 0xA0 | (call->ri & 0x7);
break;
default:
pri_error(ctrl, "!! Invalid restart indicator value %d\n", call->ri);
return-1;
}
return 3;
}
static char *redirection_reason2str(int mode)
{
static struct msgtype modes[] = {
{ PRI_REDIR_UNKNOWN, "Unknown" },
{ PRI_REDIR_FORWARD_ON_BUSY, "Forwarded on busy" },
{ PRI_REDIR_FORWARD_ON_NO_REPLY, "Forwarded on no reply" },
{ PRI_REDIR_DEFLECTION, "Call deflected" },
{ PRI_REDIR_DTE_OUT_OF_ORDER, "Called DTE out of order" },
{ PRI_REDIR_FORWARDED_BY_DTE, "Forwarded by called DTE" },
{ PRI_REDIR_UNCONDITIONAL, "Forwarded unconditionally" },
};
return code2str(mode, modes, sizeof(modes) / sizeof(modes[0]));
}
static char *cap2str(int mode)
{
static struct msgtype modes[] = {
{ PRI_TRANS_CAP_SPEECH, "Speech" },
{ PRI_TRANS_CAP_DIGITAL, "Unrestricted digital information" },
{ PRI_TRANS_CAP_RESTRICTED_DIGITAL, "Restricted digital information" },
{ PRI_TRANS_CAP_3_1K_AUDIO, "3.1kHz audio" },
{ PRI_TRANS_CAP_DIGITAL_W_TONES, "Unrestricted digital information with tones/announcements" },
{ PRI_TRANS_CAP_VIDEO, "Video" },
{ PRI_TRANS_CAP_AUDIO_4ESS, "3.1khz audio (4ESS)" },
};
return code2str(mode, modes, sizeof(modes) / sizeof(modes[0]));
}
static char *mode2str(int mode)
{
static struct msgtype modes[] = {
{ TRANS_MODE_64_CIRCUIT, "64kbps, circuit-mode" },
{ TRANS_MODE_2x64_CIRCUIT, "2x64kbps, circuit-mode" },
{ TRANS_MODE_384_CIRCUIT, "384kbps, circuit-mode" },
{ TRANS_MODE_1536_CIRCUIT, "1536kbps, circuit-mode" },
{ TRANS_MODE_1920_CIRCUIT, "1920kbps, circuit-mode" },
{ TRANS_MODE_MULTIRATE, "Multirate (Nx64kbps)" },
{ TRANS_MODE_PACKET, "Packet Mode" },
};
return code2str(mode, modes, sizeof(modes) / sizeof(modes[0]));
}
static char *l12str(int proto)
{
static struct msgtype protos[] = {
{ PRI_LAYER_1_ITU_RATE_ADAPT, "V.110 Rate Adaption" },
{ PRI_LAYER_1_ULAW, "u-Law" },
{ PRI_LAYER_1_ALAW, "A-Law" },
{ PRI_LAYER_1_G721, "G.721 ADPCM" },
{ PRI_LAYER_1_G722_G725, "G.722/G.725 7kHz Audio" },
{ PRI_LAYER_1_H223_H245, "H.223/H.245 Multimedia" },
{ PRI_LAYER_1_NON_ITU_ADAPT, "Non-ITU Rate Adaption" },
{ PRI_LAYER_1_V120_RATE_ADAPT, "V.120 Rate Adaption" },
{ PRI_LAYER_1_X31_RATE_ADAPT, "X.31 Rate Adaption" },
};
return code2str(proto, protos, sizeof(protos) / sizeof(protos[0]));
}
static char *ra2str(int proto)
{
static struct msgtype protos[] = {
{ PRI_RATE_ADAPT_9K6, "9.6 kbit/s" },
};
return code2str(proto, protos, sizeof(protos) / sizeof(protos[0]));
}
static char *l22str(int proto)
{
static struct msgtype protos[] = {
{ LAYER_2_LAPB, "LAPB" },
};
return code2str(proto, protos, sizeof(protos) / sizeof(protos[0]));
}
static char *l32str(int proto)
{
static struct msgtype protos[] = {
{ LAYER_3_X25, "X.25" },
};
return code2str(proto, protos, sizeof(protos) / sizeof(protos[0]));
}
static char *int_rate2str(int proto)
{
static struct msgtype protos[] = {
{ PRI_INT_RATE_8K, "8 kbit/s" },
{ PRI_INT_RATE_16K, "16 kbit/s" },
{ PRI_INT_RATE_32K, "32 kbit/s" },
};
return code2str(proto, protos, sizeof(protos) / sizeof(protos[0]));
}
static void dump_bearer_capability(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
int pos=2;
pri_message(ctrl, "%c Bearer Capability (len=%2d) [ Ext: %d Q.931 Std: %d Info transfer capability: %s (%d)\n",
prefix, len, (ie->data[0] & 0x80 ) >> 7, (ie->data[0] & 0x60) >> 5, cap2str(ie->data[0] & 0x1f), (ie->data[0] & 0x1f));
pri_message(ctrl, "%c Ext: %d Trans mode/rate: %s (%d)\n", prefix, (ie->data[1] & 0x80) >> 7, mode2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f);
/* octet 4.1 exists iff mode/rate is multirate */
if ((ie->data[1] & 0x7f) == 0x18) {
pri_message(ctrl, "%c Ext: %d Transfer rate multiplier: %d x 64\n", prefix, (ie->data[2] & 0x80) >> 7, ie->data[2] & 0x7f);
pos++;
}
/* don't count the IE num and length as part of the data */
len -= 2;
/* Look for octet 5; this is identified by bits 5,6 == 01 */
if (pos < len &&
(ie->data[pos] & 0x60) == 0x20) {
/* although the layer1 is only the bottom 5 bits of the byte,
previous versions of this library passed bits 5&6 through
too, so we have to do the same for binary compatability */
u_int8_t layer1 = ie->data[pos] & 0x7f;
pri_message(ctrl, "%c User information layer 1: %s (%d)\n",
prefix, l12str(layer1), layer1);
pos++;
/* octet 5a? */
if (pos < len && !(ie->data[pos-1] & 0x80)) {
int ra = ie->data[pos] & 0x7f;
pri_message(ctrl, "%c Async: %d, Negotiation: %d, "
"User rate: %s (%#x)\n",
prefix,
ra & PRI_RATE_ADAPT_ASYNC ? 1 : 0,
ra & PRI_RATE_ADAPT_NEGOTIATION_POSS ? 1 : 0,
ra2str(ra & PRI_RATE_USER_RATE_MASK),
ra & PRI_RATE_USER_RATE_MASK);
pos++;
}
/* octet 5b? */
if (pos < len && !(ie->data[pos-1] & 0x80)) {
u_int8_t data = ie->data[pos];
if (layer1 == PRI_LAYER_1_ITU_RATE_ADAPT) {
pri_message(ctrl, "%c Intermediate rate: %s (%d), "
"NIC on Tx: %d, NIC on Rx: %d, "
"Flow control on Tx: %d, "
"Flow control on Rx: %d\n",
prefix, int_rate2str((data & 0x60)>>5),
(data & 0x60)>>5,
(data & 0x10)?1:0,
(data & 0x08)?1:0,
(data & 0x04)?1:0,
(data & 0x02)?1:0);
} else if (layer1 == PRI_LAYER_1_V120_RATE_ADAPT) {
pri_message(ctrl, "%c Hdr: %d, Multiframe: %d, Mode: %d, "
"LLI negot: %d, Assignor: %d, "
"In-band neg: %d\n", prefix,
(data & 0x40)?1:0,
(data & 0x20)?1:0,
(data & 0x10)?1:0,
(data & 0x08)?1:0,
(data & 0x04)?1:0,
(data & 0x02)?1:0);
} else {
pri_message(ctrl, "%c Unknown octet 5b: 0x%x\n",
prefix, data);
}
pos++;
}
/* octet 5c? */
if (pos < len && !(ie->data[pos-1] & 0x80)) {
u_int8_t data = ie->data[pos];
const char *stop_bits[] = {"?","1","1.5","2"};
const char *data_bits[] = {"?","5","7","8"};
const char *parity[] = {"Odd","?","Even","None",
"zero","one","?","?"};
pri_message(ctrl, "%c Stop bits: %s, data bits: %s, "
"parity: %s\n", prefix,
stop_bits[(data & 0x60) >> 5],
data_bits[(data & 0x18) >> 3],
parity[(data & 0x7)]);
pos++;
}
/* octet 5d? */
if (pos < len && !(ie->data[pos-1] & 0x80)) {
u_int8_t data = ie->data[pos];
pri_message(ctrl, "%c Duplex mode: %d, modem type: %d\n",
prefix, (data & 0x40) ? 1 : 0,data & 0x3F);
pos++;
}
}
/* Look for octet 6; this is identified by bits 5,6 == 10 */
if (pos < len &&
(ie->data[pos] & 0x60) == 0x40) {
pri_message(ctrl, "%c User information layer 2: %s (%d)\n",
prefix, l22str(ie->data[pos] & 0x1f),
ie->data[pos] & 0x1f);
pos++;
}
/* Look for octet 7; this is identified by bits 5,6 == 11 */
if (pos < len && (ie->data[pos] & 0x60) == 0x60) {
pri_message(ctrl, "%c User information layer 3: %s (%d)\n",
prefix, l32str(ie->data[pos] & 0x1f),
ie->data[pos] & 0x1f);
pos++;
/* octets 7a and 7b? */
if (pos + 1 < len && !(ie->data[pos-1] & 0x80) &&
!(ie->data[pos] & 0x80)) {
unsigned int proto;
proto = ((ie->data[pos] & 0xF) << 4 ) |
(ie->data[pos+1] & 0xF);
pri_message(ctrl, "%c Network layer: 0x%x\n", prefix,
proto );
pos += 2;
}
}
}
static int receive_bearer_capability(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
int pos=2;
if (ie->data[0] & 0x60) {
pri_error(ctrl, "!! non-standard Q.931 standard field\n");
return -1;
}
call->transcapability = ie->data[0] & 0x1f;
call->transmoderate = ie->data[1] & 0x7f;
/* octet 4.1 exists iff mode/rate is multirate */
if (call->transmoderate == TRANS_MODE_MULTIRATE) {
call->transmultiple = ie->data[pos++] & 0x7f;
}
/* Look for octet 5; this is identified by bits 5,6 == 01 */
if (pos < len &&
(ie->data[pos] & 0x60) == 0x20 ) {
/* although the layer1 is only the bottom 5 bits of the byte,
previous versions of this library passed bits 5&6 through
too, so we have to do the same for binary compatability */
call->userl1 = ie->data[pos] & 0x7f;
pos++;
/* octet 5a? */
if (pos < len && !(ie->data[pos-1] & 0x80)) {
call->rateadaption = ie->data[pos] & 0x7f;
pos++;
}
/* octets 5b through 5d? */
while (pos < len && !(ie->data[pos-1] & 0x80)) {
pos++;
}
}
/* Look for octet 6; this is identified by bits 5,6 == 10 */
if (pos < len &&
(ie->data[pos] & 0x60) == 0x40) {
call->userl2 = ie->data[pos++] & 0x1f;
}
/* Look for octet 7; this is identified by bits 5,6 == 11 */
if (pos < len &&
(ie->data[pos] & 0x60) == 0x60) {
call->userl3 = ie->data[pos++] & 0x1f;
}
return 0;
}
static int transmit_bearer_capability(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
int tc;
int pos;
/* We are ready to transmit single IE only */
if(order > 1)
return 0;
if (ctrl->subchannel && !ctrl->bri) {
/* Bearer capability is *hard coded* in GR-303 */
ie->data[0] = 0x88;
ie->data[1] = 0x90;
return 4;
}
if (call->cis_call) {
ie->data[0] = 0xa8;
ie->data[1] = 0x80;
return 4;
}
tc = call->transcapability;
ie->data[0] = 0x80 | tc;
ie->data[1] = call->transmoderate | 0x80;
pos = 2;
/* octet 4.1 exists iff mode/rate is multirate */
if (call->transmoderate == TRANS_MODE_MULTIRATE ) {
ie->data[pos++] = call->transmultiple | 0x80;
}
if ((tc & PRI_TRANS_CAP_DIGITAL) && (ctrl->switchtype == PRI_SWITCH_EUROISDN_E1) &&
(call->transmoderate == TRANS_MODE_PACKET)) {
/* Apparently EuroISDN switches don't seem to like user layer 2/3 */
return 4;
}
if ((tc & PRI_TRANS_CAP_DIGITAL) && (call->transmoderate == TRANS_MODE_64_CIRCUIT)) {
/* Unrestricted digital 64k data calls don't use user layer 2/3 */
return 4;
}
if (call->transmoderate != TRANS_MODE_PACKET) {
/* If you have an AT&T 4ESS, you don't send any more info */
if ((ctrl->switchtype != PRI_SWITCH_ATT4ESS) && (call->userl1 > -1)) {
ie->data[pos++] = call->userl1 | 0x80; /* XXX Ext bit? XXX */
if (call->userl1 == PRI_LAYER_1_ITU_RATE_ADAPT) {
ie->data[pos++] = call->rateadaption | 0x80;
}
return pos + 2;
}
ie->data[pos++] = 0xa0 | (call->userl1 & 0x1f);
if (call->userl1 == PRI_LAYER_1_ITU_RATE_ADAPT) {
ie->data[pos-1] &= ~0x80; /* clear EXT bit in octet 5 */
ie->data[pos++] = call->rateadaption | 0x80;
}
}
if (call->userl2 != -1)
ie->data[pos++] = 0xc0 | (call->userl2 & 0x1f);
if (call->userl3 != -1)
ie->data[pos++] = 0xe0 | (call->userl3 & 0x1f);
return pos + 2;
}
char *pri_plan2str(int plan)
{
static struct msgtype plans[] = {
{ PRI_INTERNATIONAL_ISDN, "International number in ISDN" },
{ PRI_NATIONAL_ISDN, "National number in ISDN" },
{ PRI_LOCAL_ISDN, "Local number in ISDN" },
{ PRI_PRIVATE, "Private numbering plan" },
{ PRI_UNKNOWN, "Unknown numbering plan" },
};
return code2str(plan, plans, sizeof(plans) / sizeof(plans[0]));
}
static char *npi2str(int plan)
{
static struct msgtype plans[] = {
{ PRI_NPI_UNKNOWN, "Unknown Number Plan" },
{ PRI_NPI_E163_E164, "ISDN/Telephony Numbering Plan (E.164/E.163)" },
{ PRI_NPI_X121, "Data Numbering Plan (X.121)" },
{ PRI_NPI_F69, "Telex Numbering Plan (F.69)" },
{ PRI_NPI_NATIONAL, "National Standard Numbering Plan" },
{ PRI_NPI_PRIVATE, "Private Numbering Plan" },
{ PRI_NPI_RESERVED, "Reserved Number Plan" },
};
return code2str(plan, plans, sizeof(plans) / sizeof(plans[0]));
}
static char *ton2str(int plan)
{
static struct msgtype plans[] = {
{ PRI_TON_UNKNOWN, "Unknown Number Type" },
{ PRI_TON_INTERNATIONAL, "International Number" },
{ PRI_TON_NATIONAL, "National Number" },
{ PRI_TON_NET_SPECIFIC, "Network Specific Number" },
{ PRI_TON_SUBSCRIBER, "Subscriber Number" },
{ PRI_TON_ABBREVIATED, "Abbreviated number" },
{ PRI_TON_RESERVED, "Reserved Number" },
};
return code2str(plan, plans, sizeof(plans) / sizeof(plans[0]));
}
static char *subaddrtype2str(int plan)
{
static struct msgtype plans[] = {
{ 0, "NSAP (X.213/ISO 8348 AD2)" },
{ 2, "User Specified" },
};
return code2str(plan, plans, sizeof(plans) / sizeof(plans[0]));
}
/* Calling Party Category (Definitions from Q.763) */
static char *cpc2str(int plan)
{
static struct msgtype plans[] = {
{ 0, "Unknown Source" },
{ 1, "Operator French" },
{ 2, "Operator English" },
{ 3, "Operator German" },
{ 4, "Operator Russian" },
{ 5, "Operator Spanish" },
{ 6, "Mut Agree Chinese" },
{ 7, "Mut Agreement" },
{ 8, "Mut Agree Japanese" },
{ 9, "National Operator" },
{ 10, "Ordinary Toll Caller" },
{ 11, "Priority Toll Caller" },
{ 12, "Data Call" },
{ 13, "Test Call" },
{ 14, "Spare" },
{ 15, "Pay Phone" },
};
return code2str(plan, plans, ARRAY_LEN(plans));
}
char *pri_pres2str(int pres)
{
static struct msgtype press[] = {
{ PRES_ALLOWED_USER_NUMBER_NOT_SCREENED, "Presentation permitted, user number not screened" },
{ PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN, "Presentation permitted, user number passed network screening" },
{ PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN, "Presentation permitted, user number failed network screening" },
{ PRES_ALLOWED_NETWORK_NUMBER, "Presentation allowed of network provided number" },
{ PRES_PROHIB_USER_NUMBER_NOT_SCREENED, "Presentation prohibited, user number not screened" },
{ PRES_PROHIB_USER_NUMBER_PASSED_SCREEN, "Presentation prohibited, user number passed network screening" },
{ PRES_PROHIB_USER_NUMBER_FAILED_SCREEN, "Presentation prohibited, user number failed network screening" },
{ PRES_PROHIB_NETWORK_NUMBER, "Presentation prohibited of network provided number" },
{ PRES_NUMBER_NOT_AVAILABLE, "Number not available" },
};
return code2str(pres, press, sizeof(press) / sizeof(press[0]));
}
static void q931_get_number(unsigned char *num, int maxlen, unsigned char *src, int len)
{
if ((len < 0) || (len > maxlen - 1)) {
num[0] = 0;
return;
}
memcpy(num, src, len);
num[len] = 0;
}
static void q931_get_subaddr_specific(unsigned char *num, int maxlen, unsigned char *src, int len, char oddflag)
{
/* User Specified */
int x;
char *ptr = (char *) num;
if (len <= 0) {
num[0] = '\0';
return;
}
if (((len * 2) + 1) > maxlen) {
len = (maxlen / 2) - 1;
}
for (x = 0; x < (len - 1); ++x) {
ptr += sprintf(ptr, "%02x", src[x]);
}
if (oddflag) {
/* ODD */
sprintf(ptr, "%01x", (src[len - 1]) >> 4);
} else {
/* EVEN */
sprintf(ptr, "%02x", src[len - 1]);
}
}
static int transmit_subaddr_helper(int full_ie, struct pri *ctrl, struct q931_party_subaddress *q931_subaddress, int msgtype, q931_ie *ie, int offset, int len, int order)
{
size_t datalen;
if (!q931_subaddress->valid) {
return 0;
}
datalen = q931_subaddress->length;
if (!q931_subaddress->type) {
/* 0 = NSAP */
/* 0 = Odd/Even indicator */
ie->data[0] = 0x80;
} else {
/* 2 = User Specified */
ie->data[0] = q931_subaddress->odd_even_indicator ? 0xA8 : 0xA0;
}
memcpy(ie->data + offset, q931_subaddress->data, datalen);
return datalen + (offset + 2);
}
static int receive_subaddr_helper(int full_ie, struct pri *ctrl, struct q931_party_subaddress *q931_subaddress, int msgtype, q931_ie *ie, int offset, int len)
{
if (len <= 0) {
return -1;
}
q931_subaddress->valid = 1;
q931_subaddress->length = len;
/* type: 0 = NSAP, 2 = User Specified */
q931_subaddress->type = ((ie->data[0] & 0x70) >> 4);
q931_subaddress->odd_even_indicator = (ie->data[0] & 0x08) ? 1 : 0;
q931_get_number(q931_subaddress->data, sizeof(q931_subaddress->data),
ie->data + offset, len);
return 0;
}
static void dump_subaddr_helper(int full_ie, struct pri *ctrl, q931_ie *ie, int offset, int len, int datalen, char prefix, const char *named)
{
unsigned char cnum[256];
if (!(ie->data[0] & 0x70)) {
/* NSAP */
q931_get_number(cnum, sizeof(cnum), ie->data + offset, datalen);
} else {
/* User Specified */
q931_get_subaddr_specific(cnum, sizeof(cnum), ie->data + offset, datalen,
ie->data[0] & 0x08);
}
pri_message(ctrl,
"%c %s Sub-Address (len=%2d) [ Ext: %d Type: %s (%d) O: %d '%s' ]\n",
prefix, named, len, ie->data[0] >> 7,
subaddrtype2str((ie->data[0] & 0x70) >> 4), (ie->data[0] & 0x70) >> 4,
(ie->data[0] & 0x08) >> 3, cnum);
}
static void dump_called_party_number(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
unsigned char cnum[256];
q931_get_number(cnum, sizeof(cnum), ie->data + 1, len - 3);
pri_message(ctrl, "%c Called Number (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d) '%s' ]\n",
prefix, len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f, cnum);
}
static void dump_called_party_subaddr(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
dump_subaddr_helper(full_ie, ctrl, ie, 1 , len, len - 3, prefix, "Called");
}
static void dump_calling_party_number(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
unsigned char cnum[256];
if (ie->data[0] & 0x80)
q931_get_number(cnum, sizeof(cnum), ie->data + 1, len - 3);
else
q931_get_number(cnum, sizeof(cnum), ie->data + 2, len - 4);
pri_message(ctrl, "%c Calling Number (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d)\n", prefix, len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f);
if (ie->data[0] & 0x80)
pri_message(ctrl, "%c Presentation: %s (%d) '%s' ]\n", prefix, pri_pres2str(0), 0, cnum);
else
pri_message(ctrl, "%c Presentation: %s (%d) '%s' ]\n", prefix, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f, cnum);
}
static void dump_calling_party_subaddr(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
dump_subaddr_helper(full_ie, ctrl, ie, 1 , len, len - 3, prefix, "Calling");
}
static void dump_calling_party_category(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
pri_message(ctrl, "%c Calling Party Category (len=%2d) [ Ext: %d Cat: %s (%d) ]\n",
prefix, len, ie->data[0] >> 7, cpc2str(ie->data[0] & 0x0F), ie->data[0] & 0x0F);
}
static void dump_redirecting_number(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
unsigned char cnum[256];
int i = 0;
/* To follow Q.931 (4.5.1), we must search for start of octet 4 by
walking through all bytes until one with ext bit (8) set to 1 */
do {
switch(i) {
case 0: /* Octet 3 */
pri_message(ctrl, "%c Redirecting Number (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d)",
prefix, len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f);
break;
case 1: /* Octet 3a */
pri_message(ctrl, "\n%c Ext: %d Presentation: %s (%d)",
prefix, ie->data[1] >> 7, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f);
break;
case 2: /* Octet 3b */
pri_message(ctrl, "\n%c Ext: %d Reason: %s (%d)",
prefix, ie->data[2] >> 7, redirection_reason2str(ie->data[2] & 0x7f), ie->data[2] & 0x7f);
break;
}
} while(!(ie->data[i++]& 0x80));
q931_get_number(cnum, sizeof(cnum), ie->data + i, ie->len - i);
pri_message(ctrl, " '%s' ]\n", cnum);
}
static void dump_redirection_number(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
unsigned char cnum[256];
int i = 0;
/* To follow Q.931 (4.5.1), we must search for start of octet 4 by
walking through all bytes until one with ext bit (8) set to 1 */
do {
switch (i) {
case 0: /* Octet 3 */
pri_message(ctrl,
"%c Redirection Number (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d)",
prefix, len, ie->data[0] >> 7,
ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07,
npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f);
break;
case 1: /* Octet 3a */
pri_message(ctrl, "\n%c Ext: %d Presentation: %s (%d)",
prefix, ie->data[1] >> 7, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f);
break;
}
} while (!(ie->data[i++] & 0x80));
q931_get_number(cnum, sizeof(cnum), ie->data + i, ie->len - i);
pri_message(ctrl, " '%s' ]\n", cnum);
}
static int receive_connected_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
int i = 0;
call->remote_id.number.valid = 1;
call->remote_id.number.presentation =
PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED;
/* To follow Q.931 (4.5.1), we must search for start of octet 4 by
walking through all bytes until one with ext bit (8) set to 1 */
do {
switch (i) {
case 0:
call->remote_id.number.plan = ie->data[i] & 0x7f;
break;
case 1:
/* Keep only the presentation and screening fields */
call->remote_id.number.presentation =
ie->data[i] & (PRI_PRES_RESTRICTION | PRI_PRES_NUMBER_TYPE);
break;
}
} while (!(ie->data[i++] & 0x80));
q931_get_number((unsigned char *) call->remote_id.number.str, sizeof(call->remote_id.number.str), ie->data + i, ie->len - i);
return 0;
}
static int transmit_connected_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
size_t datalen;
if (!call->local_id.number.valid) {
return 0;
}
datalen = strlen(call->local_id.number.str);
ie->data[0] = call->local_id.number.plan;
ie->data[1] = 0x80 | call->local_id.number.presentation;
memcpy(ie->data + 2, call->local_id.number.str, datalen);
return datalen + (2 + 2);
}
static void dump_connected_number(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
unsigned char cnum[256];
int i = 0;
/* To follow Q.931 (4.5.1), we must search for start of octet 4 by
walking through all bytes until one with ext bit (8) set to 1 */
do {
switch(i) {
case 0: /* Octet 3 */
pri_message(ctrl, "%c Connected Number (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d)",
prefix, len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f);
break;
case 1: /* Octet 3a */
pri_message(ctrl, "\n%c Ext: %d Presentation: %s (%d)",
prefix, ie->data[1] >> 7, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f);
break;
}
} while(!(ie->data[i++]& 0x80));
q931_get_number(cnum, sizeof(cnum), ie->data + i, ie->len - i);
pri_message(ctrl, " '%s' ]\n", cnum);
}
static int receive_connected_subaddr(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
if (len < 3) {
return -1;
}
return receive_subaddr_helper(full_ie, ctrl, &call->remote_id.subaddress, msgtype, ie,
1, len - 3);
}
static int transmit_connected_subaddr(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
return transmit_subaddr_helper(full_ie, ctrl, &call->local_id.subaddress, msgtype, ie,
1, len, order);
}
static void dump_connected_subaddr(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
dump_subaddr_helper(full_ie, ctrl, ie, 1 , len, len - 3, prefix, "Connected");
}
static int receive_redirecting_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
int i = 0;
call->redirecting.from.number.valid = 1;
call->redirecting.from.number.presentation =
PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED;
call->redirecting.reason = PRI_REDIR_UNKNOWN;
/* To follow Q.931 (4.5.1), we must search for start of octet 4 by
walking through all bytes until one with ext bit (8) set to 1 */
do {
switch (i) {
case 0:
call->redirecting.from.number.plan = ie->data[i] & 0x7f;
break;
case 1:
/* Keep only the presentation and screening fields */
call->redirecting.from.number.presentation =
ie->data[i] & (PRI_PRES_RESTRICTION | PRI_PRES_NUMBER_TYPE);
break;
case 2:
call->redirecting.reason = ie->data[i] & 0x0f;
break;
}
} while (!(ie->data[i++] & 0x80));
q931_get_number((unsigned char *) call->redirecting.from.number.str, sizeof(call->redirecting.from.number.str), ie->data + i, ie->len - i);
return 0;
}
static int transmit_redirecting_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
size_t datalen;
if (order > 1)
return 0;
if (!call->redirecting.from.number.valid) {
return 0;
}
datalen = strlen(call->redirecting.from.number.str);
ie->data[0] = call->redirecting.from.number.plan;
#if 1
/* ETSI and Q.952 do not define the screening field */
ie->data[1] = call->redirecting.from.number.presentation & PRI_PRES_RESTRICTION;
#else
/* Q.931 defines the screening field */
ie->data[1] = call->redirecting.from.number.presentation;
#endif
ie->data[2] = (call->redirecting.reason & 0x0f) | 0x80;
memcpy(ie->data + 3, call->redirecting.from.number.str, datalen);
return datalen + (3 + 2);
}
static void dump_redirecting_subaddr(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
dump_subaddr_helper(full_ie, ctrl, ie, 2, len, len - 4, prefix, "Redirecting");
}
static int receive_redirection_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
int i = 0;
call->redirection_number.valid = 1;
call->redirection_number.presentation =
PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED;
/* To follow Q.931 (4.5.1), we must search for start of octet 4 by
walking through all bytes until one with ext bit (8) set to 1 */
do {
switch (i) {
case 0:
call->redirection_number.plan = ie->data[i] & 0x7f;
break;
case 1:
/* Keep only the presentation and screening fields */
call->redirection_number.presentation =
ie->data[i] & (PRI_PRES_RESTRICTION | PRI_PRES_NUMBER_TYPE);
break;
}
} while (!(ie->data[i++] & 0x80));
q931_get_number((unsigned char *) call->redirection_number.str, sizeof(call->redirection_number.str), ie->data + i, ie->len - i);
return 0;
}
static int transmit_redirection_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
size_t datalen;
if (order > 1) {
return 0;
}
if (!call->redirection_number.valid) {
return 0;
}
datalen = strlen(call->redirection_number.str);
ie->data[0] = call->redirection_number.plan;
ie->data[1] = (call->redirection_number.presentation & PRI_PRES_RESTRICTION) | 0x80;
memcpy(ie->data + 2, call->redirection_number.str, datalen);
return datalen + (2 + 2);
}
static int receive_calling_party_subaddr(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
if (len < 3) {
return -1;
}
return receive_subaddr_helper(full_ie, ctrl, &call->remote_id.subaddress, msgtype, ie,
1, len - 3);
}
static int transmit_calling_party_subaddr(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
return transmit_subaddr_helper(full_ie, ctrl, &call->local_id.subaddress, msgtype, ie,
1, len, order);
}
static int receive_called_party_subaddr(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
if (len < 3) {
return -1;
}
return receive_subaddr_helper(full_ie, ctrl, &call->called.subaddress, msgtype, ie, 1,
len - 3);
}
static int transmit_called_party_subaddr(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
return transmit_subaddr_helper(full_ie, ctrl, &call->called.subaddress, msgtype, ie,
1, len, order);
}
static int receive_called_party_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
size_t called_len;
size_t max_len;
char *called_end;
if (len < 3) {
return -1;
}
call->called.number.valid = 1;
call->called.number.plan = ie->data[0] & 0x7f;
if (msgtype == Q931_SETUP) {
q931_get_number((unsigned char *) call->called.number.str,
sizeof(call->called.number.str), ie->data + 1, len - 3);
} else if (call->ourcallstate == Q931_CALL_STATE_OVERLAP_RECEIVING) {
/*
* Since we are receiving overlap digits now, we need to append
* them to any previously received digits in call->called.number.str.
*/
called_len = strlen(call->called.number.str);
called_end = call->called.number.str + called_len;
max_len = (sizeof(call->called.number.str) - 1) - called_len;
if (max_len < len - 3) {
called_len = max_len;
} else {
called_len = len - 3;
}
strncat(called_end, (char *) ie->data + 1, called_len);
}
q931_get_number((unsigned char *) call->overlap_digits, sizeof(call->overlap_digits),
ie->data + 1, len - 3);
return 0;
}
static int transmit_called_party_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
size_t datalen;
if (!call->called.number.valid) {
return 0;
}
datalen = strlen(call->overlap_digits);
ie->data[0] = 0x80 | call->called.number.plan;
memcpy(ie->data + 1, call->overlap_digits, datalen);
return datalen + (1 + 2);
}
static int receive_calling_party_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
int i = 0;
call->remote_id.number.valid = 1;
call->remote_id.number.presentation =
PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED;
/* To follow Q.931 (4.5.1), we must search for start of octet 4 by
walking through all bytes until one with ext bit (8) set to 1 */
do {
switch (i) {
case 0:
call->remote_id.number.plan = ie->data[i] & 0x7f;
break;
case 1:
/* Keep only the presentation and screening fields */
call->remote_id.number.presentation =
ie->data[i] & (PRI_PRES_RESTRICTION | PRI_PRES_NUMBER_TYPE);
break;
}
} while (!(ie->data[i++] & 0x80));
q931_get_number((unsigned char *) call->remote_id.number.str,
sizeof(call->remote_id.number.str), ie->data + i, ie->len - i);
return 0;
}
static int transmit_calling_party_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
size_t datalen;
if (!call->local_id.number.valid) {
return 0;
}
datalen = strlen(call->local_id.number.str);
ie->data[0] = call->local_id.number.plan;
ie->data[1] = 0x80 | call->local_id.number.presentation;
memcpy(ie->data + 2, call->local_id.number.str, datalen);
return datalen + (2 + 2);
}
static void dump_user_user(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
int x;
pri_message(ctrl, "%c User-User Information (len=%2d) [", prefix, len);
for (x=0;x<ie->len;x++)
pri_message(ctrl, " %02x", ie->data[x] & 0x7f);
pri_message(ctrl, " ]\n");
}
static int receive_user_user(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
call->useruserprotocoldisc = ie->data[0] & 0xff;
if (call->useruserprotocoldisc == 4) /* IA5 */
q931_get_number((unsigned char *) call->useruserinfo, sizeof(call->useruserinfo), ie->data + 1, len - 3);
return 0;
}
static int transmit_user_user(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
int datalen = strlen(call->useruserinfo);
if (datalen > 0) {
/* Restricted to 35 characters */
if (msgtype == Q931_USER_INFORMATION) {
if (datalen > 260)
datalen = 260;
} else {
if (datalen > 35)
datalen = 35;
}
ie->data[0] = 4; /* IA5 characters */
memcpy(&ie->data[1], call->useruserinfo, datalen);
call->useruserinfo[0] = '\0';
return datalen + 3;
}
return 0;
}
static void dump_change_status(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
int x;
pri_message(ctrl, "%c Change Status Information (len=%2d) [", prefix, len);
for (x=0; x<ie->len; x++) {
pri_message(ctrl, " %02x", ie->data[x] & 0x7f);
}
pri_message(ctrl, " ]\n");
}
static int receive_change_status(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
call->changestatus = ie->data[0] & 0x0f;
return 0;
}
static int transmit_change_status(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
ie->data[0] = 0xc0 | call->changestatus;
return 3;
}
static char *prog2str(int prog)
{
static struct msgtype progs[] = {
{ Q931_PROG_CALL_NOT_E2E_ISDN, "Call is not end-to-end ISDN; further call progress information may be available inband." },
{ Q931_PROG_CALLED_NOT_ISDN, "Called equipment is non-ISDN." },
{ Q931_PROG_CALLER_NOT_ISDN, "Calling equipment is non-ISDN." },
{ Q931_PROG_INBAND_AVAILABLE, "Inband information or appropriate pattern now available." },
{ Q931_PROG_DELAY_AT_INTERF, "Delay in response at called Interface." },
{ Q931_PROG_INTERWORKING_WITH_PUBLIC, "Interworking with a public network." },
{ Q931_PROG_INTERWORKING_NO_RELEASE, "Interworking with a network unable to supply a release signal." },
{ Q931_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER, "Interworking with a network unable to supply a release signal before answer." },
{ Q931_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER, "Interworking with a network unable to supply a release signal after answer." },
};
return code2str(prog, progs, sizeof(progs) / sizeof(progs[0]));
}
static char *coding2str(int cod)
{
static struct msgtype cods[] = {
{ CODE_CCITT, "CCITT (ITU) standard" },
{ CODE_INTERNATIONAL, "Non-ITU international standard" },
{ CODE_NATIONAL, "National standard" },
{ CODE_NETWORK_SPECIFIC, "Network specific standard" },
};
return code2str(cod, cods, sizeof(cods) / sizeof(cods[0]));
}
static char *loc2str(int loc)
{
static struct msgtype locs[] = {
{ LOC_USER, "User" },
{ LOC_PRIV_NET_LOCAL_USER, "Private network serving the local user" },
{ LOC_PUB_NET_LOCAL_USER, "Public network serving the local user" },
{ LOC_TRANSIT_NET, "Transit network" },
{ LOC_PUB_NET_REMOTE_USER, "Public network serving the remote user" },
{ LOC_PRIV_NET_REMOTE_USER, "Private network serving the remote user" },
{ LOC_INTERNATIONAL_NETWORK, "International network" },
{ LOC_NETWORK_BEYOND_INTERWORKING, "Network beyond the interworking point" },
};
return code2str(loc, locs, sizeof(locs) / sizeof(locs[0]));
}
static void dump_progress_indicator(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
pri_message(ctrl, "%c Progress Indicator (len=%2d) [ Ext: %d Coding: %s (%d) 0: %d Location: %s (%d)\n",
prefix, len, ie->data[0] >> 7, coding2str((ie->data[0] & 0x60) >> 5), (ie->data[0] & 0x60) >> 5,
(ie->data[0] & 0x10) >> 4, loc2str(ie->data[0] & 0xf), ie->data[0] & 0xf);
pri_message(ctrl, "%c Ext: %d Progress Description: %s (%d) ]\n",
prefix, ie->data[1] >> 7, prog2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f);
}
static int receive_display(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
unsigned char *data;
switch (msgtype) {
case Q931_SETUP:
case Q931_CONNECT:
/*
* Only keep the display message on SETUP and CONNECT messages
* as the remote name.
*/
break;
default:
return 0;
}
call->remote_id.name.valid = 1;
data = ie->data;
if (data[0] & 0x80) {
/* Skip over character set */
data++;
len--;
}
call->remote_id.name.char_set = PRI_CHAR_SET_ISO8859_1;
q931_get_number((unsigned char *) call->remote_id.name.str, sizeof(call->remote_id.name.str), data, len - 2);
if (call->remote_id.name.str[0]) {
call->remote_id.name.presentation = PRI_PRES_ALLOWED;
} else {
call->remote_id.name.presentation = PRI_PRES_RESTRICTED;
}
return 0;
}
static int transmit_display(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
size_t datalen;
int i;
i = 0;
if (!call->local_id.name.valid || !call->local_id.name.str[0]) {
return 0;
}
switch (ctrl->switchtype) {
case PRI_SWITCH_QSIG:
/* Q.SIG supports names */
return 0;
case PRI_SWITCH_EUROISDN_E1:
case PRI_SWITCH_EUROISDN_T1:
if (ctrl->localtype == PRI_CPE) {
return 0;
}
break;
default:
/* Prefix name with character set indicator. */
ie->data[0] = 0xb1;
++i;
break;
}
datalen = strlen(call->local_id.name.str);
memcpy(ie->data + i, call->local_id.name.str, datalen);
return 2 + i + datalen;
}
static int receive_progress_indicator(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
call->progloc = ie->data[0] & 0xf;
call->progcode = (ie->data[0] & 0x60) >> 5;
switch (call->progress = (ie->data[1] & 0x7f)) {
case Q931_PROG_CALL_NOT_E2E_ISDN:
call->progressmask |= PRI_PROG_CALL_NOT_E2E_ISDN;
break;
case Q931_PROG_CALLED_NOT_ISDN:
call->progressmask |= PRI_PROG_CALLED_NOT_ISDN;
break;
case Q931_PROG_CALLER_NOT_ISDN:
call->progressmask |= PRI_PROG_CALLER_NOT_ISDN;
break;
case Q931_PROG_CALLER_RETURNED_TO_ISDN:
call->progressmask |= PRI_PROG_CALLER_RETURNED_TO_ISDN;
break;
case Q931_PROG_INBAND_AVAILABLE:
call->progressmask |= PRI_PROG_INBAND_AVAILABLE;
break;
case Q931_PROG_DELAY_AT_INTERF:
call->progressmask |= PRI_PROG_DELAY_AT_INTERF;
break;
case Q931_PROG_INTERWORKING_WITH_PUBLIC:
call->progressmask |= PRI_PROG_INTERWORKING_WITH_PUBLIC;
break;
case Q931_PROG_INTERWORKING_NO_RELEASE:
call->progressmask |= PRI_PROG_INTERWORKING_NO_RELEASE;
break;
case Q931_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER:
call->progressmask |= PRI_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER;
break;
case Q931_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER:
call->progressmask |= PRI_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER;
break;
default:
pri_error(ctrl, "XXX Invalid Progress indicator value received: %02x\n",(ie->data[1] & 0x7f));
break;
}
return 0;
}
static int transmit_facility(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
struct apdu_event *tmp;
int i = 0;
for (tmp = call->apdus; tmp; tmp = tmp->next) {
if ((tmp->message == msgtype) && !tmp->sent)
break;
}
if (!tmp) /* No APDU found */
return 0;
if (ctrl->debug & PRI_DEBUG_APDU) {
pri_message(ctrl, "Adding facility ie contents to send in %s message:\n",
msg2str(msgtype));
facility_decode_dump(ctrl, tmp->apdu, tmp->apdu_len);
}
if (tmp->apdu_len > 235) { /* TODO: find out how much space we can use */
pri_message(ctrl, "Requested APDU (%d bytes) is too long\n", tmp->apdu_len);
return 0;
}
memcpy(&ie->data[i], tmp->apdu, tmp->apdu_len);
i += tmp->apdu_len;
tmp->sent = 1;
return i + 2;
}
static int receive_facility(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
struct fac_extension_header header;
struct rose_message rose;
const unsigned char *pos;
const unsigned char *end;
pos = ie->data;
end = ie->data + ie->len;
/* Make sure we have enough room for the protocol profile ie octet(s) */
if (end < pos + 2) {
return -1;
}
switch (*pos & Q932_PROTOCOL_MASK) {
case Q932_PROTOCOL_ROSE:
case Q932_PROTOCOL_EXTENSIONS:
break;
default:
case Q932_PROTOCOL_CMIP:
case Q932_PROTOCOL_ACSE:
if (ctrl->debug & PRI_DEBUG_APDU) {
pri_message(ctrl,
"!! Don't know how to handle Q.932 Protocol Profile type 0x%X\n",
*pos & Q932_PROTOCOL_MASK);
}
return -1;
}
if (!(*pos & 0x80)) {
/* DMS-100 Service indicator octet - Just ignore for now */
++pos;
}
++pos;
if (ctrl->debug & PRI_DEBUG_APDU) {
asn1_dump(ctrl, pos, end);
}
pos = fac_dec_extension_header(ctrl, pos, end, &header);
if (!pos) {
return -1;
}
if (header.npp_present) {
if (ctrl->debug & PRI_DEBUG_APDU) {
pri_message(ctrl,
"!! Don't know how to handle Network Protocol Profile type 0x%X\n",
header.npp);
}
return -1;
}
pos = rose_decode(ctrl, pos, end, &rose);
if (!pos) {
return -1;
}
switch (rose.type) {
case ROSE_COMP_TYPE_INVOKE:
rose_handle_invoke(ctrl, call, msgtype, ie, &header, &rose.component.invoke);
break;
case ROSE_COMP_TYPE_RESULT:
rose_handle_result(ctrl, call, msgtype, ie, &header, &rose.component.result);
break;
case ROSE_COMP_TYPE_ERROR:
rose_handle_error(ctrl, call, msgtype, ie, &header, &rose.component.error);
break;
case ROSE_COMP_TYPE_REJECT:
rose_handle_reject(ctrl, call, msgtype, ie, &header, &rose.component.reject);
break;
default:
return -1;
}
return 0;
}
static int transmit_progress_indicator(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
int code, mask;
/* Can't send progress indicator on GR-303 -- EVER! */
if (ctrl->subchannel && !ctrl->bri)
return 0;
if (call->progressmask > 0) {
if (call->progressmask & (mask = PRI_PROG_CALL_NOT_E2E_ISDN))
code = Q931_PROG_CALL_NOT_E2E_ISDN;
else if (call->progressmask & (mask = PRI_PROG_CALLED_NOT_ISDN))
code = Q931_PROG_CALLED_NOT_ISDN;
else if (call->progressmask & (mask = PRI_PROG_CALLER_NOT_ISDN))
code = Q931_PROG_CALLER_NOT_ISDN;
else if (call->progressmask & (mask = PRI_PROG_INBAND_AVAILABLE))
code = Q931_PROG_INBAND_AVAILABLE;
else if (call->progressmask & (mask = PRI_PROG_DELAY_AT_INTERF))
code = Q931_PROG_DELAY_AT_INTERF;
else if (call->progressmask & (mask = PRI_PROG_INTERWORKING_WITH_PUBLIC))
code = Q931_PROG_INTERWORKING_WITH_PUBLIC;
else if (call->progressmask & (mask = PRI_PROG_INTERWORKING_NO_RELEASE))
code = Q931_PROG_INTERWORKING_NO_RELEASE;
else if (call->progressmask & (mask = PRI_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER))
code = Q931_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER;
else if (call->progressmask & (mask = PRI_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER))
code = Q931_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER;
else {
code = 0;
pri_error(ctrl, "XXX Undefined progress bit: %x\n", call->progressmask);
}
if (code) {
ie->data[0] = 0x80 | (call->progcode << 5) | (call->progloc);
ie->data[1] = 0x80 | code;
call->progressmask &= ~mask;
return 4;
}
}
/* Leave off */
return 0;
}
static int transmit_call_state(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
ie->data[0] = Q931_CALL_STATE_NULL;
switch (call->ourcallstate) {
case Q931_CALL_STATE_NULL:
case Q931_CALL_STATE_CALL_INITIATED:
case Q931_CALL_STATE_OVERLAP_SENDING:
case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING:
case Q931_CALL_STATE_CALL_DELIVERED:
case Q931_CALL_STATE_CALL_PRESENT:
case Q931_CALL_STATE_CALL_RECEIVED:
case Q931_CALL_STATE_CONNECT_REQUEST:
case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING:
case Q931_CALL_STATE_ACTIVE:
case Q931_CALL_STATE_DISCONNECT_REQUEST:
case Q931_CALL_STATE_DISCONNECT_INDICATION:
case Q931_CALL_STATE_SUSPEND_REQUEST:
case Q931_CALL_STATE_RESUME_REQUEST:
case Q931_CALL_STATE_RELEASE_REQUEST:
case Q931_CALL_STATE_CALL_ABORT:
case Q931_CALL_STATE_OVERLAP_RECEIVING:
case Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE:
case Q931_CALL_STATE_RESTART_REQUEST:
case Q931_CALL_STATE_RESTART:
ie->data[0] = call->ourcallstate;
break;
case Q931_CALL_STATE_NOT_SET:
break;
}
return 3;
}
static int receive_call_state(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
call->sugcallstate = ie->data[0] & 0x3f;
return 0;
}
/*!
* \brief Convert the internal Q.931 call state to a string.
*
* \param callstate Internal Q.931 call state.
*
* \return String equivalent of the given Q.931 call state.
*/
const char *q931_call_state_str(enum Q931_CALL_STATE callstate)
{
static struct msgtype callstates[] = {
/* *INDENT-OFF* */
{ Q931_CALL_STATE_NULL, "Null" },
{ Q931_CALL_STATE_CALL_INITIATED, "Call Initiated" },
{ Q931_CALL_STATE_OVERLAP_SENDING, "Overlap Sending" },
{ Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING, "Outgoing Call Proceeding" },
{ Q931_CALL_STATE_CALL_DELIVERED, "Call Delivered" },
{ Q931_CALL_STATE_CALL_PRESENT, "Call Present" },
{ Q931_CALL_STATE_CALL_RECEIVED, "Call Received" },
{ Q931_CALL_STATE_CONNECT_REQUEST, "Connect Request" },
{ Q931_CALL_STATE_INCOMING_CALL_PROCEEDING, "Incoming Call Proceeding" },
{ Q931_CALL_STATE_ACTIVE, "Active" },
{ Q931_CALL_STATE_DISCONNECT_REQUEST, "Disconnect Request" },
{ Q931_CALL_STATE_DISCONNECT_INDICATION, "Disconnect Indication" },
{ Q931_CALL_STATE_SUSPEND_REQUEST, "Suspend Request" },
{ Q931_CALL_STATE_RESUME_REQUEST, "Resume Request" },
{ Q931_CALL_STATE_RELEASE_REQUEST, "Release Request" },
{ Q931_CALL_STATE_CALL_ABORT, "Call Abort" },
{ Q931_CALL_STATE_OVERLAP_RECEIVING, "Overlap Receiving" },
{ Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE, "Call Independent Service" },
{ Q931_CALL_STATE_RESTART_REQUEST, "Restart Request" },
{ Q931_CALL_STATE_RESTART, "Restart" },
{ Q931_CALL_STATE_NOT_SET, "Not set. Internal use only." },
/* *INDENT-ON* */
};
return code2str(callstate, callstates, ARRAY_LEN(callstates));
}
/*!
* \internal
* \brief Convert the Q.932 supplementary hold state to a string.
*
* \param state Q.932 supplementary hold state.
*
* \return String equivalent of the given hold state.
*/
static const char *q931_hold_state_str(enum Q931_HOLD_STATE state)
{
static struct msgtype hold_states[] = {
/* *INDENT-OFF* */
{ Q931_HOLD_STATE_IDLE, "Idle" },
{ Q931_HOLD_STATE_HOLD_REQ, "Hold Request" },
{ Q931_HOLD_STATE_HOLD_IND, "Hold Indication" },
{ Q931_HOLD_STATE_CALL_HELD, "Call Held" },
{ Q931_HOLD_STATE_RETRIEVE_REQ, "Retrieve Request" },
{ Q931_HOLD_STATE_RETRIEVE_IND, "Retrieve Indication" },
/* *INDENT-ON* */
};
return code2str(state, hold_states, ARRAY_LEN(hold_states));
}
static void dump_call_state(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
pri_message(ctrl, "%c Call State (len=%2d) [ Ext: %d Coding: %s (%d) Call state: %s (%d)\n",
prefix, len, ie->data[0] >> 7, coding2str((ie->data[0] & 0xC0) >> 6), (ie->data[0] & 0xC0) >> 6,
q931_call_state_str(ie->data[0] & 0x3f), ie->data[0] & 0x3f);
}
static void dump_call_identity(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
int x;
pri_message(ctrl, "%c Call Identity (len=%2d) [ ", prefix, len);
for (x=0;x<ie->len;x++)
pri_message(ctrl, "0x%02X ", ie->data[x]);
pri_message(ctrl, " ]\n");
}
static void dump_time_date(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
pri_message(ctrl, "%c Time Date (len=%2d) [ ", prefix, len);
if (ie->len > 0)
pri_message(ctrl, "%02d", ie->data[0]);
if (ie->len > 1)
pri_message(ctrl, "-%02d", ie->data[1]);
if (ie->len > 2)
pri_message(ctrl, "-%02d", ie->data[2]);
if (ie->len > 3)
pri_message(ctrl, " %02d", ie->data[3]);
if (ie->len > 4)
pri_message(ctrl, ":%02d", ie->data[4]);
if (ie->len > 5)
pri_message(ctrl, ":%02d", ie->data[5]);
pri_message(ctrl, " ]\n");
}
static void dump_keypad_facility(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
char tmp[64];
if (ie->len == 0 || ie->len > sizeof(tmp))
return;
memcpy(tmp, ie->data, ie->len);
tmp[ie->len] = '\0';
pri_message(ctrl, "%c Keypad Facility (len=%2d) [ %s ]\n", prefix, ie->len, tmp );
}
static int receive_keypad_facility(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
int mylen;
if (ie->len == 0)
return -1;
if (ie->len > (sizeof(call->keypad_digits) - 1))
mylen = (sizeof(call->keypad_digits) - 1);
else
mylen = ie->len;
memcpy(call->keypad_digits, ie->data, mylen);
call->keypad_digits[mylen] = 0;
return 0;
}
static int transmit_keypad_facility(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
int sublen;
sublen = strlen(call->keypad_digits);
if (sublen) {
libpri_copy_string((char *) ie->data, call->keypad_digits, sizeof(call->keypad_digits));
return sublen + 2;
} else
return 0;
}
static void dump_display(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
int x, y;
char *buf = malloc(len + 1);
char tmp[80] = "";
if (buf) {
x=y=0;
if ((x < ie->len) && (ie->data[x] & 0x80)) {
sprintf(tmp, "Charset: %02x ", ie->data[x] & 0x7f);
++x;
}
for (y=x; x<ie->len; x++)
buf[x] = ie->data[x] & 0x7f;
buf[x] = '\0';
pri_message(ctrl, "%c Display (len=%2d) %s[ %s ]\n", prefix, ie->len, tmp, &buf[y]);
free(buf);
}
}
#define CHECK_OVERFLOW(limit) \
if (tmpptr - tmp + limit >= sizeof(tmp)) { \
*tmpptr = '\0'; \
pri_message(ctrl, "%s", tmpptr = tmp); \
}
static void dump_ie_data(struct pri *ctrl, unsigned char *c, int len)
{
static char hexs[16] = "0123456789ABCDEF";
char tmp[1024], *tmpptr;
int lastascii = 0;
tmpptr = tmp;
for (; len; --len, ++c) {
CHECK_OVERFLOW(7);
if (isprint(*c)) {
if (!lastascii) {
if (tmpptr != tmp) {
*tmpptr++ = ',';
*tmpptr++ = ' ';
}
*tmpptr++ = '\'';
lastascii = 1;
}
*tmpptr++ = *c;
} else {
if (lastascii) {
*tmpptr++ = '\'';
lastascii = 0;
}
if (tmpptr != tmp) {
*tmpptr++ = ',';
*tmpptr++ = ' ';
}
*tmpptr++ = '0';
*tmpptr++ = 'x';
*tmpptr++ = hexs[(*c >> 4) & 0x0f];
*tmpptr++ = hexs[(*c) & 0x0f];
}
}
if (lastascii)
*tmpptr++ = '\'';
*tmpptr = '\0';
pri_message(ctrl, "%s", tmp);
}
static void dump_facility(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
pri_message(ctrl, "%c Facility (len=%2d, codeset=%d) [ ", prefix, len, Q931_IE_CODESET(full_ie));
dump_ie_data(ctrl, ie->data, ie->len);
pri_message(ctrl, " ]\n");
#if 0 /* Lets not dump parse of facility contents here anymore. */
/*
* The ASN.1 decode dump has already been done when the facility ie was added to the outgoing
* message or the ASN.1 decode dump will be done when the facility ie is processed on incoming
* messages. This dump is redundant and very noisy.
*/
if (ie->len > 1) {
int dataat = (ie->data[0] & 0x80) ? 1 : 2;
pri_message(ctrl, "PROTOCOL %02X\n", ie->data[0] & Q932_PROTOCOL_MASK);
asn1_dump(ctrl, ie->data + dataat, ie->data + ie->len);
}
#endif /* Lets not dump parse of facility contents here anymore. */
}
static void dump_network_spec_fac(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
pri_message(ctrl, "%c Network-Specific Facilities (len=%2d) [ ", prefix, ie->len);
if (ie->data[0] == 0x00) {
pri_message(ctrl, "%s", code2str(ie->data[1], facilities, ARRAY_LEN(facilities)));
}
else
dump_ie_data(ctrl, ie->data, ie->len);
pri_message(ctrl, " ]\n");
}
static int receive_network_spec_fac(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
return 0;
}
static int transmit_network_spec_fac(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
/* We are ready to transmit single IE only */
if (order > 1)
return 0;
if (ctrl->nsf != PRI_NSF_NONE) {
ie->data[0] = 0x00;
ie->data[1] = ctrl->nsf;
return 4;
}
/* Leave off */
return 0;
}
char *pri_cause2str(int cause)
{
return code2str(cause, causes, sizeof(causes) / sizeof(causes[0]));
}
static char *pri_causeclass2str(int cause)
{
static struct msgtype causeclasses[] = {
{ 0, "Normal Event" },
{ 1, "Normal Event" },
{ 2, "Network Congestion (resource unavailable)" },
{ 3, "Service or Option not Available" },
{ 4, "Service or Option not Implemented" },
{ 5, "Invalid message (e.g. parameter out of range)" },
{ 6, "Protocol Error (e.g. unknown message)" },
{ 7, "Interworking" },
};
return code2str(cause, causeclasses, sizeof(causeclasses) / sizeof(causeclasses[0]));
}
static void dump_cause(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
int x;
pri_message(ctrl, "%c Cause (len=%2d) [ Ext: %d Coding: %s (%d) Spare: %d Location: %s (%d)\n",
prefix, len, ie->data[0] >> 7, coding2str((ie->data[0] & 0x60) >> 5), (ie->data[0] & 0x60) >> 5,
(ie->data[0] & 0x10) >> 4, loc2str(ie->data[0] & 0xf), ie->data[0] & 0xf);
pri_message(ctrl, "%c Ext: %d Cause: %s (%d), class = %s (%d) ]\n",
prefix, (ie->data[1] >> 7), pri_cause2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f,
pri_causeclass2str((ie->data[1] & 0x7f) >> 4), (ie->data[1] & 0x7f) >> 4);
if (ie->len < 3)
return;
/* Dump cause data in readable form */
switch(ie->data[1] & 0x7f) {
case PRI_CAUSE_IE_NONEXIST:
for (x=2;x<ie->len;x++)
pri_message(ctrl, "%c Cause data %d: %02x (%d, %s IE)\n", prefix, x-1, ie->data[x], ie->data[x], ie2str(ie->data[x]));
break;
case PRI_CAUSE_WRONG_CALL_STATE:
for (x=2;x<ie->len;x++)
pri_message(ctrl, "%c Cause data %d: %02x (%d, %s message)\n", prefix, x-1, ie->data[x], ie->data[x], msg2str(ie->data[x]));
break;
case PRI_CAUSE_RECOVERY_ON_TIMER_EXPIRE:
pri_message(ctrl, "%c Cause data:", prefix);
for (x=2;x<ie->len;x++)
pri_message(ctrl, " %02x", ie->data[x]);
pri_message(ctrl, " (Timer T");
for (x=2;x<ie->len;x++)
pri_message(ctrl, "%c", ((ie->data[x] >= ' ') && (ie->data[x] < 0x7f)) ? ie->data[x] : '.');
pri_message(ctrl, ")\n");
break;
default:
for (x=2;x<ie->len;x++)
pri_message(ctrl, "%c Cause data %d: %02x (%d)\n", prefix, x-1, ie->data[x], ie->data[x]);
break;
}
}
static int receive_cause(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
call->causeloc = ie->data[0] & 0xf;
call->causecode = (ie->data[0] & 0x60) >> 5;
call->cause = (ie->data[1] & 0x7f);
return 0;
}
static int transmit_cause(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
/* We are ready to transmit single IE only */
if (order > 1)
return 0;
if (call->cause > 0) {
ie->data[0] = 0x80 | (call->causecode << 5) | (call->causeloc);
ie->data[1] = 0x80 | (call->cause);
return 4;
} else {
/* Leave off */
return 0;
}
}
static void dump_sending_complete(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
pri_message(ctrl, "%c Sending Complete (len=%2d)\n", prefix, len);
}
static int receive_sending_complete(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
/* We've got a "Complete" message: Exect no further digits. */
call->complete = 1;
return 0;
}
static int transmit_sending_complete(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
if ((ctrl->overlapdial && call->complete) || /* Explicit */
(!ctrl->overlapdial && ((ctrl->switchtype == PRI_SWITCH_EUROISDN_E1) ||
/* Implicit */ (ctrl->switchtype == PRI_SWITCH_EUROISDN_T1)))) {
/* Include this single-byte IE */
return 1;
}
return 0;
}
static char *notify2str(int info)
{
/* ITU-T Q.763 */
static struct msgtype notifies[] = {
{ PRI_NOTIFY_USER_SUSPENDED, "User suspended" },
{ PRI_NOTIFY_USER_RESUMED, "User resumed" },
{ PRI_NOTIFY_BEARER_CHANGE, "Bearer service change (DSS1)" },
{ PRI_NOTIFY_ASN1_COMPONENT, "ASN.1 encoded component (DSS1)" },
{ PRI_NOTIFY_COMPLETION_DELAY, "Call completion delay" },
{ PRI_NOTIFY_CONF_ESTABLISHED, "Conference established" },
{ PRI_NOTIFY_CONF_DISCONNECTED, "Conference disconnected" },
{ PRI_NOTIFY_CONF_PARTY_ADDED, "Other party added" },
{ PRI_NOTIFY_CONF_ISOLATED, "Isolated" },
{ PRI_NOTIFY_CONF_REATTACHED, "Reattached" },
{ PRI_NOTIFY_CONF_OTHER_ISOLATED, "Other party isolated" },
{ PRI_NOTIFY_CONF_OTHER_REATTACHED, "Other party reattached" },
{ PRI_NOTIFY_CONF_OTHER_SPLIT, "Other party split" },
{ PRI_NOTIFY_CONF_OTHER_DISCONNECTED, "Other party disconnected" },
{ PRI_NOTIFY_CONF_FLOATING, "Conference floating" },
{ PRI_NOTIFY_WAITING_CALL, "Call is waiting call" },
{ PRI_NOTIFY_DIVERSION_ACTIVATED, "Diversion activated (DSS1)" },
{ PRI_NOTIFY_TRANSFER_ALERTING, "Call transfer, alerting" },
{ PRI_NOTIFY_TRANSFER_ACTIVE, "Call transfer, active" },
{ PRI_NOTIFY_REMOTE_HOLD, "Remote hold" },
{ PRI_NOTIFY_REMOTE_RETRIEVAL, "Remote retrieval" },
{ PRI_NOTIFY_CALL_DIVERTING, "Call is diverting" },
};
return code2str(info, notifies, sizeof(notifies) / sizeof(notifies[0]));
}
static void dump_notify(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
pri_message(ctrl, "%c Notification indicator (len=%2d): Ext: %d %s (%d)\n", prefix, len, ie->data[0] >> 7, notify2str(ie->data[0] & 0x7f), ie->data[0] & 0x7f);
}
static int receive_notify(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
call->notify = ie->data[0] & 0x7F;
return 0;
}
static int transmit_notify(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
if (call->notify >= 0) {
ie->data[0] = 0x80 | call->notify;
return 3;
}
return 0;
}
static void dump_shift(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
pri_message(ctrl, "%c %sLocking Shift (len=%02d): Requested codeset %d\n", prefix, (full_ie & 8) ? "Non-" : "", len, full_ie & 7);
}
static char *lineinfo2str(int info)
{
/* NAPNA ANI II digits */
static struct msgtype lineinfo[] = {
{ 0, "Plain Old Telephone Service (POTS)" },
{ 1, "Multiparty line (more than 2)" },
{ 2, "ANI failure" },
{ 6, "Station Level Rating" },
{ 7, "Special Operator Handling Required" },
{ 20, "Automatic Identified Outward Dialing (AIOD)" },
{ 23, "Coing or Non-Coin" },
{ 24, "Toll free translated to POTS originated for non-pay station" },
{ 25, "Toll free translated to POTS originated from pay station" },
{ 27, "Pay station with coin control signalling" },
{ 29, "Prison/Inmate Service" },
{ 30, "Intercept (blank)" },
{ 31, "Intercept (trouble)" },
{ 32, "Intercept (regular)" },
{ 34, "Telco Operator Handled Call" },
{ 52, "Outward Wide Area Telecommunications Service (OUTWATS)" },
{ 60, "TRS call from unrestricted line" },
{ 61, "Cellular/Wireless PCS (Type 1)" },
{ 62, "Cellular/Wireless PCS (Type 2)" },
{ 63, "Cellular/Wireless PCS (Roaming)" },
{ 66, "TRS call from hotel/motel" },
{ 67, "TRS call from restricted line" },
{ 70, "Line connected to pay station" },
{ 93, "Private virtual network call" },
};
return code2str(info, lineinfo, sizeof(lineinfo) / sizeof(lineinfo[0]));
}
static void dump_line_information(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
pri_message(ctrl, "%c Originating Line Information (len=%02d): %s (%d)\n", prefix, len, lineinfo2str(ie->data[0]), ie->data[0]);
}
static int receive_line_information(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
call->ani2 = ie->data[0];
return 0;
}
static int transmit_line_information(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
#if 0 /* XXX Is this IE possible for 4ESS only? XXX */
if(ctrl->switchtype == PRI_SWITCH_ATT4ESS) {
ie->data[0] = 0;
return 3;
}
#endif
return 0;
}
static char *gdencoding2str(int encoding)
{
static struct msgtype gdencoding[] = {
{ 0, "BCD even" },
{ 1, "BCD odd" },
{ 2, "IA5" },
{ 3, "Binary" },
};
return code2str(encoding, gdencoding, sizeof(gdencoding) / sizeof(gdencoding[0]));
}
static char *gdtype2str(int type)
{
static struct msgtype gdtype[] = {
{ 0, "Account Code" },
{ 1, "Auth Code" },
{ 2, "Customer ID" },
{ 3, "Universal Access" },
{ 4, "Info Digits" },
{ 5, "Callid" },
{ 6, "Opart" },
{ 7, "TCN" },
{ 9, "Adin" },
};
return code2str(type, gdtype, sizeof(gdtype) / sizeof(gdtype[0]));
}
static void dump_generic_digits(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
int encoding;
int type;
int idx;
int value;
if (len < 3) {
pri_message(ctrl, "%c Generic Digits (len=%02d): Invalid length\n", prefix, len);
return;
}
encoding = (ie->data[0] >> 5) & 7;
type = ie->data[0] & 0x1F;
pri_message(ctrl, "%c Generic Digits (len=%02d): Encoding %s Type %s\n", prefix, len, gdencoding2str(encoding), gdtype2str(type));
if (encoding == 3) { /* Binary */
pri_message(ctrl, "%c Don't know how to handle binary encoding\n",
prefix);
return;
}
if (len == 3) /* No number information */
return;
pri_message(ctrl, "%c Digits: ", prefix);
value = 0;
for(idx = 3; idx < len; ++idx) {
switch(encoding) {
case 0: /* BCD even */
case 1: /* BCD odd */
pri_message(ctrl, "%d", ie->data[idx-2] & 0x0f);
value = value * 10 + (ie->data[idx-2] & 0x0f);
if(!encoding || (idx+1 < len)) { /* Special handling for BCD odd */
pri_message(ctrl, "%d", (ie->data[idx-2] >> 4) & 0x0f);
value = value * 10 + ((ie->data[idx-2] >> 4) & 0x0f);
}
break;
case 2: /* IA5 */
pri_message(ctrl, "%c", ie->data[idx-2]);
value = value * 10 + ie->data[idx-2] - '0';
break;
}
}
switch(type) {
case 4: /* Info Digits */
pri_message(ctrl, " - %s", lineinfo2str(value));
break;
}
pri_message(ctrl, "\n");
}
static int receive_generic_digits(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
int encoding;
int type;
int idx;
int value;
int num_idx;
char number[260];
if (len < 3) {
pri_error(ctrl, "Invalid length of Generic Digits IE\n");
return -1;
}
encoding = (ie->data[0] >> 5) & 7;
type = ie->data[0] & 0x1F;
if (encoding == 3) { /* Binary */
pri_message(ctrl, "!! Unable to handle binary encoded Generic Digits IE\n");
return 0;
}
if (len == 3) /* No number information */
return 0;
value = 0;
switch(type) {
/* Integer value handling */
case 4: /* Info Digits */
for(idx = 3; idx < len; ++idx) {
switch(encoding) {
case 0: /* BCD even */
case 1: /* BCD odd */
value = value * 10 + (ie->data[idx-2] & 0x0f);
if(!encoding || (idx+1 < len)) /* Special handling for BCD odd */
value = value * 10 + ((ie->data[idx-2] >> 4) & 0x0f);
break;
case 2: /* IA5 */
value = value * 10 + (ie->data[idx-2] - '0');
break;
}
}
break;
/* String value handling */
case 5: /* Callid */
num_idx = 0;
for(idx = 3; (idx < len) && (num_idx < sizeof(number) - 4); ++idx) {
switch(encoding) {
case 0: /* BCD even */
case 1: /* BCD odd */
number[num_idx++] = '0' + (ie->data[idx-2] & 0x0f);
if(!encoding || (idx+1 < len)) /* Special handling for BCD odd */
number[num_idx++] = '0' + ((ie->data[idx-2] >> 4) & 0x0f);
break;
case 2:
number[num_idx++] = ie->data[idx-2];
break;
}
}
number[num_idx] = '\0';
break;
}
switch(type) {
case 4: /* Info Digits */
call->ani2 = value;
break;
#if 0
case 5: /* Callid */
if (!call->remote_id.number.valid) {
call->remote_id.number.valid = 1;
call->remote_id.number.presentation =
PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED;
call->remote_id.number.plan = PRI_UNKNOWN;
libpri_copy_string(call->remote_id.number.str, number,
sizeof(call->remote_id.number.str));
}
break;
#endif
}
return 0;
}
static int transmit_generic_digits(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
#if 0 /* XXX Is this IE possible for other switches? XXX */
if (order > 1)
return 0;
if(ctrl->switchtype == PRI_SWITCH_NI1) {
ie->data[0] = 0x04; /* BCD even, Info Digits */
ie->data[1] = 0x00; /* POTS */
return 4;
}
#endif
return 0;
}
static char *signal2str(int signal)
{
/* From Q.931 4.5.8 Table 4-24 */
static struct msgtype mtsignal[] = {
{ 0, "Dial tone" },
{ 1, "Ring back tone" },
{ 2, "Intercept tone" },
{ 3, "Network congestion tone" },
{ 4, "Busy tone" },
{ 5, "Confirm tone" },
{ 6, "Answer tone" },
{ 7, "Call waiting tone" },
{ 8, "Off-hook warning tone" },
{ 9, "Pre-emption tone" },
{ 63, "Tones off" },
{ 64, "Alerting on - pattern 0" },
{ 65, "Alerting on - pattern 1" },
{ 66, "Alerting on - pattern 2" },
{ 67, "Alerting on - pattern 3" },
{ 68, "Alerting on - pattern 4" },
{ 69, "Alerting on - pattern 5" },
{ 70, "Alerting on - pattern 6" },
{ 71, "Alerting on - pattern 7" },
{ 79, "Alerting off" },
};
return code2str(signal, mtsignal, sizeof(mtsignal) / sizeof(mtsignal[0]));
}
static void dump_signal(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
pri_message(ctrl, "%c Signal (len=%02d): ", prefix, len);
if (len < 3) {
pri_message(ctrl, "Invalid length\n");
return;
}
pri_message(ctrl, "Signal %s (%d)\n", signal2str(ie->data[0]), ie->data[0]);
}
static void dump_transit_count(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
/* Defined in ECMA-225 */
pri_message(ctrl, "%c Transit Count (len=%02d): ", prefix, len);
if (len < 3) {
pri_message(ctrl, "Invalid length\n");
return;
}
pri_message(ctrl, "Count=%d (0x%02x)\n", ie->data[0] & 0x1f, ie->data[0] & 0x1f);
}
static void dump_reverse_charging_indication(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix)
{
pri_message(ctrl, "%c Reverse Charging Indication (len=%02d): %d\n", prefix, len, ie->data[0] & 0x7);
}
static int receive_reverse_charging_indication(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len)
{
call->reversecharge = ie->data[0] & 0x7;
return 0;
}
static int transmit_reverse_charging_indication(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order)
{
if (call->reversecharge != PRI_REVERSECHARGE_NONE) {
ie->data[0] = 0x80 | (call->reversecharge & 0x7);
return 3;
}
return 0;
}
static struct ie ies[] = {
/* Codeset 0 - Common */
{ 1, NATIONAL_CHANGE_STATUS, "Change Status", dump_change_status, receive_change_status, transmit_change_status },
{ 0, Q931_LOCKING_SHIFT, "Locking Shift", dump_shift },
{ 0, Q931_BEARER_CAPABILITY, "Bearer Capability", dump_bearer_capability, receive_bearer_capability, transmit_bearer_capability },
{ 0, Q931_CAUSE, "Cause", dump_cause, receive_cause, transmit_cause },
{ 1, Q931_IE_CALL_STATE, "Call State", dump_call_state, receive_call_state, transmit_call_state },
{ 0, Q931_CHANNEL_IDENT, "Channel Identification", dump_channel_id, receive_channel_id, transmit_channel_id },
{ 0, Q931_PROGRESS_INDICATOR, "Progress Indicator", dump_progress_indicator, receive_progress_indicator, transmit_progress_indicator },
{ 0, Q931_NETWORK_SPEC_FAC, "Network-Specific Facilities", dump_network_spec_fac, receive_network_spec_fac, transmit_network_spec_fac },
{ 1, Q931_INFORMATION_RATE, "Information Rate" },
{ 1, Q931_TRANSIT_DELAY, "End-to-End Transit Delay" },
{ 1, Q931_TRANS_DELAY_SELECT, "Transmit Delay Selection and Indication" },
{ 1, Q931_BINARY_PARAMETERS, "Packet-layer Binary Parameters" },
{ 1, Q931_WINDOW_SIZE, "Packet-layer Window Size" },
{ 1, Q931_CLOSED_USER_GROUP, "Closed User Group" },
{ 1, Q931_REVERSE_CHARGE_INDIC, "Reverse Charging Indication", dump_reverse_charging_indication, receive_reverse_charging_indication, transmit_reverse_charging_indication },
{ 1, Q931_CALLING_PARTY_NUMBER, "Calling Party Number", dump_calling_party_number, receive_calling_party_number, transmit_calling_party_number },
{ 1, Q931_CALLING_PARTY_SUBADDR, "Calling Party Subaddress", dump_calling_party_subaddr, receive_calling_party_subaddr, transmit_calling_party_subaddr },
{ 1, Q931_CALLED_PARTY_NUMBER, "Called Party Number", dump_called_party_number, receive_called_party_number, transmit_called_party_number },
{ 1, Q931_CALLED_PARTY_SUBADDR, "Called Party Subaddress", dump_called_party_subaddr, receive_called_party_subaddr, transmit_called_party_subaddr },
{ 0, Q931_REDIRECTING_NUMBER, "Redirecting Number", dump_redirecting_number, receive_redirecting_number, transmit_redirecting_number },
{ 1, Q931_REDIRECTING_SUBADDR, "Redirecting Subaddress", dump_redirecting_subaddr },
{ 0, Q931_TRANSIT_NET_SELECT, "Transit Network Selection" },
{ 1, Q931_RESTART_INDICATOR, "Restart Indicator", dump_restart_indicator, receive_restart_indicator, transmit_restart_indicator },
{ 0, Q931_LOW_LAYER_COMPAT, "Low-layer Compatibility" },
{ 0, Q931_HIGH_LAYER_COMPAT, "High-layer Compatibility" },
{ 1, Q931_PACKET_SIZE, "Packet Size" },
{ 0, Q931_IE_FACILITY, "Facility" , dump_facility, receive_facility, transmit_facility },
{ 1, Q931_IE_REDIRECTION_NUMBER, "Redirection Number", dump_redirection_number, receive_redirection_number, transmit_redirection_number },
{ 1, Q931_IE_REDIRECTION_SUBADDR, "Redirection Subaddress" },
{ 1, Q931_IE_FEATURE_ACTIVATE, "Feature Activation" },
{ 1, Q931_IE_INFO_REQUEST, "Feature Request" },
{ 1, Q931_IE_FEATURE_IND, "Feature Indication" },
{ 1, Q931_IE_SEGMENTED_MSG, "Segmented Message" },
{ 1, Q931_IE_CALL_IDENTITY, "Call Identity", dump_call_identity },
{ 1, Q931_IE_ENDPOINT_ID, "Endpoint Identification" },
{ 1, Q931_IE_NOTIFY_IND, "Notification Indicator", dump_notify, receive_notify, transmit_notify },
{ 1, Q931_DISPLAY, "Display", dump_display, receive_display, transmit_display },
{ 1, Q931_IE_TIME_DATE, "Date/Time", dump_time_date },
{ 1, Q931_IE_KEYPAD_FACILITY, "Keypad Facility", dump_keypad_facility, receive_keypad_facility, transmit_keypad_facility },
{ 0, Q931_IE_SIGNAL, "Signal", dump_signal },
{ 1, Q931_IE_SWITCHHOOK, "Switch-hook" },
{ 1, Q931_IE_USER_USER, "User-User", dump_user_user, receive_user_user, transmit_user_user },
{ 1, Q931_IE_ESCAPE_FOR_EXT, "Escape for Extension" },
{ 1, Q931_IE_CALL_STATUS, "Call Status" },
{ 1, Q931_IE_CHANGE_STATUS, "Change Status", dump_change_status, receive_change_status, transmit_change_status },
{ 1, Q931_IE_CONNECTED_ADDR, "Connected Number", dump_connected_number },
{ 1, Q931_IE_CONNECTED_NUM, "Connected Number", dump_connected_number, receive_connected_number, transmit_connected_number },
{ 1, Q931_IE_CONNECTED_SUBADDR, "Connected Subaddress", dump_connected_subaddr, receive_connected_subaddr, transmit_connected_subaddr },
{ 1, Q931_IE_ORIGINAL_CALLED_NUMBER, "Original Called Number", dump_redirecting_number, receive_redirecting_number, transmit_redirecting_number },
{ 1, Q931_IE_USER_USER_FACILITY, "User-User Facility" },
{ 1, Q931_IE_UPDATE, "Update" },
{ 1, Q931_SENDING_COMPLETE, "Sending Complete", dump_sending_complete, receive_sending_complete, transmit_sending_complete },
/* Codeset 4 - Q.SIG specific */
{ 1, QSIG_IE_TRANSIT_COUNT | Q931_CODESET(4), "Transit Count", dump_transit_count },
/* Codeset 5 - National specific (ETSI PISN specific) */
{ 1, Q931_CALLING_PARTY_CATEGORY, "Calling Party Category", dump_calling_party_category },
/* Codeset 6 - Network specific */
{ 1, Q931_IE_ORIGINATING_LINE_INFO, "Originating Line Information", dump_line_information, receive_line_information, transmit_line_information },
{ 1, Q931_IE_FACILITY | Q931_CODESET(6), "Facility", dump_facility, receive_facility, transmit_facility },
{ 1, Q931_DISPLAY | Q931_CODESET(6), "Display (CS6)", dump_display, receive_display, transmit_display },
{ 0, Q931_IE_GENERIC_DIGITS, "Generic Digits", dump_generic_digits, receive_generic_digits, transmit_generic_digits },
/* Codeset 7 */
};
static char *ie2str(int ie)
{
unsigned int x;
/* Special handling for Locking/Non-Locking Shifts */
switch (ie & 0xf8) {
case Q931_LOCKING_SHIFT:
switch (ie & 7) {
case 0:
return "!! INVALID Locking Shift To Codeset 0";
case 1:
return "Locking Shift To Codeset 1";
case 2:
return "Locking Shift To Codeset 2";
case 3:
return "Locking Shift To Codeset 3";
case 4:
return "Locking Shift To Codeset 4";
case 5:
return "Locking Shift To Codeset 5";
case 6:
return "Locking Shift To Codeset 6";
case 7:
return "Locking Shift To Codeset 7";
}
case Q931_NON_LOCKING_SHIFT:
switch (ie & 7) {
case 0:
return "Non-Locking Shift To Codeset 0";
case 1:
return "Non-Locking Shift To Codeset 1";
case 2:
return "Non-Locking Shift To Codeset 2";
case 3:
return "Non-Locking Shift To Codeset 3";
case 4:
return "Non-Locking Shift To Codeset 4";
case 5:
return "Non-Locking Shift To Codeset 5";
case 6:
return "Non-Locking Shift To Codeset 6";
case 7:
return "Non-Locking Shift To Codeset 7";
}
default:
for (x=0;x<sizeof(ies) / sizeof(ies[0]); x++)
if (ie == ies[x].ie)
return ies[x].name;
return "Unknown Information Element";
}
}
static inline unsigned int ielen(q931_ie *ie)
{
if ((ie->ie & 0x80) != 0)
return 1;
else
return 2 + ie->len;
}
static char *msg2str(int msg)
{
unsigned int x;
for (x=0;x<sizeof(msgs) / sizeof(msgs[0]); x++)
if (msgs[x].msgnum == msg)
return msgs[x].name;
return "Unknown Message Type";
}
static char *maintenance_msg2str(int msg, int pd)
{
unsigned int x, max;
struct msgtype *m = NULL;
if (pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) {
m = att_maintenance_msgs;
max = ARRAY_LEN(att_maintenance_msgs);
} else {
m = national_maintenance_msgs;
max = ARRAY_LEN(national_maintenance_msgs);
}
for (x = 0; x < max; x++) {
if (m[x].msgnum == msg) {
return m[x].name;
}
}
return "Unknown Message Type";
}
static inline int q931_cr(q931_h *h)
{
int cr = 0;
int x;
if (h->crlen > 3) {
pri_error(NULL, "Call Reference Length Too long: %d\n", h->crlen);
return -1;
}
switch (h->crlen) {
case 2:
for (x=0;x<h->crlen;x++) {
cr <<= 8;
cr |= h->crv[x];
}
break;
case 1:
cr = h->crv[0];
if (cr & 0x80) {
cr &= ~0x80;
cr |= 0x8000;
}
break;
default:
pri_error(NULL, "Call Reference Length not supported: %d\n", h->crlen);
}
return cr;
}
static inline void q931_dumpie(struct pri *ctrl, int codeset, q931_ie *ie, char prefix)
{
unsigned int x;
int full_ie = Q931_FULL_IE(codeset, ie->ie);
int base_ie;
char *buf = malloc(ielen(ie) * 3 + 1);
int buflen = 0;
buf[0] = '\0';
if (!(ie->ie & 0x80)) {
buflen += sprintf(buf, " %02x", ielen(ie)-2);
for (x = 0; x + 2 < ielen(ie); ++x)
buflen += sprintf(buf + buflen, " %02x", ie->data[x]);
}
pri_message(ctrl, "%c [%02x%s]\n", prefix, ie->ie, buf);
free(buf);
/* Special treatment for shifts */
if((full_ie & 0xf0) == Q931_LOCKING_SHIFT)
full_ie &= 0xff;
base_ie = (((full_ie & ~0x7f) == Q931_FULL_IE(0, 0x80)) && ((full_ie & 0x70) != 0x20)) ? full_ie & ~0x0f : full_ie;
for (x = 0; x < ARRAY_LEN(ies); ++x)
if (ies[x].ie == base_ie) {
if (ies[x].dump)
ies[x].dump(full_ie, ctrl, ie, ielen(ie), prefix);
else
pri_message(ctrl, "%c IE: %s (len = %d)\n", prefix, ies[x].name, ielen(ie));
return;
}
pri_error(ctrl, "!! %c Unknown IE %d (cs%d, len = %d)\n", prefix, Q931_IE_IE(base_ie), Q931_IE_CODESET(base_ie), ielen(ie));
}
static q931_call *q931_getcall(struct pri *ctrl, int cr)
{
q931_call *cur;
q931_call *prev;
struct pri *master;
/* Find the master - He has the call pool */
master = PRI_MASTER(ctrl);
cur = *master->callpool;
prev = NULL;
while (cur) {
if (cur->cr == cr) {
/* Found existing call. */
switch (ctrl->switchtype) {
case PRI_SWITCH_GR303_EOC:
case PRI_SWITCH_GR303_EOC_PATH:
case PRI_SWITCH_GR303_TMC:
case PRI_SWITCH_GR303_TMC_SWITCHING:
break;
default:
if (!ctrl->bri) {
/* PRI is set to whoever called us */
cur->pri = ctrl;
}
break;
}
return cur;
}
prev = cur;
cur = cur->next;
}
/* No call exists, make a new one */
if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
pri_message(ctrl, "-- Making new call for cr %d\n", cr);
}
cur = calloc(1, sizeof(*cur));
if (!cur) {
return NULL;
}
/* Initialize call structure. */
cur->cr = cr;
cur->slotmap = -1;
cur->channelno = -1;
cur->newcall = 1;
cur->ourcallstate = Q931_CALL_STATE_NULL;
cur->peercallstate = Q931_CALL_STATE_NULL;
cur->sugcallstate = Q931_CALL_STATE_NOT_SET;
cur->ri = -1;
cur->transcapability = -1;
cur->transmoderate = -1;
cur->transmultiple = -1;
cur->userl1 = -1;
cur->userl2 = -1;
cur->userl3 = -1;
cur->rateadaption = -1;
cur->progress = -1;
cur->causecode = -1;
cur->causeloc = -1;
cur->cause = -1;
cur->useruserprotocoldisc = -1;
cur->aoc_units = -1;
cur->changestatus = -1;
cur->reversecharge = -1;
cur->pri_winner = -1;
cur->master_call = cur;
q931_party_number_init(&cur->redirection_number);
q931_party_address_init(&cur->called);
q931_party_id_init(&cur->local_id);
q931_party_id_init(&cur->remote_id);
q931_party_redirecting_init(&cur->redirecting);
/* PRI is set to whoever called us */
if (BRI_TE_PTMP(ctrl)) {
/*
* Point to the master to avoid stale pointer problems if
* the TEI is removed later.
*/
cur->pri = master;
} else {
cur->pri = ctrl;
}
/* Append to end of list */
if (prev) {
prev->next = cur;
} else {
*master->callpool = cur;
}
return cur;
}
q931_call *q931_new_call(struct pri *ctrl)
{
q931_call *cur;
do {
cur = *ctrl->callpool;
ctrl->cref++;
if (!ctrl->bri) {
if (ctrl->cref > 32767)
ctrl->cref = 1;
} else {
if (ctrl->cref > 127)
ctrl->cref = 1;
}
while(cur) {
if (cur->cr == (0x8000 | ctrl->cref))
break;
cur = cur->next;
}
} while(cur);
return q931_getcall(ctrl, ctrl->cref | 0x8000);
}
static void stop_t303(struct q931_call *call);
static void cleanup_and_free_call(struct q931_call *cur)
{
stop_t303(cur);
pri_schedule_del(cur->pri, cur->retranstimer);
pri_call_apdu_queue_cleanup(cur);
free(cur);
}
static void pri_create_fake_clearing(struct q931_call *c, struct pri *master);
void q931_destroycall(struct pri *ctrl, q931_call *c)
{
q931_call *cur;
q931_call *prev;
q931_call *slave;
int i;
int slavesleft;
int slaveidx;
if (c->master_call != c) {
slave = c;
c = slave->master_call;
} else {
slave = NULL;
}
/* For destroying, make sure we are using the master span, since it maintains the call pool */
ctrl = PRI_MASTER(ctrl);
prev = NULL;
cur = *ctrl->callpool;
while (cur) {
if (cur == c) {
slaveidx = -1;
if (slave) {
for (i = 0; i < Q931_MAX_TEI; i++) {
if (cur->subcalls[i] == slave) {
if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
pri_message(ctrl, "Destroying subcall %p of call %p at %d\n",
slave, cur, i);
}
cleanup_and_free_call(slave);
cur->subcalls[i] = NULL;
slaveidx = i;
break;
}
}
}
slavesleft = 0;
for (i = 0; i < Q931_MAX_TEI; i++) {
if (cur->subcalls[i]) {
if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
pri_message(ctrl, "Subcall still present at %d\n", i);
}
slavesleft++;
}
}
/* We have 3 different phases to deal with:
* 1.) Sent outbound call, but no response, indicated by t203 present
* 2.) Sent outbound call, with responses, indicated by lack of t203 and subcalls present
* 3.) Outbound call connected, indicated by pri_winner > -1
*
* If chan_dahdi hangs up in phase:
* 1.) T303 will be present, and we will fake clear in this case
* 2.) pri_winner will be < 0 and subcalls will be present.
* 3.) pri_winner will be > -1 and we will free the master when the winner dies.
*
* If remote ends hang up in phase:
* 1.) Impossible, defined by phase.
* 2.) When last end hangs up, we should cause a fake clearing.
* 3.) Pass events to winner up and be freed when winner is freed
*
* Exceptional conditions in phase:
* 1.) None.
* 2.) None.
* 3.) We hang up a call so quickly that it hangs up before other competing lines finish hangup sequence
* Subcalls present still even though we have hung up the winner.
*
* So, we could say:
* If, when the library user hangs up the master call, and there are more than one subcall up, we fake clear
* regardless of whether or not we drop down to one subcall left in the clearing process.
*
* If there are only one call up, we mirror what it does.
*
* OR
*
* Phase 2. them clearing:
* For handling of Phase 2 (indicated by not running and pri_winner not present):
* We create a fake hangup sequence after all the subcalls have been destroyed and after
*
* "" us clearing:
* For we need to start the fake clearing, but it needs to be half of a fake clearing, not a full one (since we already had a hangup).
*
* For handling of Phase 3 plus exceptions:
*
* If pri_winner exists, we mirror him in terms of events (which provides our hangup sequence), and when we have the complete
* hangup sequence completed (destroy called on master call), if there still exist non winner subcalls at this time, we declare the master
* call as dead and free it when the last subcall clears.
*/
if ((slave && !slavesleft) &&
((cur->pri_winner < 0) || (slave && slaveidx != cur->pri_winner))) {
pri_create_fake_clearing(cur, ctrl);
return;
}
if (slavesleft) {
return;
}
/* Master call or normal call destruction. */
if ((cur->pri_winner > -1) && cur->outboundbroadcast) {
if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
pri_message(ctrl,
"Since we already had a winner, we should just be able to kill the call anyways\n");
}
}
if (prev)
prev->next = cur->next;
else
*ctrl->callpool = cur->next;
if (ctrl->debug & PRI_DEBUG_Q931_STATE)
pri_message(ctrl,
"NEW_HANGUP DEBUG: Destroying the call, ourstate %s, peerstate %s, hold-state %s\n",
q931_call_state_str(cur->ourcallstate),
q931_call_state_str(cur->peercallstate),
q931_hold_state_str(cur->hold_state));
pri_schedule_del(ctrl, cur->hold_timer);
cleanup_and_free_call(cur);
return;
}
prev = cur;
cur = cur->next;
}
pri_error(ctrl, "Can't destroy call %p cref:%d!\n", c, c->cr);
}
static int add_ie(struct pri *ctrl, q931_call *call, int msgtype, int ie, q931_ie *iet, int maxlen, int *codeset)
{
unsigned int x;
int res, total_res;
int have_shift;
int ies_count, order;
for (x=0;x<sizeof(ies) / sizeof(ies[0]);x++) {
if (ies[x].ie == ie) {
/* This is our baby */
if (ies[x].transmit) {
/* Prepend with CODE SHIFT IE if required */
if (*codeset != Q931_IE_CODESET(ies[x].ie)) {
/* Locking shift to codeset 0 isn't possible */
iet->ie = Q931_IE_CODESET(ies[x].ie) | (Q931_IE_CODESET(ies[x].ie) ? Q931_LOCKING_SHIFT : Q931_NON_LOCKING_SHIFT);
have_shift = 1;
iet = (q931_ie *)((char *)iet + 1);
maxlen--;
}
else
have_shift = 0;
ies_count = ies[x].max_count;
if (ies_count == 0)
ies_count = INT_MAX;
order = 0;
total_res = 0;
do {
iet->ie = ie;
res = ies[x].transmit(ie, ctrl, call, msgtype, iet, maxlen, ++order);
/* Error if res < 0 or ignored if res == 0 */
if (res < 0)
return res;
if (res > 0) {
if ((iet->ie & 0x80) == 0) /* Multibyte IE */
iet->len = res - 2;
total_res += res;
maxlen -= res;
iet = (q931_ie *)((char *)iet + res);
}
} while (res > 0 && order < ies_count);
if (have_shift && total_res) {
if (Q931_IE_CODESET(ies[x].ie))
*codeset = Q931_IE_CODESET(ies[x].ie);
return total_res + 1; /* Shift is single-byte IE */
}
return total_res;
} else {
pri_error(ctrl, "!! Don't know how to add an IE %s (%d)\n", ie2str(ie), ie);
return -1;
}
}
}
pri_error(ctrl, "!! Unknown IE %d (%s)\n", ie, ie2str(ie));
return -1;
}
static char *disc2str(int disc)
{
static struct msgtype discs[] = {
{ Q931_PROTOCOL_DISCRIMINATOR, "Q.931" },
{ GR303_PROTOCOL_DISCRIMINATOR, "GR-303" },
{ 0x3, "AT&T Maintenance" },
{ 0x43, "New AT&T Maintenance" },
};
return code2str(disc, discs, sizeof(discs) / sizeof(discs[0]));
}
void q931_dump(struct pri *ctrl, q931_h *h, int len, int txrx)
{
q931_mh *mh;
char c;
int x=0, r;
int cur_codeset;
int codeset;
int cref;
c = txrx ? '>' : '<';
pri_message(ctrl, "%c Protocol Discriminator: %s (%d) len=%d\n", c, disc2str(h->pd), h->pd, len);
cref = q931_cr(h);
pri_message(ctrl, "%c Call Ref: len=%2d (reference %d/0x%X) (%s)\n",
c, h->crlen, cref & 0x7FFF, cref & 0x7FFF,
(cref & 0x8000) ? "Terminator" : "Originator");
/* Message header begins at the end of the call reference number */
mh = (q931_mh *)(h->contents + h->crlen);
if ((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) {
pri_message(ctrl, "%c Message Type: %s (%d)\n", c, maintenance_msg2str(mh->msg, h->pd), mh->msg);
} else {
pri_message(ctrl, "%c Message Type: %s (%d)\n", c, msg2str(mh->msg), mh->msg);
}
/* Drop length of header, including call reference */
len -= (h->crlen + 3);
codeset = cur_codeset = 0;
while(x < len) {
r = ielen((q931_ie *)(mh->data + x));
q931_dumpie(ctrl, cur_codeset, (q931_ie *)(mh->data + x), c);
switch (mh->data[x] & 0xf8) {
case Q931_LOCKING_SHIFT:
if ((mh->data[x] & 7) > 0)
codeset = cur_codeset = mh->data[x] & 7;
break;
case Q931_NON_LOCKING_SHIFT:
cur_codeset = mh->data[x] & 7;
break;
default:
/* Reset temporary codeset change */
cur_codeset = codeset;
}
x += r;
}
if (x > len)
pri_error(ctrl, "XXX Message longer than it should be?? XXX\n");
}
static int q931_handle_ie(int codeset, struct pri *ctrl, q931_call *c, int msg, q931_ie *ie)
{
unsigned int x;
int full_ie = Q931_FULL_IE(codeset, ie->ie);
if (ctrl->debug & PRI_DEBUG_Q931_STATE)
pri_message(ctrl, "-- Processing IE %d (cs%d, %s)\n", ie->ie, codeset, ie2str(full_ie));
for (x=0;x<sizeof(ies) / sizeof(ies[0]);x++) {
if (full_ie == ies[x].ie) {
if (ies[x].receive)
return ies[x].receive(full_ie, ctrl, c, msg, ie, ielen(ie));
else {
if (ctrl->debug & PRI_DEBUG_Q931_ANOMALY)
pri_error(ctrl, "!! No handler for IE %d (cs%d, %s)\n", ie->ie, codeset, ie2str(full_ie));
return -1;
}
}
}
pri_message(ctrl, "!! Unknown IE %d (cs%d, %s)\n", ie->ie, codeset, ie2str(full_ie));
return -1;
}
/* Returns header and message header and modifies length in place */
static void init_header(struct pri *ctrl, q931_call *call, unsigned char *buf, q931_h **hb, q931_mh **mhb, int *len, int protodisc)
{
q931_h *h = (q931_h *) buf;
q931_mh *mh;
unsigned crv;
if (protodisc) {
h->pd = protodisc;
} else {
h->pd = ctrl->protodisc;
}
h->x0 = 0; /* Reserved 0 */
if (!ctrl->bri) {
/* Two bytes of Call Reference. */
h->crlen = 2;
/* Invert the top bit to make it from our sense */
crv = (unsigned) call->cr;
h->crv[0] = ((crv >> 8) ^ 0x80) & 0xff;
h->crv[1] = crv & 0xff;
if (ctrl->subchannel && !ctrl->bri) {
/* On GR-303, top bit is always 0 */
h->crv[0] &= 0x7f;
}
} else {
h->crlen = 1;
/* Invert the top bit to make it from our sense */
crv = (unsigned) call->cr;
h->crv[0] = (((crv >> 8) ^ 0x80) & 0x80) | (crv & 0x7f);
}
*hb = h;
*len -= 3;/* Protocol discriminator, call reference length, message type id */
*len -= h->crlen;
mh = (q931_mh *) (h->contents + h->crlen);
mh->f = 0;
*mhb = mh;
}
static int q931_xmit(struct pri *ctrl, q931_h *h, int len, int cr, int uiframe)
{
/*
* For NT-PTMP mode, we need to check the following:
* MODE = NT-PTMP
* MESSAGE = SETUP
*
* If those are true, we need to send the SETUP in a UI frame
* instead of an I-frame.
*/
if (BRI_NT_PTMP(ctrl) && uiframe)
q921_transmit_uiframe(ctrl, h, len);
else
q921_transmit_iframe(ctrl, h, len, cr);
/* The transmit operation might dump the q921 header, so logging the q931
message body after the transmit puts the sections of the message in the
right order in the log */
if (ctrl->debug & PRI_DEBUG_Q931_DUMP)
q931_dump(ctrl, h, len, 1);
#ifdef LIBPRI_COUNTERS
ctrl->q931_txcount++;
#endif
return 0;
}
/*!
* \internal
* \brief Build and send the requested message.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg
* \param msgtype Q.931 message type to build.
* \param ies List of ie's to put in the message.
*
* \note The ie's in the ie list must be in numerical order.
* See Q.931 section 4.5.1 coding rules.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int send_message(struct pri *ctrl, q931_call *call, int msgtype, int ies[])
{
unsigned char buf[1024];
q931_h *h;
q931_mh *mh;
int len;
int res;
int offset=0;
int x;
int codeset;
if (call->outboundbroadcast && call->master_call == call && msgtype != Q931_SETUP) {
pri_error(ctrl,
"Attempting to use master call record to send %s on BRI PTMP NT %p\n",
msg2str(msgtype), ctrl);
return -1;
}
memset(buf, 0, sizeof(buf));
len = sizeof(buf);
init_header(ctrl, call, buf, &h, &mh, &len, (msgtype >> 8));
mh->msg = msgtype & 0x00ff;
x=0;
codeset = 0;
while(ies[x] > -1) {
res = add_ie(ctrl, call, mh->msg, ies[x], (q931_ie *)(mh->data + offset), len, &codeset);
if (res < 0) {
pri_error(ctrl, "!! Unable to add IE '%s'\n", ie2str(ies[x]));
return -1;
}
offset += res;
len -= res;
x++;
}
/* Invert the logic */
len = sizeof(buf) - len;
ctrl = call->pri;
if (BRI_TE_PTMP(ctrl)) {
/*
* Must use the BRI subchannel structure to send with the correct TEI.
* Note: If the subchannel is NULL then there is no TEI assigned and
* we should not be sending anything out at this time.
*/
ctrl = ctrl->subchannel;
}
if (ctrl) {
if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
pri_message(ctrl,
"Sending message for call %p on %p TEI/SAPI %d/%d, call->pri is %p, TEI/SAPI %d/%d\n",
call,
ctrl, ctrl->tei, ctrl->sapi,
call->pri, call->pri->tei, call->pri->sapi);
}
q931_xmit(ctrl, h, len, 1, (msgtype == Q931_SETUP) ? 1 : 0);
}
call->acked = 1;
return 0;
}
static int maintenance_service_ies[] = { Q931_IE_CHANGE_STATUS, Q931_CHANNEL_IDENT, -1 };
int maintenance_service_ack(struct pri *ctrl, q931_call *c)
{
int pd = MAINTENANCE_PROTOCOL_DISCRIMINATOR_1;
int mt = ATT_SERVICE_ACKNOWLEDGE;
if (ctrl->switchtype == PRI_SWITCH_NI2) {
pd = MAINTENANCE_PROTOCOL_DISCRIMINATOR_2;
mt = NATIONAL_SERVICE_ACKNOWLEDGE;
}
return send_message(ctrl, c, (pd << 8) | mt, maintenance_service_ies);
}
int maintenance_service(struct pri *ctrl, int span, int channel, int changestatus)
{
struct q931_call *c;
int pd = MAINTENANCE_PROTOCOL_DISCRIMINATOR_1;
int mt = ATT_SERVICE;
c = q931_getcall(ctrl, 0 | 0x8000);
if (!c) {
return -1;
}
if (channel > -1) {
c->channelno = channel & 0xff;
c->chanflags = FLAG_EXCLUSIVE;
} else {
c->channelno = channel;
c->chanflags = FLAG_EXCLUSIVE | FLAG_WHOLE_INTERFACE;
}
c->ds1no = span;
c->ds1explicit = 0;
c->changestatus = changestatus;
if (ctrl->switchtype == PRI_SWITCH_NI2) {
pd = MAINTENANCE_PROTOCOL_DISCRIMINATOR_2;
mt = NATIONAL_SERVICE;
}
return send_message(ctrl, c, (pd << 8) | mt, maintenance_service_ies);
}
static int status_ies[] = { Q931_CAUSE, Q931_IE_CALL_STATE, -1 };
static int q931_status(struct pri *ctrl, q931_call *c, int cause)
{
q931_call *cur = NULL;
if (!cause)
cause = PRI_CAUSE_RESPONSE_TO_STATUS_ENQUIRY;
if (c->cr > -1)
cur = *ctrl->callpool;
while(cur) {
if (cur->cr == c->cr) {
cur->cause=cause;
cur->causecode = CODE_CCITT;
cur->causeloc = LOC_USER;
break;
}
cur = cur->next;
}
if (!cur) {
pri_message(ctrl, "YYY Here we get reset YYY\n");
/* something went wrong, respond with "no such call" */
c->ourcallstate = Q931_CALL_STATE_NULL;
c->peercallstate = Q931_CALL_STATE_NULL;
cur=c;
}
return send_message(ctrl, cur, Q931_STATUS, status_ies);
}
static int information_ies[] = { Q931_CALLED_PARTY_NUMBER, -1 };
int q931_information(struct pri *ctrl, q931_call *c, char digit)
{
c->overlap_digits[0] = digit;
c->overlap_digits[1] = '\0';
/*
* Since we are doing overlap dialing now, we need to accumulate
* the digits into call->called.number.str.
*/
c->called.number.valid = 1;
if (strlen(c->called.number.str) < sizeof(c->called.number.str) - 1) {
/* There is enough room for the new digit. */
strcat(c->called.number.str, c->overlap_digits);
}
return send_message(ctrl, c, Q931_INFORMATION, information_ies);
}
static int keypad_facility_ies[] = { Q931_IE_KEYPAD_FACILITY, -1 };
int q931_keypad_facility(struct pri *ctrl, q931_call *call, const char *digits)
{
libpri_copy_string(call->keypad_digits, digits, sizeof(call->keypad_digits));
return send_message(ctrl, call, Q931_INFORMATION, keypad_facility_ies);
}
static int restart_ack_ies[] = { Q931_CHANNEL_IDENT, Q931_RESTART_INDICATOR, -1 };
static int restart_ack(struct pri *ctrl, q931_call *c)
{
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL);
c->peercallstate = Q931_CALL_STATE_NULL;
return send_message(ctrl, c, Q931_RESTART_ACKNOWLEDGE, restart_ack_ies);
}
static int facility_ies[] = { Q931_IE_FACILITY, -1 };
int q931_facility(struct pri*ctrl, q931_call *c)
{
return send_message(ctrl, c, Q931_FACILITY, facility_ies);
}
static int notify_ies[] = { Q931_IE_NOTIFY_IND, Q931_IE_REDIRECTION_NUMBER, -1 };
/*!
* \internal
* \brief Actually send a NOTIFY message with optional redirection number.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg
* \param notify Notification indicator
* \param number Redirection number to send if not NULL.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int q931_notify_redirection_helper(struct pri *ctrl, q931_call *call, int notify, const struct q931_party_number *number)
{
if (number) {
call->redirection_number = *number;
} else {
q931_party_number_init(&call->redirection_number);
}
call->notify = notify;
return send_message(ctrl, call, Q931_NOTIFY, notify_ies);
}
/*!
* \brief Send a NOTIFY message with optional redirection number.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg
* \param notify Notification indicator
* \param number Redirection number to send if not NULL.
*
* \retval 0 on success.
* \retval -1 on error.
*/
int q931_notify_redirection(struct pri *ctrl, q931_call *call, int notify, const struct q931_party_number *number)
{
int status;
unsigned idx;
struct q931_call *subcall;
if (call->outboundbroadcast && call->master_call == call) {
status = 0;
for (idx = 0; idx < Q931_MAX_TEI; ++idx) {
subcall = call->subcalls[idx];
if (subcall) {
/* Send to all subcalls that have given a positive response. */
switch (subcall->ourcallstate) {
case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING:
case Q931_CALL_STATE_CALL_DELIVERED:
case Q931_CALL_STATE_ACTIVE:
if (q931_notify_redirection_helper(ctrl, subcall, notify, number)) {
status = -1;
}
break;
default:
break;
}
}
}
} else {
status = q931_notify_redirection_helper(ctrl, call, notify, number);
}
return status;
}
int q931_notify(struct pri *ctrl, q931_call *c, int channel, int info)
{
if ((ctrl->switchtype == PRI_SWITCH_EUROISDN_T1) || (ctrl->switchtype != PRI_SWITCH_EUROISDN_E1)) {
if ((info > 0x2) || (info < 0x00)) {
return 0;
}
}
if (info >= 0) {
info = info & 0x7F;
} else {
info = -1;
}
return q931_notify_redirection(ctrl, c, info, NULL);
}
#ifdef ALERTING_NO_PROGRESS
static int call_progress_ies[] = { -1 };
#else
static int call_progress_with_cause_ies[] = { Q931_CAUSE, Q931_PROGRESS_INDICATOR, -1 };
static int call_progress_ies[] = { Q931_PROGRESS_INDICATOR, -1 };
#endif
int q931_call_progress(struct pri *ctrl, q931_call *c, int channel, int info)
{
if (channel) {
c->ds1no = (channel & 0xff00) >> 8;
c->ds1explicit = (channel & 0x10000) >> 16;
c->channelno = channel & 0xff;
}
if (info) {
c->progloc = LOC_PRIV_NET_LOCAL_USER;
c->progcode = CODE_CCITT;
c->progressmask = PRI_PROG_INBAND_AVAILABLE;
} else {
/* PI is mandatory IE for PROGRESS message - Q.931 3.1.8 */
pri_error(ctrl, "XXX Progress message requested but no information is provided\n");
c->progressmask = 0;
}
c->alive = 1;
return send_message(ctrl, c, Q931_PROGRESS, call_progress_ies);
}
int q931_call_progress_with_cause(struct pri *ctrl, q931_call *c, int channel, int info, int cause)
{
if (channel) {
c->ds1no = (channel & 0xff00) >> 8;
c->ds1explicit = (channel & 0x10000) >> 16;
c->channelno = channel & 0xff;
}
if (info) {
c->progloc = LOC_PRIV_NET_LOCAL_USER;
c->progcode = CODE_CCITT;
c->progressmask = PRI_PROG_INBAND_AVAILABLE;
} else {
/* PI is mandatory IE for PROGRESS message - Q.931 3.1.8 */
pri_error(ctrl, "XXX Progress message requested but no information is provided\n");
c->progressmask = 0;
}
c->cause = cause;
c->causecode = CODE_CCITT;
c->causeloc = LOC_PRIV_NET_LOCAL_USER;
c->alive = 1;
return send_message(ctrl, c, Q931_PROGRESS, call_progress_with_cause_ies);
}
#ifdef ALERTING_NO_PROGRESS
static int call_proceeding_ies[] = { Q931_CHANNEL_IDENT, -1 };
#else
static int call_proceeding_ies[] = { Q931_CHANNEL_IDENT, Q931_PROGRESS_INDICATOR, -1 };
#endif
int q931_call_proceeding(struct pri *ctrl, q931_call *c, int channel, int info)
{
if (channel) {
c->ds1no = (channel & 0xff00) >> 8;
c->ds1explicit = (channel & 0x10000) >> 16;
c->channelno = channel & 0xff;
}
c->chanflags &= ~FLAG_PREFERRED;
c->chanflags |= FLAG_EXCLUSIVE;
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_INCOMING_CALL_PROCEEDING);
c->peercallstate = Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING;
if (info) {
c->progloc = LOC_PRIV_NET_LOCAL_USER;
c->progcode = CODE_CCITT;
c->progressmask = PRI_PROG_INBAND_AVAILABLE;
} else
c->progressmask = 0;
c->proc = 1;
c->alive = 1;
return send_message(ctrl, c, Q931_CALL_PROCEEDING, call_proceeding_ies);
}
#ifndef ALERTING_NO_PROGRESS
static int alerting_ies[] = { Q931_IE_FACILITY, Q931_PROGRESS_INDICATOR, Q931_IE_USER_USER, -1 };
#else
static int alerting_ies[] = { Q931_IE_FACILITY, -1 };
#endif
int q931_alerting(struct pri *ctrl, q931_call *c, int channel, int info)
{
if (!c->proc)
q931_call_proceeding(ctrl, c, channel, 0);
if (info) {
c->progloc = LOC_PRIV_NET_LOCAL_USER;
c->progcode = CODE_CCITT;
c->progressmask = PRI_PROG_INBAND_AVAILABLE;
} else
c->progressmask = 0;
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_RECEIVED);
c->peercallstate = Q931_CALL_STATE_CALL_DELIVERED;
c->alive = 1;
switch (ctrl->switchtype) {
case PRI_SWITCH_QSIG:
if (c->local_id.name.valid) {
/* Send calledName with ALERTING */
rose_called_name_encode(ctrl, c, Q931_ALERTING);
}
break;
default:
break;
}
return send_message(ctrl, c, Q931_ALERTING, alerting_ies);
}
static int setup_ack_ies[] = { Q931_CHANNEL_IDENT, Q931_IE_FACILITY, Q931_PROGRESS_INDICATOR, -1 };
int q931_setup_ack(struct pri *ctrl, q931_call *c, int channel, int nonisdn)
{
if (channel) {
c->ds1no = (channel & 0xff00) >> 8;
c->ds1explicit = (channel & 0x10000) >> 16;
c->channelno = channel & 0xff;
}
c->chanflags &= ~FLAG_PREFERRED;
c->chanflags |= FLAG_EXCLUSIVE;
if (nonisdn && (ctrl->switchtype != PRI_SWITCH_DMS100)) {
c->progloc = LOC_PRIV_NET_LOCAL_USER;
c->progcode = CODE_CCITT;
c->progressmask = PRI_PROG_CALLED_NOT_ISDN;
} else
c->progressmask = 0;
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_OVERLAP_RECEIVING);
c->peercallstate = Q931_CALL_STATE_OVERLAP_SENDING;
c->alive = 1;
return send_message(ctrl, c, Q931_SETUP_ACKNOWLEDGE, setup_ack_ies);
}
/* T313 expiry, first time */
static void pri_connect_timeout(void *data)
{
struct q931_call *c = data;
struct pri *ctrl = c->pri;
if (ctrl->debug & PRI_DEBUG_Q931_STATE)
pri_message(ctrl, "Timed out looking for connect acknowledge\n");
q931_disconnect(ctrl, c, PRI_CAUSE_NORMAL_CLEARING);
}
/* T308 expiry, first time */
static void pri_release_timeout(void *data)
{
struct q931_call *c = data;
struct pri *ctrl = c->pri;
if (ctrl->debug & PRI_DEBUG_Q931_STATE)
pri_message(ctrl, "Timed out looking for release complete\n");
c->t308_timedout++;
c->alive = 1;
/* The call to q931_release will re-schedule T308 */
q931_release(ctrl, c, c->cause);
}
/* T308 expiry, second time */
static void pri_release_finaltimeout(void *data)
{
struct q931_call *c = data;
struct pri *ctrl = c->pri;
c->alive = 1;
if (ctrl->debug & PRI_DEBUG_Q931_STATE)
pri_message(ctrl, "Final time-out looking for release complete\n");
c->t308_timedout++;
c->ourcallstate = Q931_CALL_STATE_NULL;
c->peercallstate = Q931_CALL_STATE_NULL;
q931_clr_subcommands(ctrl);
ctrl->schedev = 1;
ctrl->ev.e = PRI_EVENT_HANGUP_ACK;
ctrl->ev.hangup.subcmds = &ctrl->subcmds;
ctrl->ev.hangup.channel = q931_encode_channel(c);
ctrl->ev.hangup.cause = c->cause;
ctrl->ev.hangup.cref = c->cr;
ctrl->ev.hangup.call = c->master_call;
ctrl->ev.hangup.aoc_units = c->aoc_units;
ctrl->ev.hangup.call_held = NULL;
ctrl->ev.hangup.call_active = NULL;
libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo));
pri_hangup(ctrl, c, c->cause);
}
/* T305 expiry, first time */
static void pri_disconnect_timeout(void *data)
{
struct q931_call *c = data;
struct pri *ctrl = c->pri;
if (ctrl->debug & PRI_DEBUG_Q931_STATE)
pri_message(ctrl, "Timed out looking for release\n");
c->alive = 1;
q931_release(ctrl, c, PRI_CAUSE_NORMAL_CLEARING);
}
static int connect_ies[] = {
Q931_CHANNEL_IDENT,
Q931_IE_FACILITY,
Q931_PROGRESS_INDICATOR,
Q931_DISPLAY,
Q931_IE_CONNECTED_NUM,
Q931_IE_CONNECTED_SUBADDR,
-1
};
int q931_connect(struct pri *ctrl, q931_call *c, int channel, int nonisdn)
{
if (channel) {
c->ds1no = (channel & 0xff00) >> 8;
c->ds1explicit = (channel & 0x10000) >> 16;
c->channelno = channel & 0xff;
}
c->chanflags &= ~FLAG_PREFERRED;
c->chanflags |= FLAG_EXCLUSIVE;
if (nonisdn && (ctrl->switchtype != PRI_SWITCH_DMS100)) {
c->progloc = LOC_PRIV_NET_LOCAL_USER;
c->progcode = CODE_CCITT;
c->progressmask = PRI_PROG_CALLED_NOT_ISDN;
} else
c->progressmask = 0;
if(ctrl->localtype == PRI_NETWORK || ctrl->switchtype == PRI_SWITCH_QSIG)
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_ACTIVE);
else
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CONNECT_REQUEST);
c->peercallstate = Q931_CALL_STATE_ACTIVE;
c->alive = 1;
/* Connect request timer */
pri_schedule_del(ctrl, c->retranstimer);
c->retranstimer = 0;
if ((c->ourcallstate == Q931_CALL_STATE_CONNECT_REQUEST) && (ctrl->bri || (!ctrl->subchannel)))
c->retranstimer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T313], pri_connect_timeout, c);
if (c->redirecting.state == Q931_REDIRECTING_STATE_PENDING_TX_DIV_LEG_3) {
c->redirecting.state = Q931_REDIRECTING_STATE_IDLE;
/* Send DivertingLegInformation3 with CONNECT. */
c->redirecting.to = c->local_id;
if (!c->redirecting.to.number.valid) {
q931_party_number_init(&c->redirecting.to.number);
c->redirecting.to.number.valid = 1;
c->redirecting.to.number.presentation =
PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED;
}
rose_diverting_leg_information3_encode(ctrl, c, Q931_CONNECT);
}
switch (ctrl->switchtype) {
case PRI_SWITCH_QSIG:
if (c->local_id.name.valid) {
/* Send connectedName with CONNECT */
rose_connected_name_encode(ctrl, c, Q931_CONNECT);
}
break;
default:
break;
}
return send_message(ctrl, c, Q931_CONNECT, connect_ies);
}
static int release_ies[] = { Q931_CAUSE, Q931_IE_USER_USER, -1 };
int q931_release(struct pri *ctrl, q931_call *c, int cause)
{
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_RELEASE_REQUEST);
/* c->peercallstate stays the same */
if (c->alive) {
c->alive = 0;
c->cause = cause;
c->causecode = CODE_CCITT;
c->causeloc = LOC_PRIV_NET_LOCAL_USER;
if (c->acked) {
pri_schedule_del(ctrl, c->retranstimer);
if (!c->t308_timedout) {
c->retranstimer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T308], pri_release_timeout, c);
} else {
c->retranstimer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T308], pri_release_finaltimeout, c);
}
return send_message(ctrl, c, Q931_RELEASE, release_ies);
} else
return send_message(ctrl, c, Q931_RELEASE_COMPLETE, release_ies); /* Yes, release_ies, not release_complete_ies */
} else
return 0;
}
static int restart_ies[] = { Q931_CHANNEL_IDENT, Q931_RESTART_INDICATOR, -1 };
int q931_restart(struct pri *ctrl, int channel)
{
struct q931_call *c;
c = q931_getcall(ctrl, 0 | 0x8000);
if (!c)
return -1;
if (!channel)
return -1;
c->ri = 0;
c->ds1no = (channel & 0xff00) >> 8;
c->ds1explicit = (channel & 0x10000) >> 16;
c->channelno = channel & 0xff;
c->chanflags &= ~FLAG_PREFERRED;
c->chanflags |= FLAG_EXCLUSIVE;
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_RESTART);
c->peercallstate = Q931_CALL_STATE_RESTART_REQUEST;
return send_message(ctrl, c, Q931_RESTART, restart_ies);
}
static int disconnect_ies[] = { Q931_CAUSE, Q931_IE_FACILITY, Q931_IE_USER_USER, -1 };
int q931_disconnect(struct pri *ctrl, q931_call *c, int cause)
{
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_DISCONNECT_REQUEST);
c->peercallstate = Q931_CALL_STATE_DISCONNECT_INDICATION;
if (c->alive) {
c->alive = 0;
c->cause = cause;
c->causecode = CODE_CCITT;
c->causeloc = LOC_PRIV_NET_LOCAL_USER;
c->sendhangupack = 1;
pri_schedule_del(ctrl, c->retranstimer);
c->retranstimer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T305], pri_disconnect_timeout, c);
return send_message(ctrl, c, Q931_DISCONNECT, disconnect_ies);
} else
return 0;
}
static int setup_ies[] = {
Q931_BEARER_CAPABILITY,
Q931_CHANNEL_IDENT,
Q931_IE_FACILITY,
Q931_PROGRESS_INDICATOR,
Q931_NETWORK_SPEC_FAC,
Q931_DISPLAY,
Q931_IE_KEYPAD_FACILITY,
Q931_REVERSE_CHARGE_INDIC,
Q931_CALLING_PARTY_NUMBER,
Q931_CALLING_PARTY_SUBADDR,
Q931_CALLED_PARTY_NUMBER,
Q931_CALLED_PARTY_SUBADDR,
Q931_REDIRECTING_NUMBER,
Q931_IE_USER_USER,
Q931_SENDING_COMPLETE,
Q931_IE_ORIGINATING_LINE_INFO,
Q931_IE_GENERIC_DIGITS,
-1
};
static int gr303_setup_ies[] = {
Q931_BEARER_CAPABILITY,
Q931_CHANNEL_IDENT,
-1
};
/*! Call Independent Signalling SETUP ie's */
static int cis_setup_ies[] = {
Q931_BEARER_CAPABILITY,
Q931_CHANNEL_IDENT,
Q931_IE_FACILITY,
Q931_IE_KEYPAD_FACILITY,
Q931_CALLING_PARTY_NUMBER,
Q931_CALLING_PARTY_SUBADDR,
Q931_CALLED_PARTY_NUMBER,
Q931_CALLED_PARTY_SUBADDR,
Q931_SENDING_COMPLETE,
-1
};
static void stop_t303(struct q931_call *call)
{
/* T303 should only be running on the master call */
pri_schedule_del(call->master_call->pri, call->master_call->t303_timer);
call->master_call->t303_timer = 0;
}
static void t303_expiry(void *data);
static void start_t303(struct q931_call *call)
{
if (call->t303_timer) {
pri_error(call->pri, "Should not have T303 set when starting again. Stopping first\n");
stop_t303(call);
}
//pri_error(call->pri, "T303 should be %d\n", call->pri->timers[PRI_TIMER_T303]);
call->t303_timer = pri_schedule_event(call->pri, call->pri->timers[PRI_TIMER_T303], t303_expiry, call);
}
static void pri_fake_clearing(void *data);
static void t303_expiry(void *data)
{
struct q931_call *c = data;
struct pri *ctrl = c->pri;
int res;
c->t303_expirycnt++;
c->t303_timer = 0;
if (c->cause != -1) {
/* We got a DISCONNECT, RELEASE, or RELEASE_COMPLETE and no other responses. */
pri_fake_clearing(c);
} else if (c->t303_expirycnt < 2) {
if (ctrl->subchannel && !ctrl->bri)
res = send_message(ctrl, c, Q931_SETUP, gr303_setup_ies);
else if (c->cis_call)
res = send_message(ctrl, c, Q931_SETUP, cis_setup_ies);
else
res = send_message(ctrl, c, Q931_SETUP, setup_ies);
if (res) {
pri_error(c->pri, "Error resending setup message!\n");
}
start_t303(c);
} else {
c->cause = PRI_CAUSE_NO_USER_RESPONSE;
pri_fake_clearing(c);
}
}
int q931_setup(struct pri *ctrl, q931_call *c, struct pri_sr *req)
{
int res;
if (!req->called.number.valid && (!req->keypad_digits || !req->keypad_digits[0])) {
/* No called number or keypad digits to send. */
return -1;
}
c->called = req->called;
libpri_copy_string(c->overlap_digits, req->called.number.str, sizeof(c->overlap_digits));
if (req->keypad_digits) {
libpri_copy_string(c->keypad_digits, req->keypad_digits,
sizeof(c->keypad_digits));
} else {
c->keypad_digits[0] = '\0';
}
c->transcapability = req->transmode;
c->transmoderate = TRANS_MODE_64_CIRCUIT;
if (!req->userl1)
req->userl1 = PRI_LAYER_1_ULAW;
c->userl1 = req->userl1;
c->userl2 = -1;
c->userl3 = -1;
c->ds1no = (req->channel & 0xff00) >> 8;
c->ds1explicit = (req->channel & 0x10000) >> 16;
if ((ctrl->localtype == PRI_CPE) && ctrl->subchannel && !ctrl->bri) {
c->channelno = 0;
c->chanflags = 0;
} else {
c->channelno = req->channel & 0xff;
if (req->exclusive) {
c->chanflags = FLAG_EXCLUSIVE;
} else {
c->chanflags = FLAG_PREFERRED;
}
}
c->slotmap = -1;
c->nonisdn = req->nonisdn;
c->newcall = 0;
c->cis_call = req->cis_call;
c->cis_auto_disconnect = req->cis_auto_disconnect;
c->complete = req->numcomplete;
if (req->caller.number.valid) {
c->local_id = req->caller;
q931_party_id_fixup(ctrl, &c->local_id);
}
if (req->redirecting.from.number.valid) {
c->redirecting = req->redirecting;
q931_party_id_fixup(ctrl, &c->redirecting.from);
q931_party_id_fixup(ctrl, &c->redirecting.to);
q931_party_id_fixup(ctrl, &c->redirecting.orig_called);
}
if (req->useruserinfo)
libpri_copy_string(c->useruserinfo, req->useruserinfo, sizeof(c->useruserinfo));
else
c->useruserinfo[0] = '\0';
if (req->nonisdn && (ctrl->switchtype == PRI_SWITCH_NI2))
c->progressmask = PRI_PROG_CALLER_NOT_ISDN;
else
c->progressmask = 0;
c->reversecharge = req->reversecharge;
pri_call_add_standard_apdus(ctrl, c);
if (ctrl->subchannel && !ctrl->bri)
res = send_message(ctrl, c, Q931_SETUP, gr303_setup_ies);
else if (c->cis_call)
res = send_message(ctrl, c, Q931_SETUP, cis_setup_ies);
else
res = send_message(ctrl, c, Q931_SETUP, setup_ies);
if (!res) {
c->alive = 1;
/* make sure we call PRI_EVENT_HANGUP_ACK once we send/receive RELEASE_COMPLETE */
c->sendhangupack = 1;
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_INITIATED);
c->peercallstate = Q931_CALL_STATE_CALL_PRESENT;
c->t303_expirycnt = 0;
if (BRI_NT_PTMP(ctrl)) {
c->outboundbroadcast = 1;
}
start_t303(c);
}
return res;
}
static int release_complete_ies[] = { Q931_IE_USER_USER, -1 };
static int q931_release_complete(struct pri *ctrl, q931_call *c, int cause)
{
int res = 0;
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL);
c->peercallstate = Q931_CALL_STATE_NULL;
if (cause > -1) {
c->cause = cause;
c->causecode = CODE_CCITT;
c->causeloc = LOC_PRIV_NET_LOCAL_USER;
/* release_ies has CAUSE in it */
res = send_message(ctrl, c, Q931_RELEASE_COMPLETE, release_ies);
} else
res = send_message(ctrl, c, Q931_RELEASE_COMPLETE, release_complete_ies);
c->alive = 0;
/* release the structure */
res += pri_hangup(ctrl, c, cause);
return res;
}
static int connect_acknowledge_ies[] = { -1 };
static int gr303_connect_acknowledge_ies[] = { Q931_CHANNEL_IDENT, -1 };
static int q931_connect_acknowledge(struct pri *ctrl, q931_call *c)
{
if (ctrl->subchannel && !ctrl->bri) {
if (ctrl->localtype == PRI_CPE)
return send_message(ctrl, c, Q931_CONNECT_ACKNOWLEDGE, gr303_connect_acknowledge_ies);
} else
return send_message(ctrl, c, Q931_CONNECT_ACKNOWLEDGE, connect_acknowledge_ies);
return 0;
}
/*!
* \internal
* \brief Find the winning subcall if it exists or current call if not outboundbroadcast.
*
* \param call Starting Q.931 call record of search.
*
* \retval winning-call or given call if not outboundbroadcast.
* \retval NULL if no winning call yet.
*/
static struct q931_call *q931_find_winning_call(struct q931_call *call)
{
struct q931_call *master;
master = call->master_call;
if (master->outboundbroadcast) {
/* We have potential subcalls. Now get the winning call if declared yet. */
if (master->pri_winner < 0) {
/* Winner not declared yet.*/
call = NULL;
} else {
call = master->subcalls[master->pri_winner];
}
}
return call;
}
/*!
* \internal
* \brief Send HOLD message response wait timeout.
*
* \param data Q.931 call leg. (Master Q.931 subcall structure)
*
* \return Nothing
*/
static void q931_hold_timeout(void *data)
{
struct q931_call *call = data;
struct pri *ctrl = call->pri;
if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
pri_message(ctrl, "Time-out waiting for HOLD response\n");
}
/* Ensure that the timer is deleted. */
pri_schedule_del(ctrl, call->hold_timer);
call->hold_timer = 0;
UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_IDLE);
q931_clr_subcommands(ctrl);
ctrl->schedev = 1;
ctrl->ev.e = PRI_EVENT_HOLD_REJ;
ctrl->ev.hold_rej.channel = q931_encode_channel(call);
ctrl->ev.hold_rej.call = call;
ctrl->ev.hold_rej.cause = PRI_CAUSE_MESSAGE_TYPE_NONEXIST;
ctrl->ev.hold_rej.subcmds = &ctrl->subcmds;
}
/*!
* \internal
* \brief Determine if a hold request is allowed now.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg. (Master Q.931 subcall structure)
*
* \retval TRUE if we can send a HOLD request.
* \retval FALSE if not allowed.
*/
static int q931_is_hold_allowed(const struct pri *ctrl, const struct q931_call *call)
{
int allowed;
allowed = 0;
switch (call->ourcallstate) {
case Q931_CALL_STATE_CALL_RECEIVED:
case Q931_CALL_STATE_CONNECT_REQUEST:
case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING:
if (q931_is_ptmp(ctrl)) {
/* HOLD request only allowed in these states if point-to-point mode. */
break;
}
/* Fall through */
case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING:
case Q931_CALL_STATE_CALL_DELIVERED:
case Q931_CALL_STATE_ACTIVE:
switch (call->hold_state) {
case Q931_HOLD_STATE_IDLE:
allowed = 1;
break;
default:
break;
}
break;
case Q931_CALL_STATE_DISCONNECT_INDICATION:
case Q931_CALL_STATE_RELEASE_REQUEST:
/* Ignore HOLD request in these states. */
break;
default:
break;
}
return allowed;
}
static int hold_ies[] = {
-1
};
/*!
* \brief Send the HOLD message.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg. (Master Q.931 subcall structure)
*
* \retval 0 on success.
* \retval -1 on error.
*/
int q931_send_hold(struct pri *ctrl, struct q931_call *call)
{
struct q931_call *winner;
winner = q931_find_winning_call(call);
if (!winner || !q931_is_hold_allowed(ctrl, call)) {
return -1;
}
pri_schedule_del(ctrl, call->hold_timer);
call->hold_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T_HOLD],
q931_hold_timeout, call);
if (send_message(ctrl, winner, Q931_HOLD, hold_ies)) {
pri_schedule_del(ctrl, call->hold_timer);
call->hold_timer = 0;
return -1;
}
UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_HOLD_REQ);
return 0;
}
static int hold_ack_ies[] = {
-1
};
/*!
* \brief Send the HOLD ACKNOWLEDGE message.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg. (Master Q.931 subcall structure)
*
* \retval 0 on success.
* \retval -1 on error.
*/
int q931_send_hold_ack(struct pri *ctrl, struct q931_call *call)
{
struct q931_call *winner;
UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_CALL_HELD);
winner = q931_find_winning_call(call);
if (!winner) {
return -1;
}
/* Call is now on hold so forget the channel. */
winner->channelno = 0;/* No channel */
winner->ds1no = 0;
winner->ds1explicit = 0;
winner->chanflags = 0;
return send_message(ctrl, winner, Q931_HOLD_ACKNOWLEDGE, hold_ack_ies);
}
static int hold_reject_ies[] = {
Q931_CAUSE,
-1
};
/*!
* \internal
* \brief Send the HOLD REJECT message only.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg. (subcall)
* \param cause Q.931 cause code for rejecting the hold request.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int q931_send_hold_rej_msg(struct pri *ctrl, struct q931_call *call, int cause)
{
call->cause = cause;
call->causecode = CODE_CCITT;
call->causeloc = LOC_PRIV_NET_LOCAL_USER;
return send_message(ctrl, call, Q931_HOLD_REJECT, hold_reject_ies);
}
/*!
* \brief Send the HOLD REJECT message.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg. (Master Q.931 subcall structure)
* \param cause Q.931 cause code for rejecting the hold request.
*
* \retval 0 on success.
* \retval -1 on error.
*/
int q931_send_hold_rej(struct pri *ctrl, struct q931_call *call, int cause)
{
struct q931_call *winner;
UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_IDLE);
winner = q931_find_winning_call(call);
if (!winner) {
return -1;
}
return q931_send_hold_rej_msg(ctrl, winner, cause);
}
/*!
* \internal
* \brief Send RETRIEVE message response wait timeout.
*
* \param data Q.931 call leg. (Master Q.931 subcall structure)
*
* \return Nothing
*/
static void q931_retrieve_timeout(void *data)
{
struct q931_call *call = data;
struct pri *ctrl = call->pri;
struct q931_call *winner;
if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
pri_message(ctrl, "Time-out waiting for RETRIEVE response\n");
}
/* Ensure that the timer is deleted. */
pri_schedule_del(ctrl, call->hold_timer);
call->hold_timer = 0;
UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_CALL_HELD);
winner = q931_find_winning_call(call);
if (winner) {
/* Call is still on hold so forget the channel. */
winner->channelno = 0;/* No channel */
winner->ds1no = 0;
winner->ds1explicit = 0;
winner->chanflags = 0;
}
q931_clr_subcommands(ctrl);
ctrl->schedev = 1;
ctrl->ev.e = PRI_EVENT_RETRIEVE_REJ;
ctrl->ev.retrieve_rej.channel = q931_encode_channel(call);
ctrl->ev.retrieve_rej.call = call;
ctrl->ev.retrieve_rej.cause = PRI_CAUSE_MESSAGE_TYPE_NONEXIST;
ctrl->ev.retrieve_rej.subcmds = &ctrl->subcmds;
}
/*!
* \internal
* \brief Determine if a retrieve request is allowed now.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg. (Master Q.931 subcall structure)
*
* \retval TRUE if we can send a RETRIEVE request.
* \retval FALSE if not allowed.
*/
static int q931_is_retrieve_allowed(const struct pri *ctrl, const struct q931_call *call)
{
int allowed;
allowed = 0;
switch (call->ourcallstate) {
case Q931_CALL_STATE_CALL_RECEIVED:
case Q931_CALL_STATE_CONNECT_REQUEST:
case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING:
if (q931_is_ptmp(ctrl)) {
/* RETRIEVE request only allowed in these states if point-to-point mode. */
break;
}
/* Fall through */
case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING:
case Q931_CALL_STATE_CALL_DELIVERED:
case Q931_CALL_STATE_ACTIVE:
switch (call->hold_state) {
case Q931_HOLD_STATE_CALL_HELD:
allowed = 1;
break;
default:
break;
}
break;
case Q931_CALL_STATE_DISCONNECT_INDICATION:
case Q931_CALL_STATE_RELEASE_REQUEST:
/* Ignore RETRIEVE request in these states. */
break;
default:
break;
}
return allowed;
}
static int retrieve_ies[] = {
Q931_CHANNEL_IDENT,
-1
};
/*!
* \brief Send the RETRIEVE message.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg. (Master Q.931 subcall structure)
* \param channel Encoded channel id to use. If zero do not send channel id.
*
* \retval 0 on success.
* \retval -1 on error.
*/
int q931_send_retrieve(struct pri *ctrl, struct q931_call *call, int channel)
{
struct q931_call *winner;
winner = q931_find_winning_call(call);
if (!winner || !q931_is_retrieve_allowed(ctrl, call)) {
return -1;
}
if (channel) {
winner->ds1no = (channel & 0xff00) >> 8;
winner->ds1explicit = (channel & 0x10000) >> 16;
winner->channelno = channel & 0xff;
if (ctrl->localtype == PRI_NETWORK) {
winner->chanflags = FLAG_EXCLUSIVE;
} else {
winner->chanflags = FLAG_PREFERRED;
}
} else {
/* Do not send Q931_CHANNEL_IDENT */
winner->chanflags = 0;
}
pri_schedule_del(ctrl, call->hold_timer);
call->hold_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T_RETRIEVE],
q931_retrieve_timeout, call);
if (send_message(ctrl, winner, Q931_RETRIEVE, retrieve_ies)) {
pri_schedule_del(ctrl, call->hold_timer);
call->hold_timer = 0;
/* Call is still on hold so forget the channel. */
winner->channelno = 0;/* No channel */
winner->ds1no = 0;
winner->ds1explicit = 0;
winner->chanflags = 0;
return -1;
}
UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_RETRIEVE_REQ);
return 0;
}
static int retrieve_ack_ies[] = {
Q931_CHANNEL_IDENT,
-1
};
/*!
* \brief Send the RETRIEVE ACKNOWLEDGE message.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg. (Master Q.931 subcall structure)
* \param channel Encoded channel id to use.
*
* \retval 0 on success.
* \retval -1 on error.
*/
int q931_send_retrieve_ack(struct pri *ctrl, struct q931_call *call, int channel)
{
struct q931_call *winner;
winner = q931_find_winning_call(call);
if (!winner) {
return -1;
}
winner->ds1no = (channel & 0xff00) >> 8;
winner->ds1explicit = (channel & 0x10000) >> 16;
winner->channelno = channel & 0xff;
winner->chanflags = FLAG_EXCLUSIVE;
UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_IDLE);
return send_message(ctrl, winner, Q931_RETRIEVE_ACKNOWLEDGE, retrieve_ack_ies);
}
static int retrieve_reject_ies[] = {
Q931_CAUSE,
-1
};
/*!
* \internal
* \brief Send the RETRIEVE REJECT message only.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg. (subcall)
* \param cause Q.931 cause code for rejecting the retrieve request.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int q931_send_retrieve_rej_msg(struct pri *ctrl, struct q931_call *call, int cause)
{
call->cause = cause;
call->causecode = CODE_CCITT;
call->causeloc = LOC_PRIV_NET_LOCAL_USER;
return send_message(ctrl, call, Q931_RETRIEVE_REJECT, retrieve_reject_ies);
}
/*!
* \brief Send the RETRIEVE REJECT message.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg. (Master Q.931 subcall structure)
* \param cause Q.931 cause code for rejecting the retrieve request.
*
* \retval 0 on success.
* \retval -1 on error.
*/
int q931_send_retrieve_rej(struct pri *ctrl, struct q931_call *call, int cause)
{
struct q931_call *winner;
UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_CALL_HELD);
winner = q931_find_winning_call(call);
if (!winner) {
return -1;
}
/* Call is still on hold so forget the channel. */
winner->channelno = 0;/* No channel */
winner->ds1no = 0;
winner->ds1explicit = 0;
winner->chanflags = 0;
return q931_send_retrieve_rej_msg(ctrl, winner, cause);
}
static int pri_internal_clear(void *data);
/* Fake RELEASE for NT-PTMP initiated SETUPs w/o response */
static void pri_fake_clearing(void *data)
{
struct q931_call *c = data;
struct pri *ctrl = c->pri;
c->performing_fake_clearing = 1;
if (pri_internal_clear(c) == Q931_RES_HAVEEVENT)
ctrl->schedev = 1;
}
static void pri_create_fake_clearing(struct q931_call *c, struct pri *master)
{
c->pri = master;
pri_schedule_del(master, c->retranstimer);
c->retranstimer = pri_schedule_event(master, 0, pri_fake_clearing, c);
}
//static int q931_get_subcall_count(struct q931_call *call);
static int __q931_hangup(struct pri *ctrl, q931_call *c, int cause)
{
int disconnect = 1;
int release_compl = 0;
int t303_was_running = c->master_call->t303_timer;
if (ctrl->debug & PRI_DEBUG_Q931_STATE)
pri_message(ctrl,
"NEW_HANGUP DEBUG: Calling q931_hangup, ourstate %s, peerstate %s, hold-state %s\n",
q931_call_state_str(c->ourcallstate),
q931_call_state_str(c->peercallstate),
q931_hold_state_str(c->master_call->hold_state));
if (!ctrl || !c)
return -1;
/* If mandatory IE was missing, insist upon that cause code */
if (c->cause == PRI_CAUSE_MANDATORY_IE_MISSING)
cause = c->cause;
switch (cause) {
case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION:
case PRI_CAUSE_REQUESTED_CHAN_UNAVAIL:
case PRI_CAUSE_IDENTIFIED_CHANNEL_NOTEXIST:
case PRI_CAUSE_UNALLOCATED:
case PRI_CAUSE_INVALID_CALL_REFERENCE:
/* We'll send RELEASE_COMPLETE with these causes */
disconnect = 0;
release_compl = 1;
break;
case PRI_CAUSE_CHANNEL_UNACCEPTABLE:
case PRI_CAUSE_CALL_AWARDED_DELIVERED:
case PRI_CAUSE_NONSELECTED_USER_CLEARING:
/* We'll send RELEASE with these causes */
disconnect = 0;
break;
default:
break;
}
if (c->cis_call) {
disconnect = 0;
}
c->hangupinitiated = 1;
stop_t303(c);
/* All other causes we send with DISCONNECT */
switch(c->ourcallstate) {
case Q931_CALL_STATE_NULL:
if (c->peercallstate == Q931_CALL_STATE_NULL)
/* free the resources if we receive or send REL_COMPL */
pri_destroycall(ctrl, c);
else if (c->peercallstate == Q931_CALL_STATE_RELEASE_REQUEST)
q931_release_complete(ctrl,c,cause);
break;
case Q931_CALL_STATE_CALL_INITIATED:
if (c->outboundbroadcast && c->master_call == c && t303_was_running) {
//c->fakeclearing = 1;
//c->alive = 0;
/* We need to fake a received clearing sequence in this case... */
if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
pri_message(ctrl, "Faking clearing\n");
}
pri_create_fake_clearing(c, PRI_MASTER(ctrl));
/* This means that we never got a response from a TEI */
return 0;
}
/* we sent SETUP */
case Q931_CALL_STATE_OVERLAP_SENDING:
/* received SETUP_ACKNOWLEDGE */
case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING:
/* received CALL_PROCEEDING */
case Q931_CALL_STATE_CALL_DELIVERED:
/* received ALERTING */
case Q931_CALL_STATE_CALL_PRESENT:
/* received SETUP */
case Q931_CALL_STATE_CALL_RECEIVED:
/* sent ALERTING */
case Q931_CALL_STATE_CONNECT_REQUEST:
/* sent CONNECT */
case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING:
/* we sent CALL_PROCEEDING */
case Q931_CALL_STATE_OVERLAP_RECEIVING:
/* received SETUP_ACKNOWLEDGE */
/* send DISCONNECT in general */
switch (c->peercallstate) {
default:
if (disconnect)
q931_disconnect(ctrl,c,cause);
else if (release_compl)
q931_release_complete(ctrl,c,cause);
else
q931_release(ctrl,c,cause);
break;
case Q931_CALL_STATE_NULL:
case Q931_CALL_STATE_DISCONNECT_REQUEST:
case Q931_CALL_STATE_DISCONNECT_INDICATION:
case Q931_CALL_STATE_RELEASE_REQUEST:
case Q931_CALL_STATE_RESTART_REQUEST:
case Q931_CALL_STATE_RESTART:
pri_error(ctrl,
"Wierd, doing nothing but this shouldn't happen, ourstate %s, peerstate %s\n",
q931_call_state_str(c->ourcallstate),
q931_call_state_str(c->peercallstate));
break;
}
break;
case Q931_CALL_STATE_ACTIVE:
/* received CONNECT */
if (c->cis_call) {
q931_release(ctrl, c, cause);
break;
}
q931_disconnect(ctrl,c,cause);
break;
case Q931_CALL_STATE_DISCONNECT_REQUEST:
/* sent DISCONNECT */
q931_release(ctrl,c,cause);
break;
case Q931_CALL_STATE_DISCONNECT_INDICATION:
/* received DISCONNECT */
if (c->peercallstate == Q931_CALL_STATE_DISCONNECT_REQUEST) {
c->alive = 1;
q931_release(ctrl,c,cause);
}
break;
case Q931_CALL_STATE_RELEASE_REQUEST:
/* sent RELEASE */
/* don't do anything, waiting for RELEASE_COMPLETE */
break;
case Q931_CALL_STATE_RESTART:
case Q931_CALL_STATE_RESTART_REQUEST:
/* sent RESTART */
pri_error(ctrl,
"q931_hangup shouldn't be called in this state, ourstate %s, peerstate %s\n",
q931_call_state_str(c->ourcallstate),
q931_call_state_str(c->peercallstate));
break;
default:
pri_error(ctrl,
"We're not yet handling hanging up when our state is %d, contact support@digium.com, ourstate %s, peerstate %s\n",
c->ourcallstate,
q931_call_state_str(c->ourcallstate),
q931_call_state_str(c->peercallstate));
return -1;
}
/* we did handle hangup properly at this point */
return 0;
}
static void initiate_hangup_if_needed(struct pri *pri, q931_call *call, int cause);
int q931_hangup(struct pri *ctrl, q931_call *call, int cause)
{
int i;
if (call->master_call->outboundbroadcast) {
if (call->master_call == call) {
int slaves = 0;
/* Master is called with hangup - initiate hangup with slaves */
for (i = 0; i < Q931_MAX_TEI; i++) {
if (call->subcalls[i]) {
slaves++;
if (i == call->master_call->pri_winner) {
__q931_hangup(call->subcalls[i]->pri, call->subcalls[i], cause);
} else {
initiate_hangup_if_needed(call->subcalls[i]->pri, call->subcalls[i], cause);
}
if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
pri_message(ctrl, "%s: Hanging up %d, winner %d\n", __FUNCTION__,
i, call->master_call->pri_winner);
}
}
}
call->hangupinitiated = 1;
if ((!slaves && (call->master_call->pri_winner < 0)) || (call->performing_fake_clearing)) {
__q931_hangup(ctrl, call, cause);
}
if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
pri_message(ctrl, "%s: Slaves %d\n", __FUNCTION__, slaves);
}
return 0;
} else {
if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
pri_message(ctrl, "%s: Slave hangup\n", __FUNCTION__);
}
return __q931_hangup(ctrl, call, cause);
}
} else {
if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
pri_message(ctrl, "%s: other hangup\n", __FUNCTION__);
}
return __q931_hangup(ctrl, call, cause);
}
return 0;
}
static int prepare_to_handle_maintenance_message(struct pri *ctrl, q931_mh *mh, q931_call *c)
{
if ((!ctrl) || (!mh) || (!c)) {
return -1;
}
/* SERVICE messages are a superset of messages that can take b-channels
* or entire d-channels in and out of service */
switch(mh->msg) {
/* the ATT_SERVICE/ATT_SERVICE_ACKNOWLEDGE and NATIONAL_SERVICE/NATIONAL_SERVICE_ACKNOWLEDGE
* are mirrors of each other. We only have to check for one type because they are pre-handled
* the same way as each other */
case ATT_SERVICE:
case ATT_SERVICE_ACKNOWLEDGE:
c->channelno = -1;
c->slotmap = -1;
c->chanflags = 0;
c->ds1explicit = 0;
c->ds1no = 0;
c->cis_call = 0;
c->ri = -1;
c->changestatus = -1;
break;
default:
pri_error(ctrl, "!! Don't know how to pre-handle maintenance message type '%d'\n", mh->msg);
return -1;
}
return 0;
}
static int prepare_to_handle_q931_message(struct pri *ctrl, q931_mh *mh, q931_call *c)
{
if ((!ctrl) || (!mh) || (!c)) {
return -1;
}
switch(mh->msg) {
case Q931_RESTART:
if (ctrl->debug & PRI_DEBUG_Q931_STATE)
pri_message(ctrl, "-- Processing Q.931 Restart\n");
/* Reset information */
c->channelno = -1;
c->slotmap = -1;
c->chanflags = 0;
c->ds1no = 0;
c->ds1explicit = 0;
c->cis_call = 0;
c->ri = -1;
break;
case Q931_FACILITY:
break;
case Q931_SETUP:
if (ctrl->debug & PRI_DEBUG_Q931_STATE)
pri_message(ctrl, "-- Processing Q.931 Call Setup\n");
c->channelno = -1;
c->slotmap = -1;
c->chanflags = 0;
c->ds1no = 0;
c->ri = -1;
c->transcapability = -1;
c->transmoderate = -1;
c->transmultiple = -1;
c->userl1 = -1;
c->userl2 = -1;
c->userl3 = -1;
c->rateadaption = -1;
q931_party_address_init(&c->called);
q931_party_id_init(&c->local_id);
q931_party_id_init(&c->remote_id);
q931_party_redirecting_init(&c->redirecting);
/*
* Make sure that keypad and overlap digit buffers are empty in
* case they are not in the message.
*/
c->keypad_digits[0] = '\0';
c->overlap_digits[0] = '\0';
c->useruserprotocoldisc = -1;
c->useruserinfo[0] = '\0';
c->complete = 0;
c->nonisdn = 0;
c->aoc_units = -1;
c->reversecharge = -1;
/* Fall through */
case Q931_CONNECT:
case Q931_ALERTING:
case Q931_PROGRESS:
c->useruserinfo[0] = '\0';
c->cause = -1;
/* Fall through */
case Q931_CALL_PROCEEDING:
c->progress = -1;
c->progressmask = 0;
break;
case Q931_CONNECT_ACKNOWLEDGE:
pri_schedule_del(ctrl, c->retranstimer);
c->retranstimer = 0;
break;
case Q931_RELEASE:
case Q931_DISCONNECT:
c->cause = -1;
c->causecode = -1;
c->causeloc = -1;
c->aoc_units = -1;
pri_schedule_del(ctrl, c->retranstimer);
c->retranstimer = 0;
c->useruserinfo[0] = '\0';
break;
case Q931_RELEASE_COMPLETE:
pri_schedule_del(ctrl, c->retranstimer);
c->retranstimer = 0;
c->useruserinfo[0] = '\0';
/* Fall through */
case Q931_STATUS:
c->cause = -1;
c->causecode = -1;
c->causeloc = -1;
c->sugcallstate = Q931_CALL_STATE_NOT_SET;
c->aoc_units = -1;
break;
case Q931_RESTART_ACKNOWLEDGE:
c->channelno = -1;
c->ds1no = 0;
c->ds1explicit = 0;
c->cis_call = 0;
break;
case Q931_INFORMATION:
/*
* Make sure that keypad and overlap digit buffers are empty in
* case they are not in the message.
*/
c->keypad_digits[0] = '\0';
c->overlap_digits[0] = '\0';
break;
case Q931_STATUS_ENQUIRY:
break;
case Q931_SETUP_ACKNOWLEDGE:
break;
case Q931_NOTIFY:
q931_party_number_init(&c->redirection_number);
break;
case Q931_HOLD:
break;
case Q931_HOLD_ACKNOWLEDGE:
break;
case Q931_HOLD_REJECT:
c->cause = -1;
break;
case Q931_RETRIEVE:
c->channelno = 0xFF;
c->ds1no = 0;
c->ds1explicit = 0;
break;
case Q931_RETRIEVE_ACKNOWLEDGE:
break;
case Q931_RETRIEVE_REJECT:
c->cause = -1;
break;
case Q931_USER_INFORMATION:
case Q931_SEGMENT:
case Q931_CONGESTION_CONTROL:
case Q931_RESUME:
case Q931_RESUME_ACKNOWLEDGE:
case Q931_RESUME_REJECT:
case Q931_SUSPEND:
case Q931_SUSPEND_ACKNOWLEDGE:
case Q931_SUSPEND_REJECT:
pri_error(ctrl, "!! Not yet handling pre-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg);
/* Fall through */
default:
pri_error(ctrl, "!! Don't know how to pre-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg);
q931_status(ctrl,c, PRI_CAUSE_MESSAGE_TYPE_NONEXIST);
if (c->newcall)
pri_destroycall(ctrl, c);
return -1;
}
return 0;
}
static struct q931_call *q931_get_subcall_winner(struct q931_call *master)
{
if (master->pri_winner < 0) {
return NULL;
} else {
return master->subcalls[master->pri_winner];
}
}
static void initiate_hangup_if_needed(struct pri *pri, q931_call *call, int cause)
{
if (!call->hangupinitiated) {
q931_hangup(pri, call, cause);
call->alive = 0;
}
}
#if 0
static int q931_get_subcall_count(struct q931_call *call)
{
int count = 0;
int i;
call = call->master_call;
for (i = 0; i < Q931_MAX_TEI; i++) {
if (call->subcalls[i])
count++;
}
return count;
}
#endif
static void q931_set_subcall_winner(struct q931_call *subcall)
{
struct q931_call *realcall = subcall->master_call;
int i;
/* Set the winner first */
for (i = 0; i < Q931_MAX_TEI; i++) {
if (realcall->subcalls[i] && realcall->subcalls[i] == subcall) {
realcall->pri_winner = i;
}
}
if (realcall->pri_winner < 0) {
pri_error(subcall->pri, "We should always find the winner in the list!\n");
return;
}
/* Start tear down of calls that were not chosen */
for (i = 0; i < Q931_MAX_TEI; i++) {
if (realcall->subcalls[i] && realcall->subcalls[i] != subcall) {
initiate_hangup_if_needed(realcall->subcalls[i]->pri, realcall->subcalls[i],
PRI_CAUSE_NONSELECTED_USER_CLEARING);
}
}
}
static struct q931_call *q931_get_subcall(struct pri *ctrl, struct q931_call *master_call)
{
int i;
struct q931_call *cur;
int firstfree = -1;
/* First try to locate our subcall */
for (i = 0; i < Q931_MAX_TEI; i++) {
if (master_call->subcalls[i]) {
if (master_call->subcalls[i]->pri == ctrl) {
return master_call->subcalls[i];
}
} else if (firstfree == -1) {
firstfree = i;
}
}
if (firstfree < 0) {
pri_error(ctrl, "Tried to add more than %d TEIs to call and failed\n",
Q931_MAX_TEI);
return NULL;
}
/* Create new subcall. */
cur = malloc(sizeof(*cur));
if (!cur) {
pri_error(ctrl, "Unable to allocate call\n");
return NULL;
}
*cur = *master_call;
cur->pri = ctrl;
cur->next = NULL;
cur->apdus = NULL;
cur->bridged_call = NULL;
//cur->master_call = master_call; /* We get this assignment for free. */
for (i = 0; i < Q931_MAX_TEI; ++i) {
cur->subcalls[i] = NULL;
}
cur->t303_timer = 0;/* T303 should only be on on the master call */
cur->hold_timer = 0;
cur->retranstimer = 0;
/* Assume we sent a SETUP and this is the first response to it from this peer. */
cur->ourcallstate = Q931_CALL_STATE_CALL_INITIATED;
cur->peercallstate = Q931_CALL_STATE_CALL_PRESENT;
master_call->subcalls[firstfree] = cur;
if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
pri_message(ctrl, "Adding subcall %p for TEI %d to call %p at position %d\n",
cur, ctrl->tei, master_call, firstfree);
}
/* Should only get here if the TEI is not found */
return cur;
}
int q931_receive(struct pri *ctrl, q931_h *h, int len)
{
q931_mh *mh;
q931_call *c;
q931_ie *ie;
unsigned int x;
int y;
int res;
int r;
int mandies[MAX_MAND_IES];
int missingmand;
int codeset, cur_codeset;
int last_ie[8];
int cref;
memset(last_ie, 0, sizeof(last_ie));
if (ctrl->debug & PRI_DEBUG_Q931_DUMP)
q931_dump(ctrl, h, len, 0);
#ifdef LIBPRI_COUNTERS
ctrl->q931_rxcount++;
#endif
mh = (q931_mh *)(h->contents + h->crlen);
if ((h->pd != ctrl->protodisc) && (h->pd != MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) && (h->pd != MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) {
pri_error(ctrl, "Warning: unknown/inappropriate protocol discriminator received (%02x/%d)\n", h->pd, h->pd);
return 0;
}
if (((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) && (!ctrl->service_message_support)) {
/* Real service message support has not been enabled (and is OFF in libpri by default),
* so we have to revert to the 'traditional' KLUDGE of changing byte 4 from a 0xf (SERVICE)
* to a 0x7 (SERVICE ACKNOWLEDGE) */
/* This is the weird maintenance stuff. We majorly
KLUDGE this by changing byte 4 from a 0xf (SERVICE)
to a 0x7 (SERVICE ACKNOWLEDGE) */
h->raw[h->crlen + 2] -= 0x8;
q931_xmit(ctrl, h, len, 1, 0);
return 0;
}
cref = q931_cr(h);
c = q931_getcall(ctrl, cref);
if (!c) {
pri_error(ctrl, "Unable to locate call %d\n", cref);
return -1;
}
if (c->master_call->outboundbroadcast && ctrl != PRI_MASTER(ctrl)) {
c = q931_get_subcall(ctrl, c->master_call);
if (!c) {
pri_error(ctrl, "Unable to locate subcall for %d\n", cref);
return -1;
}
}
if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
pri_message(ctrl,
"Received message for call %p on %p TEI/SAPI %d/%d, call->pri is %p TEI/SAPI %d/%d\n",
c,
ctrl, ctrl->tei, ctrl->sapi,
c->pri, c->pri->tei, c->pri->sapi);
}
/* Preliminary handling */
if ((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) {
prepare_to_handle_maintenance_message(ctrl, mh, c);
} else {
prepare_to_handle_q931_message(ctrl, mh, c);
}
q931_clr_subcommands(ctrl);
/* Handle IEs */
memset(mandies, 0, sizeof(mandies));
missingmand = 0;
for (x=0;x<sizeof(msgs) / sizeof(msgs[0]); x++) {
if (msgs[x].msgnum == mh->msg) {
memcpy(mandies, msgs[x].mandies, sizeof(mandies));
}
}
x = 0;
/* Do real IE processing */
len -= (h->crlen + 3);
codeset = cur_codeset = 0;
while(len) {
ie = (q931_ie *)(mh->data + x);
for (y=0;y<MAX_MAND_IES;y++) {
if (mandies[y] == Q931_FULL_IE(cur_codeset, ie->ie))
mandies[y] = 0;
}
r = ielen(ie);
if (r > len) {
pri_error(ctrl, "XXX Message longer than it should be?? XXX\n");
return -1;
}
/* Special processing for codeset shifts */
switch (ie->ie & 0xf8) {
case Q931_LOCKING_SHIFT:
y = ie->ie & 7; /* Requested codeset */
/* Locking shifts couldn't go to lower codeset, and couldn't follows non-locking shifts - verify this */
if ((cur_codeset != codeset) && (ctrl->debug & PRI_DEBUG_Q931_ANOMALY))
pri_message(ctrl, "XXX Locking shift immediately follows non-locking shift (from %d through %d to %d) XXX\n", codeset, cur_codeset, y);
if (y > 0) {
if ((y < codeset) && (ctrl->debug & PRI_DEBUG_Q931_ANOMALY))
pri_error(ctrl, "!! Trying to locked downshift codeset from %d to %d !!\n", codeset, y);
codeset = cur_codeset = y;
}
else {
/* Locking shift to codeset 0 is forbidden by all specifications */
pri_error(ctrl, "!! Invalid locking shift to codeset 0 !!\n");
}
break;
case Q931_NON_LOCKING_SHIFT:
cur_codeset = ie->ie & 7;
break;
default:
/* Sanity check for IE code order */
if (!(ie->ie & 0x80)) {
if (last_ie[cur_codeset] > ie->ie) {
if ((ctrl->debug & PRI_DEBUG_Q931_ANOMALY))
pri_message(ctrl, "XXX Out-of-order IE %d at codeset %d (last was %d)\n", ie->ie, cur_codeset, last_ie[cur_codeset]);
}
else
last_ie[cur_codeset] = ie->ie;
}
/* Ignore non-locking shifts for TR41459-based signalling */
switch (ctrl->switchtype) {
case PRI_SWITCH_LUCENT5E:
case PRI_SWITCH_ATT4ESS:
if (cur_codeset != codeset) {
if ((ctrl->debug & PRI_DEBUG_Q931_DUMP))
pri_message(ctrl, "XXX Ignoring IE %d for temporary codeset %d XXX\n", ie->ie, cur_codeset);
break;
}
/* Fall through */
default:
y = q931_handle_ie(cur_codeset, ctrl, c, mh->msg, ie);
/* XXX Applicable to codeset 0 only? XXX */
if (!cur_codeset && !(ie->ie & 0xf0) && (y < 0))
mandies[MAX_MAND_IES - 1] = Q931_FULL_IE(cur_codeset, ie->ie);
}
/* Reset current codeset */
cur_codeset = codeset;
}
x += r;
len -= r;
}
missingmand = 0;
for (x=0;x<MAX_MAND_IES;x++) {
if (mandies[x]) {
/* check if there is no channel identification when we're configured as network -> that's not an error */
if (((ctrl->localtype != PRI_NETWORK) || (mh->msg != Q931_SETUP) || (mandies[x] != Q931_CHANNEL_IDENT)) &&
((mh->msg != Q931_PROGRESS) || (mandies[x] != Q931_PROGRESS_INDICATOR))) {
pri_error(ctrl, "XXX Missing handling for mandatory IE %d (cs%d, %s) XXX\n", Q931_IE_IE(mandies[x]), Q931_IE_CODESET(mandies[x]), ie2str(mandies[x]));
missingmand++;
}
}
}
/* Post handling */
if ((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) {
res = post_handle_maintenance_message(ctrl, h->pd, mh, c);
} else {
int allow_event = 1, allow_posthandle = 1;
if (c->master_call->outboundbroadcast) {
nt_ptmp_handle_q931_message(ctrl, mh, c, &allow_event, &allow_posthandle);
}
if (allow_posthandle) {
res = post_handle_q931_message(ctrl, mh, c, missingmand);
if (res == Q931_RES_HAVEEVENT && !allow_event) {
res = 0;
}
} else {
res = 0;
}
}
return res;
}
static int post_handle_maintenance_message(struct pri *ctrl, int protodisc, struct q931_mh *mh, struct q931_call *c)
{
/* Do some maintenance stuff */
if (((protodisc == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) && (mh->msg == ATT_SERVICE))
|| ((protodisc == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2) && (mh->msg == NATIONAL_SERVICE))) {
if (c->channelno > 0) {
ctrl->ev.e = PRI_EVENT_SERVICE;
ctrl->ev.service.channel = q931_encode_channel(c);
ctrl->ev.service.changestatus = 0x0f & c->changestatus;
} else {
switch (0x0f & c->changestatus) {
case SERVICE_CHANGE_STATUS_INSERVICE:
ctrl->ev.e = PRI_EVENT_DCHAN_UP;
q921_dchannel_up(ctrl);
break;
case SERVICE_CHANGE_STATUS_OUTOFSERVICE:
ctrl->ev.e = PRI_EVENT_DCHAN_DOWN;
q921_dchannel_down(ctrl);
break;
default:
pri_error(ctrl, "!! Don't know how to handle span service change status '%d'\n", (0x0f & c->changestatus));
return -1;
}
}
maintenance_service_ack(ctrl, c);
return Q931_RES_HAVEEVENT;
}
if (((protodisc == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) && (mh->msg == ATT_SERVICE_ACKNOWLEDGE))
|| ((protodisc == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2) && (mh->msg == NATIONAL_SERVICE_ACKNOWLEDGE))) {
if (c->channelno > 0) {
ctrl->ev.e = PRI_EVENT_SERVICE_ACK;
ctrl->ev.service_ack.channel = q931_encode_channel(c);
ctrl->ev.service_ack.changestatus = 0x0f & c->changestatus;
} else {
switch (0x0f & c->changestatus) {
case SERVICE_CHANGE_STATUS_INSERVICE:
ctrl->ev.e = PRI_EVENT_DCHAN_UP;
q921_dchannel_up(ctrl);
break;
case SERVICE_CHANGE_STATUS_OUTOFSERVICE:
ctrl->ev.e = PRI_EVENT_DCHAN_DOWN;
q921_dchannel_down(ctrl);
break;
default:
pri_error(ctrl, "!! Don't know how to handle span service change status '%d'\n", (0x0f & c->changestatus));
return -1;
}
}
return Q931_RES_HAVEEVENT;
}
pri_error(ctrl, "!! Don't know how to post-handle maintenance message type %d\n", mh->msg);
return -1;
}
/*!
* \internal
* \brief Rank the given Q.931 call state for call etablishment.
*
* \param state Q.931 call state to rank for competing PTMP NT calls.
*
* \return Call establishment state ranking.
*/
static enum Q931_RANKED_CALL_STATE q931_rank_state(enum Q931_CALL_STATE state)
{
enum Q931_RANKED_CALL_STATE rank;
switch (state) {
case Q931_CALL_STATE_CALL_INITIATED:
case Q931_CALL_STATE_CALL_PRESENT:
rank = Q931_RANKED_CALL_STATE_PRESENT;
break;
case Q931_CALL_STATE_OVERLAP_SENDING:
case Q931_CALL_STATE_OVERLAP_RECEIVING:
rank = Q931_RANKED_CALL_STATE_OVERLAP;
break;
case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING:
case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING:
rank = Q931_RANKED_CALL_STATE_PROCEEDING;
break;
case Q931_CALL_STATE_CALL_DELIVERED:
case Q931_CALL_STATE_CALL_RECEIVED:
case Q931_CALL_STATE_CONNECT_REQUEST:
rank = Q931_RANKED_CALL_STATE_ALERTING;
break;
case Q931_CALL_STATE_ACTIVE:
case Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE:
rank = Q931_RANKED_CALL_STATE_CONNECT;
break;
default:
rank = Q931_RANKED_CALL_STATE_OTHER;
break;
}
return rank;
}
/*!
* \brief Determine if the master will pass an event to the upper layer.
*
* \param ctrl D channel controller.
* \param subcall Q.931 call leg.
* \param msg_type Current message type being processed.
*
* \note This function must parallel nt_ptmp_handle_q931_message().
*
* \retval TRUE if the master will pass an event to the upper layer.
* \retval FALSE if the event will be blocked.
*/
int q931_master_pass_event(struct pri *ctrl, struct q931_call *subcall, int msg_type)
{
struct q931_call *winner;
struct q931_call *master;
enum Q931_RANKED_CALL_STATE master_rank;
enum Q931_RANKED_CALL_STATE subcall_rank;
int will_pass; /*!< TRUE if the master will pass an event to the upper layer. */
master = subcall->master_call;
if (subcall == master) {
/* We are the master call so of course the master will pass an event. */
return 1;
}
winner = q931_get_subcall_winner(master);
if (winner && subcall == winner) {
/* We are the winner so of course the master will pass an event. */
return 1;
}
master_rank = q931_rank_state(master->ourcallstate);
will_pass = 0;
switch (msg_type) {
case Q931_SETUP_ACKNOWLEDGE:
#if 0 /* Overlap dialing in PTMP NT mode not supported at the present time. */
if (master_rank < Q931_RANKED_CALL_STATE_OVERLAP) {
will_pass = 1;
}
#endif /* Overlap dialing in PTMP NT mode not supported at the present time. */
break;
case Q931_CALL_PROCEEDING:
if (master_rank < Q931_RANKED_CALL_STATE_PROCEEDING) {
will_pass = 1;
}
break;
case Q931_PROGRESS:
/*
* We will just ignore this message since there could be multiple devices
* competing for this call. Who has access to the B channel at this time
* to give in-band signals anyway?
*/
break;
case Q931_ALERTING:
if (master_rank < Q931_RANKED_CALL_STATE_ALERTING) {
will_pass = 1;
}
break;
case Q931_CONNECT:
if (master_rank < Q931_RANKED_CALL_STATE_CONNECT) {
/* We are expected to be the winner for the next message. */
will_pass = 1;
}
break;
case Q931_DISCONNECT:
case Q931_RELEASE:
case Q931_RELEASE_COMPLETE:
/* Only deal with the winner. */
break;
case Q931_FACILITY:
case Q931_NOTIFY:
if (!winner) {
/* The overlap rank does not count here. */
if (master_rank == Q931_RANKED_CALL_STATE_OVERLAP) {
master_rank = Q931_RANKED_CALL_STATE_PRESENT;
}
subcall_rank = q931_rank_state(subcall->ourcallstate);
if (subcall_rank == Q931_RANKED_CALL_STATE_OVERLAP) {
subcall_rank = Q931_RANKED_CALL_STATE_PRESENT;
}
if (master_rank == subcall_rank) {
/*
* No winner yet but the subcall is as advanced as the master.
* Allow the supplementary service event to pass.
*/
will_pass = 1;
}
}
break;
default:
/* Only deal with the winner. */
break;
}
return will_pass;
}
/*!
* \internal
* \brief Handle outboundbroadcast incoming messages for the master_call's state.
*
* \param ctrl D channel controller.
* \param mh Q.931 message type header.
* \param subcall Q.931 call leg.
* \param allow_event Where to set the allow event to upper layer flag.
* \param allow_posthandle Where to set the allow post handle event flag.
*
* \details
* This is where we interact the subcalls state with the master_call's state.
*
* \note This function must parallel q931_master_pass_event().
*
* \return Nothing
*/
static void nt_ptmp_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *subcall, int *allow_event, int *allow_posthandle)
{
struct q931_call *master = subcall->master_call;
struct q931_call *winner = q931_get_subcall_winner(master);
enum Q931_RANKED_CALL_STATE master_rank;
enum Q931_RANKED_CALL_STATE subcall_rank;
enum Q931_CALL_STATE newstate;
/* For broadcast calls, we default to not allowing events to keep events received to a minimum
* and to allow post processing, since that is where hangup and subcall state handling and other processing is done */
*allow_event = 0;
*allow_posthandle = 1;
master_rank = q931_rank_state(master->ourcallstate);
switch (mh->msg) {
case Q931_SETUP_ACKNOWLEDGE:
#if 0 /* Overlap dialing in PTMP NT mode not supported at the present time. */
if (master_rank < Q931_RANKED_CALL_STATE_OVERLAP) {
*allow_event = 1;
UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_OVERLAP_SENDING);
}
#endif /* Overlap dialing in PTMP NT mode not supported at the present time. */
break;
case Q931_CALL_PROCEEDING:
if (master_rank < Q931_RANKED_CALL_STATE_PROCEEDING) {
*allow_event = 1;
UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING);
}
break;
case Q931_PROGRESS:
/*
* We will just ignore this message since there could be multiple devices
* competing for this call. Who has access to the B channel at this time
* to give in-band signals anyway?
*/
break;
case Q931_ALERTING:
if (master_rank < Q931_RANKED_CALL_STATE_ALERTING) {
*allow_event = 1;
UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_CALL_DELIVERED);
}
break;
case Q931_CONNECT:
if (master_rank < Q931_RANKED_CALL_STATE_CONNECT) {
UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_ACTIVE);
q931_set_subcall_winner(subcall);
*allow_event = 1;
} else {
/* Call clearing of non selected calls occurs in
* q931_set_subcall_winner() - All we need to do is make sure
* that this connect is not acknowledged */
*allow_posthandle = 0;
}
break;
case Q931_DISCONNECT:
newstate = Q931_CALL_STATE_DISCONNECT_INDICATION;
goto process_hangup;
case Q931_RELEASE:
case Q931_RELEASE_COMPLETE:
newstate = Q931_CALL_STATE_NULL;
process_hangup:
if (!winner) {
/* If there's not a winner, we just take the cause and pass it up to the
* master_call */
master->cause = subcall->cause;
} else {
/* There *is* a winner */
if (subcall == winner) {
/* .. and we're it: */
*allow_event = 1;
UPDATE_OURCALLSTATE(ctrl, master, newstate);
}
}
break;
case Q931_FACILITY:
case Q931_NOTIFY:
if (winner) {
if (subcall == winner) {
/* Only deal with the winner. */
*allow_event = 1;
}
} else {
/* The overlap rank does not count here. */
if (master_rank == Q931_RANKED_CALL_STATE_OVERLAP) {
master_rank = Q931_RANKED_CALL_STATE_PRESENT;
}
subcall_rank = q931_rank_state(subcall->ourcallstate);
if (subcall_rank == Q931_RANKED_CALL_STATE_OVERLAP) {
subcall_rank = Q931_RANKED_CALL_STATE_PRESENT;
}
if (master_rank == subcall_rank) {
/*
* No winner yet but the subcall is as advanced as the master.
* Allow the supplementary service event to pass.
*/
*allow_event = 1;
}
}
break;
default:
if (winner && subcall == winner) {
/* Only deal with the winner. */
*allow_event = 1;
}
break;
}
}
/*!
* \internal
* \brief Fill in the FACILITY event fields.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
*
* \return Nothing
*/
static void q931_fill_facility_event(struct pri *ctrl, struct q931_call *call)
{
ctrl->ev.e = PRI_EVENT_FACILITY;
ctrl->ev.facility.subcmds = &ctrl->subcmds;
ctrl->ev.facility.channel = q931_encode_channel(call);
ctrl->ev.facility.cref = call->cr;
ctrl->ev.facility.call = call->master_call;
ctrl->ev.facility.subcall = call;
/* Need to do this for backward compatibility with struct pri_event_facname */
libpri_copy_string(ctrl->ev.facility.callingname, call->remote_id.name.str,
sizeof(ctrl->ev.facility.callingname));
libpri_copy_string(ctrl->ev.facility.callingnum, call->remote_id.number.str,
sizeof(ctrl->ev.facility.callingnum));
ctrl->ev.facility.callingpres = q931_party_id_presentation(&call->remote_id);
ctrl->ev.facility.callingplan = call->remote_id.number.plan;
}
/*!
* \internal
* \brief Find the active call given the held call.
*
* \param ctrl D channel controller.
* \param held_call Held call to help locate a compatible active call.
*
* \retval master-active-call on success.
* \retval NULL on error.
*/
static struct q931_call *q931_find_held_active_call(struct pri *ctrl, struct q931_call *held_call)
{
struct pri *master;
struct q931_call *cur;
struct q931_call *winner;
struct q931_call *match;
match = NULL;
master = PRI_MASTER(ctrl);
for (cur = *master->callpool; cur; cur = cur->next) {
if (cur->hold_state == Q931_HOLD_STATE_IDLE) {
/* Found an active call. */
winner = q931_find_winning_call(cur);
if (!winner || (BRI_NT_PTMP(ctrl) && winner->pri != held_call->pri)) {
/* There is no winner or the active call does not go to the same TEI. */
continue;
}
switch (winner->ourcallstate) {
case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING:
case Q931_CALL_STATE_CALL_DELIVERED:
case Q931_CALL_STATE_CALL_RECEIVED:
case Q931_CALL_STATE_CONNECT_REQUEST:
case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING:
case Q931_CALL_STATE_ACTIVE:
break;
default:
/* Active call not in a good state to transfer. */
continue;
}
if (q931_party_number_cmp(&winner->remote_id.number,
&held_call->remote_id.number)) {
/* The remote party number does not match. This is a weak match. */
match = cur;
continue;
}
/* Found an exact match. */
match = cur;
break;
}
}
return match;
}
/*!
* \internal
* \brief Find the held call given the active call.
*
* \param ctrl D channel controller.
* \param active_call Active call to help locate a compatible held call.
*
* \retval master-held-call on success.
* \retval NULL on error.
*/
static struct q931_call *q931_find_held_call(struct pri *ctrl, struct q931_call *active_call)
{
struct pri *master;
struct q931_call *cur;
struct q931_call *winner;
struct q931_call *match;
match = NULL;
master = PRI_MASTER(ctrl);
for (cur = *master->callpool; cur; cur = cur->next) {
if (cur->hold_state == Q931_HOLD_STATE_CALL_HELD) {
/* Found a held call. */
winner = q931_find_winning_call(cur);
if (!winner || (BRI_NT_PTMP(ctrl) && winner->pri != active_call->pri)) {
/* There is no winner or the held call does not go to the same TEI. */
continue;
}
switch (winner->ourcallstate) {
case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING:
case Q931_CALL_STATE_CALL_DELIVERED:
case Q931_CALL_STATE_CALL_RECEIVED:
case Q931_CALL_STATE_CONNECT_REQUEST:
case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING:
case Q931_CALL_STATE_ACTIVE:
break;
default:
/* Held call not in a good state to transfer. */
continue;
}
if (q931_party_number_cmp(&winner->remote_id.number,
&active_call->remote_id.number)) {
/* The remote party number does not match. This is a weak match. */
match = cur;
continue;
}
/* Found an exact match. */
match = cur;
break;
}
}
return match;
}
/*!
* \internal
* \brief Process the decoded information in the Q.931 message.
*
* \param ctrl D channel controller.
* \param mh Q.931 message header.
* \param c Q.931 call leg.
* \param missingmand Number of missing mandatory ie's.
*
* \retval 0 if no error or event.
* \retval Q931_RES_HAVEEVENT if have an event.
* \retval -1 on error.
*/
static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *c, int missingmand)
{
int res;
struct apdu_event *cur = NULL;
struct pri_subcommand *subcmd;
struct q931_call *master_call;
switch(mh->msg) {
case Q931_RESTART:
if (missingmand) {
q931_status(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING);
pri_destroycall(ctrl, c);
break;
}
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_RESTART);
c->peercallstate = Q931_CALL_STATE_RESTART_REQUEST;
/* Send back the Restart Acknowledge */
restart_ack(ctrl, c);
/* Notify user of restart event */
ctrl->ev.e = PRI_EVENT_RESTART;
ctrl->ev.restart.channel = q931_encode_channel(c);
return Q931_RES_HAVEEVENT;
case Q931_SETUP:
if (missingmand) {
q931_release_complete(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING);
break;
}
/* Must be new call */
if (!c->newcall) {
break;
}
if (c->progressmask & PRI_PROG_CALLER_NOT_ISDN)
c->nonisdn = 1;
c->newcall = 0;
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_PRESENT);
c->peercallstate = Q931_CALL_STATE_CALL_INITIATED;
/* it's not yet a call since higher level can respond with RELEASE or RELEASE_COMPLETE */
c->alive = 0;
if (c->transmoderate != TRANS_MODE_64_CIRCUIT) {
q931_release_complete(ctrl, c, PRI_CAUSE_BEARERCAPABILITY_NOTIMPL);
break;
}
if (c->redirecting.from.number.valid && !c->redirecting.count) {
/*
* This is most likely because the redirecting number came in
* with the redirecting ie only and not a DivertingLegInformation2.
*/
c->redirecting.count = 1;
}
if (c->redirecting.state == Q931_REDIRECTING_STATE_PENDING_TX_DIV_LEG_3) {
/*
* Valid for Q.SIG and ETSI PRI/BRI-PTP modes:
* Setup the redirecting.to informtion so we can identify
* if the user wants to manually supply the COLR for this
* redirected to number if further redirects could happen.
*
* All the user needs to do is set the REDIRECTING(to-pres)
* to the COLR and REDIRECTING(to-num) = complete-dialed-number
* (i.e. CALLERID(dnid)) to be safe after determining that the
* incoming call was redirected by checking if the
* REDIRECTING(count) is nonzero.
*/
c->redirecting.to.number = c->called.number;
c->redirecting.to.number.presentation =
PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED;
}
ctrl->ev.e = PRI_EVENT_RING;
ctrl->ev.ring.subcmds = &ctrl->subcmds;
ctrl->ev.ring.channel = q931_encode_channel(c);
/* Calling party information */
ctrl->ev.ring.callingpres = q931_party_id_presentation(&c->remote_id);
ctrl->ev.ring.callingplan = c->remote_id.number.plan;
if (c->remote_id.number.valid
&& (c->remote_id.number.presentation == PRES_ALLOWED_NETWORK_NUMBER
|| c->remote_id.number.presentation == PRES_PROHIB_NETWORK_NUMBER)) {
ctrl->ev.ring.callingplanani = c->remote_id.number.plan;
libpri_copy_string(ctrl->ev.ring.callingani, c->remote_id.number.str, sizeof(ctrl->ev.ring.callingani));
} else {
ctrl->ev.ring.callingplanani = -1;
ctrl->ev.ring.callingani[0] = '\0';
}
libpri_copy_string(ctrl->ev.ring.callingnum, c->remote_id.number.str, sizeof(ctrl->ev.ring.callingnum));
libpri_copy_string(ctrl->ev.ring.callingname, c->remote_id.name.str, sizeof(ctrl->ev.ring.callingname));
q931_party_id_copy_to_pri(&ctrl->ev.ring.calling, &c->remote_id);
/* for backwards compatibility, still need ctrl->ev.ring.callingsubaddr */
if (!c->remote_id.subaddress.type) { /* NSAP: Type = 0 */
libpri_copy_string(ctrl->ev.ring.callingsubaddr, (char *) c->remote_id.subaddress.data, sizeof(ctrl->ev.ring.callingsubaddr));
} else {
ctrl->ev.ring.callingsubaddr[0] = '\0';
}
ctrl->ev.ring.ani2 = c->ani2;
/* Called party information */
ctrl->ev.ring.calledplan = c->called.number.plan;
libpri_copy_string(ctrl->ev.ring.callednum, c->called.number.str, sizeof(ctrl->ev.ring.callednum));
q931_party_subaddress_copy_to_pri(&ctrl->ev.ring.called_subaddress, &c->called.subaddress);
/* Original called party information (For backward compatibility) */
libpri_copy_string(ctrl->ev.ring.origcalledname, c->redirecting.orig_called.name.str, sizeof(ctrl->ev.ring.origcalledname));
libpri_copy_string(ctrl->ev.ring.origcallednum, c->redirecting.orig_called.number.str, sizeof(ctrl->ev.ring.origcallednum));
ctrl->ev.ring.callingplanorigcalled = c->redirecting.orig_called.number.plan;
if (c->redirecting.orig_called.number.valid
|| c->redirecting.orig_called.name.valid) {
ctrl->ev.ring.origredirectingreason = c->redirecting.orig_reason;
} else {
ctrl->ev.ring.origredirectingreason = -1;
}
/* Redirecting from party information (For backward compatibility) */
ctrl->ev.ring.callingplanrdnis = c->redirecting.from.number.plan;
libpri_copy_string(ctrl->ev.ring.redirectingnum, c->redirecting.from.number.str, sizeof(ctrl->ev.ring.redirectingnum));
libpri_copy_string(ctrl->ev.ring.redirectingname, c->redirecting.from.name.str, sizeof(ctrl->ev.ring.redirectingname));
ctrl->ev.ring.redirectingreason = c->redirecting.reason;
libpri_copy_string(ctrl->ev.ring.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.ring.useruserinfo));
c->useruserinfo[0] = '\0';
libpri_copy_string(ctrl->ev.ring.keypad_digits, c->keypad_digits,
sizeof(ctrl->ev.ring.keypad_digits));
ctrl->ev.ring.flexible = ! (c->chanflags & FLAG_EXCLUSIVE);
ctrl->ev.ring.cref = c->cr;
ctrl->ev.ring.call = c->master_call;
ctrl->ev.ring.layer1 = c->userl1;
ctrl->ev.ring.complete = c->complete;
ctrl->ev.ring.ctype = c->transcapability;
ctrl->ev.ring.progress = c->progress;
ctrl->ev.ring.progressmask = c->progressmask;
ctrl->ev.ring.reversecharge = c->reversecharge;
if (c->redirecting.count) {
subcmd = q931_alloc_subcommand(ctrl);
if (subcmd) {
/* Setup redirecting subcommand */
subcmd->cmd = PRI_SUBCMD_REDIRECTING;
q931_party_redirecting_copy_to_pri(&subcmd->u.redirecting,
&c->redirecting);
}
}
return Q931_RES_HAVEEVENT;
case Q931_ALERTING:
stop_t303(c);
if (c->newcall) {
q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
break;
}
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_DELIVERED);
c->peercallstate = Q931_CALL_STATE_CALL_RECEIVED;
ctrl->ev.e = PRI_EVENT_RINGING;
ctrl->ev.ringing.subcmds = &ctrl->subcmds;
ctrl->ev.ringing.channel = q931_encode_channel(c);
ctrl->ev.ringing.cref = c->cr;
ctrl->ev.ringing.call = c->master_call;
ctrl->ev.ringing.progress = c->progress;
ctrl->ev.ringing.progressmask = c->progressmask;
libpri_copy_string(ctrl->ev.ringing.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.ringing.useruserinfo));
c->useruserinfo[0] = '\0';
cur = c->apdus;
while (cur) {
if (!cur->sent && cur->message == Q931_FACILITY) {
q931_facility(ctrl, c);
break;
}
cur = cur->next;
}
return Q931_RES_HAVEEVENT;
case Q931_CONNECT:
stop_t303(c);
if (c->newcall) {
q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
break;
}
if (c->ourcallstate == Q931_CALL_STATE_ACTIVE) {
q931_status(ctrl, c, PRI_CAUSE_WRONG_MESSAGE);
break;
}
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_ACTIVE);
c->peercallstate = Q931_CALL_STATE_CONNECT_REQUEST;
ctrl->ev.e = PRI_EVENT_ANSWER;
ctrl->ev.answer.subcmds = &ctrl->subcmds;
ctrl->ev.answer.channel = q931_encode_channel(c);
ctrl->ev.answer.cref = c->cr;
ctrl->ev.answer.call = c->master_call;
ctrl->ev.answer.progress = c->progress;
ctrl->ev.answer.progressmask = c->progressmask;
libpri_copy_string(ctrl->ev.answer.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.answer.useruserinfo));
c->useruserinfo[0] = '\0';
q931_connect_acknowledge(ctrl, c);
if (c->cis_auto_disconnect && c->cis_call) {
/* Make sure WE release when we initiate a signalling only connection */
q931_hangup(ctrl, c, PRI_CAUSE_NORMAL_CLEARING);
break;
} else {
c->incoming_ct_state = INCOMING_CT_STATE_IDLE;
/* Setup connected line subcommand */
subcmd = q931_alloc_subcommand(ctrl);
if (subcmd) {
subcmd->cmd = PRI_SUBCMD_CONNECTED_LINE;
q931_party_id_copy_to_pri(&subcmd->u.connected_line.id, &c->remote_id);
}
return Q931_RES_HAVEEVENT;
}
case Q931_FACILITY:
if (c->newcall) {
q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
break;
}
switch (c->incoming_ct_state) {
case INCOMING_CT_STATE_POST_CONNECTED_LINE:
c->incoming_ct_state = INCOMING_CT_STATE_IDLE;
subcmd = q931_alloc_subcommand(ctrl);
if (subcmd) {
subcmd->cmd = PRI_SUBCMD_CONNECTED_LINE;
q931_party_id_copy_to_pri(&subcmd->u.connected_line.id, &c->remote_id);
}
break;
default:
break;
}
if (ctrl->subcmds.counter_subcmd) {
q931_fill_facility_event(ctrl, c);
return Q931_RES_HAVEEVENT;
}
break;
case Q931_PROGRESS:
if (missingmand) {
q931_status(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING);
pri_destroycall(ctrl, c);
break;
}
ctrl->ev.e = PRI_EVENT_PROGRESS;
ctrl->ev.proceeding.cause = c->cause;
/* Fall through */
case Q931_CALL_PROCEEDING:
stop_t303(c);
ctrl->ev.proceeding.subcmds = &ctrl->subcmds;
if (c->newcall) {
q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
break;
}
if ((c->ourcallstate != Q931_CALL_STATE_CALL_INITIATED) &&
(c->ourcallstate != Q931_CALL_STATE_OVERLAP_SENDING) &&
(c->ourcallstate != Q931_CALL_STATE_CALL_DELIVERED) &&
(c->ourcallstate != Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING)) {
q931_status(ctrl,c,PRI_CAUSE_WRONG_MESSAGE);
break;
}
ctrl->ev.proceeding.channel = q931_encode_channel(c);
if (mh->msg == Q931_CALL_PROCEEDING) {
ctrl->ev.e = PRI_EVENT_PROCEEDING;
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING);
c->peercallstate = Q931_CALL_STATE_INCOMING_CALL_PROCEEDING;
}
ctrl->ev.proceeding.progress = c->progress;
ctrl->ev.proceeding.progressmask = c->progressmask;
ctrl->ev.proceeding.cref = c->cr;
ctrl->ev.proceeding.call = c->master_call;
cur = c->apdus;
while (cur) {
if (!cur->sent && cur->message == Q931_FACILITY) {
q931_facility(ctrl, c);
break;
}
cur = cur->next;
}
return Q931_RES_HAVEEVENT;
case Q931_CONNECT_ACKNOWLEDGE:
if (c->newcall) {
q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
break;
}
if (!(c->ourcallstate == Q931_CALL_STATE_CONNECT_REQUEST) &&
!(c->ourcallstate == Q931_CALL_STATE_ACTIVE &&
(ctrl->localtype == PRI_NETWORK || ctrl->switchtype == PRI_SWITCH_QSIG))) {
q931_status(ctrl,c,PRI_CAUSE_WRONG_MESSAGE);
break;
}
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_ACTIVE);
c->peercallstate = Q931_CALL_STATE_ACTIVE;
break;
case Q931_STATUS:
if (missingmand) {
q931_status(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING);
pri_destroycall(ctrl, c);
break;
}
if (c->newcall) {
if (c->cr & 0x7fff)
q931_release_complete(ctrl,c,PRI_CAUSE_WRONG_CALL_STATE);
break;
}
/* Do nothing */
/* Also when the STATUS asks for the call of an unexisting reference send RELEASE_COMPL */
if ((ctrl->debug & PRI_DEBUG_Q931_ANOMALY) &&
(c->cause != PRI_CAUSE_INTERWORKING))
pri_error(ctrl, "Received unsolicited status: %s\n", pri_cause2str(c->cause));
/* Workaround for S-12 ver 7.3 - it responds for invalid/non-implemented IEs at SETUP with null call state */
#if 0
if (!c->sugcallstate && (c->ourcallstate != Q931_CALL_STATE_CALL_INITIATED)) {
#else
/* Remove "workaround" since it breaks certification testing. If we receive a STATUS message of call state
* NULL and we are not in the call state NULL we must clear resources and return to the call state to pass
* testing. See section 5.8.11 of Q.931 */
if (!c->sugcallstate) {
#endif
ctrl->ev.hangup.subcmds = &ctrl->subcmds;
ctrl->ev.hangup.channel = q931_encode_channel(c);
ctrl->ev.hangup.cause = c->cause;
ctrl->ev.hangup.cref = c->cr;
ctrl->ev.hangup.call = c->master_call;
ctrl->ev.hangup.aoc_units = c->aoc_units;
ctrl->ev.hangup.call_held = NULL;
ctrl->ev.hangup.call_active = NULL;
libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo));
/* Free resources */
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL);
c->peercallstate = Q931_CALL_STATE_NULL;
if (c->alive) {
ctrl->ev.e = PRI_EVENT_HANGUP;
res = Q931_RES_HAVEEVENT;
c->alive = 0;
} else if (c->sendhangupack) {
res = Q931_RES_HAVEEVENT;
ctrl->ev.e = PRI_EVENT_HANGUP_ACK;
pri_hangup(ctrl, c, c->cause);
} else {
pri_hangup(ctrl, c, c->cause);
res = 0;
}
if (res)
return res;
}
break;
case Q931_RELEASE_COMPLETE:
c->hangupinitiated = 1;
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL);
c->peercallstate = Q931_CALL_STATE_NULL;
ctrl->ev.hangup.subcmds = &ctrl->subcmds;
ctrl->ev.hangup.channel = q931_encode_channel(c);
ctrl->ev.hangup.cause = c->cause;
ctrl->ev.hangup.cref = c->cr;
ctrl->ev.hangup.call = c->master_call;
ctrl->ev.hangup.aoc_units = c->aoc_units;
ctrl->ev.hangup.call_held = NULL;
ctrl->ev.hangup.call_active = NULL;
libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo));
c->useruserinfo[0] = '\0';
/* Free resources */
if (c->alive) {
ctrl->ev.e = PRI_EVENT_HANGUP;
res = Q931_RES_HAVEEVENT;
c->alive = 0;
} else if (c->sendhangupack) {
res = Q931_RES_HAVEEVENT;
ctrl->ev.e = PRI_EVENT_HANGUP_ACK;
pri_hangup(ctrl, c, c->cause);
} else
res = 0;
if (res)
return res;
else
pri_hangup(ctrl,c,c->cause);
break;
case Q931_RELEASE:
c->hangupinitiated = 1;
if (missingmand) {
/* Force cause to be mandatory IE missing */
c->cause = PRI_CAUSE_MANDATORY_IE_MISSING;
}
if (c->ourcallstate == Q931_CALL_STATE_RELEASE_REQUEST)
c->peercallstate = Q931_CALL_STATE_NULL;
else {
c->peercallstate = Q931_CALL_STATE_RELEASE_REQUEST;
}
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL);
ctrl->ev.e = PRI_EVENT_HANGUP;
ctrl->ev.hangup.subcmds = &ctrl->subcmds;
ctrl->ev.hangup.channel = q931_encode_channel(c);
ctrl->ev.hangup.cause = c->cause;
ctrl->ev.hangup.cref = c->cr;
ctrl->ev.hangup.call = c->master_call;
ctrl->ev.hangup.aoc_units = c->aoc_units;
ctrl->ev.hangup.call_held = NULL;
ctrl->ev.hangup.call_active = NULL;
libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo));
c->useruserinfo[0] = '\0';
/* Don't send release complete if they send us release
while we sent it, assume a NULL state */
if (c->newcall)
q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
else if (c->outboundbroadcast && (c != q931_get_subcall_winner(c->master_call)))
return pri_hangup(ctrl, c, -1);
else
return Q931_RES_HAVEEVENT;
break;
case Q931_DISCONNECT:
c->hangupinitiated = 1;
if (missingmand) {
/* Still let user call release */
c->cause = PRI_CAUSE_MANDATORY_IE_MISSING;
}
if (c->newcall) {
q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
break;
}
/*
* Determine if there are any calls that can be proposed for
* a transfer of held call on disconnect.
*/
ctrl->ev.hangup.call_held = NULL;
ctrl->ev.hangup.call_active = NULL;
switch (c->ourcallstate) {
case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING:
case Q931_CALL_STATE_CALL_DELIVERED:
case Q931_CALL_STATE_CALL_RECEIVED:
case Q931_CALL_STATE_CONNECT_REQUEST:
case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING:
case Q931_CALL_STATE_ACTIVE:
if (c->master_call->hold_state == Q931_HOLD_STATE_CALL_HELD) {
/* Held call is being disconnected first. */
ctrl->ev.hangup.call_held = c->master_call;
ctrl->ev.hangup.call_active = q931_find_held_active_call(ctrl, c);
} else {
/* Active call is being disconnected first. */
if (q931_find_winning_call(c) == c) {
/*
* Only a normal call or the winning call of a broadcast SETUP
* can participate in a transfer of held call on disconnet.
*/
ctrl->ev.hangup.call_active = c->master_call;
ctrl->ev.hangup.call_held = q931_find_held_call(ctrl, c);
}
}
break;
default:
break;
}
if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
if (ctrl->ev.hangup.call_held) {
pri_message(ctrl, "-- Found held call: %p cref:%d\n",
ctrl->ev.hangup.call_held, ctrl->ev.hangup.call_held->cr);
}
if (ctrl->ev.hangup.call_active) {
pri_message(ctrl, "-- Found active call: %p cref:%d\n",
ctrl->ev.hangup.call_active, ctrl->ev.hangup.call_active->cr);
}
if (ctrl->ev.hangup.call_held && ctrl->ev.hangup.call_active) {
pri_message(ctrl, "-- Transfer held call on disconnect possible.\n");
}
}
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_DISCONNECT_INDICATION);
c->peercallstate = Q931_CALL_STATE_DISCONNECT_REQUEST;
c->sendhangupack = 1;
/* wait for a RELEASE so that sufficient time has passed
for the inband audio to be heard */
if (ctrl->acceptinbanddisconnect && (c->progressmask & PRI_PROG_INBAND_AVAILABLE))
break;
/* Return such an event */
ctrl->ev.e = PRI_EVENT_HANGUP_REQ;
ctrl->ev.hangup.subcmds = &ctrl->subcmds;
ctrl->ev.hangup.channel = q931_encode_channel(c);
ctrl->ev.hangup.cause = c->cause;
ctrl->ev.hangup.cref = c->cr;
ctrl->ev.hangup.call = c->master_call;
ctrl->ev.hangup.aoc_units = c->aoc_units;
libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo));
c->useruserinfo[0] = '\0';
if (c->alive)
return Q931_RES_HAVEEVENT;
else
pri_hangup(ctrl,c,c->cause);
break;
case Q931_RESTART_ACKNOWLEDGE:
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL);
c->peercallstate = Q931_CALL_STATE_NULL;
ctrl->ev.e = PRI_EVENT_RESTART_ACK;
ctrl->ev.restartack.channel = q931_encode_channel(c);
return Q931_RES_HAVEEVENT;
case Q931_INFORMATION:
/* XXX We're handling only INFORMATION messages that contain
overlap dialing received digit
+ the "Complete" msg which is basically an EOF on further digits
XXX */
if (c->newcall) {
q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
break;
}
if (c->ourcallstate != Q931_CALL_STATE_OVERLAP_RECEIVING) {
ctrl->ev.e = PRI_EVENT_KEYPAD_DIGIT;
ctrl->ev.digit.subcmds = &ctrl->subcmds;
ctrl->ev.digit.call = c->master_call;
ctrl->ev.digit.channel = q931_encode_channel(c);
libpri_copy_string(ctrl->ev.digit.digits, c->keypad_digits, sizeof(ctrl->ev.digit.digits));
return Q931_RES_HAVEEVENT;
}
ctrl->ev.e = PRI_EVENT_INFO_RECEIVED;
ctrl->ev.ring.subcmds = &ctrl->subcmds;
ctrl->ev.ring.call = c->master_call;
ctrl->ev.ring.channel = q931_encode_channel(c);
libpri_copy_string(ctrl->ev.ring.callednum, c->overlap_digits, sizeof(ctrl->ev.ring.callednum));
q931_party_id_copy_to_pri(&ctrl->ev.ring.calling, &c->remote_id);
/* for backwards compatibility, still need ctrl->ev.ring.callingsubaddr */
if (!c->remote_id.subaddress.type) { /* NSAP: Type = 0 */
libpri_copy_string(ctrl->ev.ring.callingsubaddr, (char *) c->remote_id.subaddress.data, sizeof(ctrl->ev.ring.callingsubaddr));
} else {
ctrl->ev.ring.callingsubaddr[0] = '\0';
}
ctrl->ev.ring.complete = c->complete; /* this covers IE 33 (Sending Complete) */
return Q931_RES_HAVEEVENT;
case Q931_STATUS_ENQUIRY:
if (c->newcall) {
q931_release_complete(ctrl, c, PRI_CAUSE_INVALID_CALL_REFERENCE);
} else
q931_status(ctrl,c, 0);
break;
case Q931_SETUP_ACKNOWLEDGE:
stop_t303(c);
if (c->newcall) {
q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
break;
}
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_OVERLAP_SENDING);
c->peercallstate = Q931_CALL_STATE_OVERLAP_RECEIVING;
ctrl->ev.e = PRI_EVENT_SETUP_ACK;
ctrl->ev.setup_ack.subcmds = &ctrl->subcmds;
ctrl->ev.setup_ack.channel = q931_encode_channel(c);
ctrl->ev.setup_ack.call = c->master_call;
cur = c->apdus;
while (cur) {
if (!cur->sent && cur->message == Q931_FACILITY) {
q931_facility(ctrl, c);
break;
}
cur = cur->next;
}
return Q931_RES_HAVEEVENT;
case Q931_NOTIFY:
res = 0;
switch (c->notify) {
case PRI_NOTIFY_CALL_DIVERTING:
if (c->redirection_number.valid) {
c->redirecting.to.number = c->redirection_number;
if (c->redirecting.count < PRI_MAX_REDIRECTS) {
++c->redirecting.count;
}
switch (c->ourcallstate) {
case Q931_CALL_STATE_CALL_DELIVERED:
/* Call is deflecting after we have seen an ALERTING message */
c->redirecting.reason = PRI_REDIR_FORWARD_ON_NO_REPLY;
break;
default:
/* Call is deflecting for call forwarding unconditional or busy reason. */
c->redirecting.reason = PRI_REDIR_UNKNOWN;
break;
}
/* Setup redirecting subcommand */
subcmd = q931_alloc_subcommand(ctrl);
if (subcmd) {
subcmd->cmd = PRI_SUBCMD_REDIRECTING;
q931_party_redirecting_copy_to_pri(&subcmd->u.redirecting,
&c->redirecting);
}
}
if (ctrl->subcmds.counter_subcmd) {
q931_fill_facility_event(ctrl, c);
res = Q931_RES_HAVEEVENT;
}
break;
case PRI_NOTIFY_TRANSFER_ALERTING:
case PRI_NOTIFY_TRANSFER_ACTIVE:
if (c->redirection_number.valid
&& q931_party_number_cmp(&c->remote_id.number, &c->redirection_number)) {
/* The remote party information changed. */
c->remote_id.number = c->redirection_number;
/* Setup connected line subcommand */
subcmd = q931_alloc_subcommand(ctrl);
if (subcmd) {
subcmd->cmd = PRI_SUBCMD_CONNECTED_LINE;
q931_party_id_copy_to_pri(&subcmd->u.connected_line.id,
&c->remote_id);
}
}
if (ctrl->subcmds.counter_subcmd) {
q931_fill_facility_event(ctrl, c);
res = Q931_RES_HAVEEVENT;
}
break;
default:
ctrl->ev.e = PRI_EVENT_NOTIFY;
ctrl->ev.notify.subcmds = &ctrl->subcmds;
ctrl->ev.notify.channel = q931_encode_channel(c);
ctrl->ev.notify.info = c->notify;
ctrl->ev.notify.call = c->master_call;
res = Q931_RES_HAVEEVENT;
break;
}
return res;
case Q931_HOLD:
res = 0;
if (!PRI_MASTER(ctrl)->hold_support) {
/*
* Blocking any calls from getting on HOLD effectively
* disables HOLD/RETRIEVE.
*/
q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_FACILITY_NOT_IMPLEMENTED);
break;
}
switch (c->ourcallstate) {
case Q931_CALL_STATE_CALL_RECEIVED:
case Q931_CALL_STATE_CONNECT_REQUEST:
case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING:
if (q931_is_ptmp(ctrl)) {
/* HOLD request only allowed in these states if point-to-point mode. */
q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE);
break;
}
/* Fall through */
case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING:
case Q931_CALL_STATE_CALL_DELIVERED:
case Q931_CALL_STATE_ACTIVE:
if (!q931_find_winning_call(c)) {
/*
* Only the winning call of a broadcast SETUP can do hold since the
* call must be answered first.
*/
q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE);
break;
}
master_call = c->master_call;
switch (master_call->hold_state) {
case Q931_HOLD_STATE_HOLD_REQ:
if (ctrl->localtype == PRI_NETWORK) {
/* The network ignores HOLD request on a hold collision. */
break;
}
/* Fall through */
case Q931_HOLD_STATE_IDLE:
ctrl->ev.e = PRI_EVENT_HOLD;
ctrl->ev.hold.channel = q931_encode_channel(c);
ctrl->ev.hold.call = master_call;
ctrl->ev.hold.subcmds = &ctrl->subcmds;
res = Q931_RES_HAVEEVENT;
UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_HOLD_IND);
/* Stop any T-HOLD timer from possible hold collision. */
pri_schedule_del(ctrl, master_call->hold_timer);
master_call->hold_timer = 0;
break;
default:
q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE);
break;
}
break;
case Q931_CALL_STATE_DISCONNECT_INDICATION:
case Q931_CALL_STATE_RELEASE_REQUEST:
/* Ignore HOLD request in these states. */
break;
default:
q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE);
break;
}
return res;
case Q931_HOLD_ACKNOWLEDGE:
res = 0;
master_call = c->master_call;
switch (master_call->hold_state) {
case Q931_HOLD_STATE_HOLD_REQ:
ctrl->ev.e = PRI_EVENT_HOLD_ACK;
ctrl->ev.hold_ack.channel = q931_encode_channel(c);
ctrl->ev.hold_ack.call = master_call;
ctrl->ev.hold_ack.subcmds = &ctrl->subcmds;
res = Q931_RES_HAVEEVENT;
UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_CALL_HELD);
/* Call is now on hold so forget the channel. */
c->channelno = 0;/* No channel */
c->ds1no = 0;
c->ds1explicit = 0;
c->chanflags = 0;
/* Stop T-HOLD timer */
pri_schedule_del(ctrl, master_call->hold_timer);
master_call->hold_timer = 0;
break;
default:
/* Ignore response. Response is late or spurrious. */
break;
}
return res;
case Q931_HOLD_REJECT:
res = 0;
master_call = c->master_call;
switch (master_call->hold_state) {
case Q931_HOLD_STATE_HOLD_REQ:
if (missingmand) {
/* Still, let hold rejection continue. */
c->cause = PRI_CAUSE_MANDATORY_IE_MISSING;
}
ctrl->ev.e = PRI_EVENT_HOLD_REJ;
ctrl->ev.hold_rej.channel = q931_encode_channel(c);
ctrl->ev.hold_rej.call = master_call;
ctrl->ev.hold_rej.cause = c->cause;
ctrl->ev.hold_rej.subcmds = &ctrl->subcmds;
res = Q931_RES_HAVEEVENT;
UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_IDLE);
/* Stop T-HOLD timer */
pri_schedule_del(ctrl, master_call->hold_timer);
master_call->hold_timer = 0;
break;
default:
/* Ignore response. Response is late or spurrious. */
break;
}
return res;
case Q931_RETRIEVE:
res = 0;
switch (c->ourcallstate) {
case Q931_CALL_STATE_CALL_RECEIVED:
case Q931_CALL_STATE_CONNECT_REQUEST:
case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING:
if (q931_is_ptmp(ctrl)) {
/* RETRIEVE request only allowed in these states if point-to-point mode. */
q931_send_retrieve_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE);
break;
}
/* Fall through */
case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING:
case Q931_CALL_STATE_CALL_DELIVERED:
case Q931_CALL_STATE_ACTIVE:
if (!q931_find_winning_call(c)) {
/*
* Only the winning call of a broadcast SETUP can do hold since the
* call must be answered first.
*/
q931_send_retrieve_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE);
break;
}
master_call = c->master_call;
switch (master_call->hold_state) {
case Q931_HOLD_STATE_RETRIEVE_REQ:
if (ctrl->localtype == PRI_NETWORK) {
/* The network ignores RETRIEVE request on a retrieve collision. */
break;
}
/* Fall through */
case Q931_HOLD_STATE_CALL_HELD:
ctrl->ev.e = PRI_EVENT_RETRIEVE;
ctrl->ev.retrieve.channel = q931_encode_channel(c);
ctrl->ev.retrieve.call = master_call;
ctrl->ev.retrieve.flexible = !(c->chanflags & FLAG_EXCLUSIVE);
ctrl->ev.retrieve.subcmds = &ctrl->subcmds;
res = Q931_RES_HAVEEVENT;
UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_RETRIEVE_IND);
/* Stop any T-RETRIEVE timer from possible retrieve collision. */
pri_schedule_del(ctrl, master_call->hold_timer);
master_call->hold_timer = 0;
break;
default:
q931_send_retrieve_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE);
break;
}
break;
case Q931_CALL_STATE_DISCONNECT_INDICATION:
case Q931_CALL_STATE_RELEASE_REQUEST:
/* Ignore RETRIEVE request in these states. */
break;
default:
q931_send_retrieve_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE);
break;
}
return res;
case Q931_RETRIEVE_ACKNOWLEDGE:
res = 0;
master_call = c->master_call;
switch (master_call->hold_state) {
case Q931_HOLD_STATE_RETRIEVE_REQ:
UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_IDLE);
/* Stop T-RETRIEVE timer */
pri_schedule_del(ctrl, master_call->hold_timer);
master_call->hold_timer = 0;
ctrl->ev.e = PRI_EVENT_RETRIEVE_ACK;
ctrl->ev.retrieve_ack.channel = q931_encode_channel(c);
ctrl->ev.retrieve_ack.call = master_call;
ctrl->ev.retrieve_ack.subcmds = &ctrl->subcmds;
res = Q931_RES_HAVEEVENT;
break;
default:
/* Ignore response. Response is late or spurrious. */
break;
}
return res;
case Q931_RETRIEVE_REJECT:
res = 0;
master_call = c->master_call;
switch (master_call->hold_state) {
case Q931_HOLD_STATE_RETRIEVE_REQ:
UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_CALL_HELD);
/* Call is still on hold so forget the channel. */
c->channelno = 0;/* No channel */
c->ds1no = 0;
c->ds1explicit = 0;
c->chanflags = 0;
/* Stop T-RETRIEVE timer */
pri_schedule_del(ctrl, master_call->hold_timer);
master_call->hold_timer = 0;
if (missingmand) {
/* Still, let retrive rejection continue. */
c->cause = PRI_CAUSE_MANDATORY_IE_MISSING;
}
ctrl->ev.e = PRI_EVENT_RETRIEVE_REJ;
ctrl->ev.retrieve_rej.channel = q931_encode_channel(c);
ctrl->ev.retrieve_rej.call = master_call;
ctrl->ev.retrieve_rej.cause = c->cause;
ctrl->ev.retrieve_rej.subcmds = &ctrl->subcmds;
res = Q931_RES_HAVEEVENT;
break;
default:
/* Ignore response. Response is late or spurrious. */
break;
}
return res;
case Q931_USER_INFORMATION:
case Q931_SEGMENT:
case Q931_CONGESTION_CONTROL:
case Q931_RESUME:
case Q931_RESUME_ACKNOWLEDGE:
case Q931_RESUME_REJECT:
case Q931_SUSPEND:
case Q931_SUSPEND_ACKNOWLEDGE:
case Q931_SUSPEND_REJECT:
pri_error(ctrl, "!! Not yet handling post-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg);
/* Fall through */
default:
pri_error(ctrl, "!! Don't know how to post-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg);
q931_status(ctrl,c, PRI_CAUSE_MESSAGE_TYPE_NONEXIST);
if (c->newcall)
pri_destroycall(ctrl, c);
return -1;
}
return 0;
}
/* Clear a call, although we did not receive any hangup notification. */
static int pri_internal_clear(void *data)
{
struct q931_call *c = data;
struct pri *ctrl = c->pri;
int res;
pri_schedule_del(ctrl, c->retranstimer);
c->retranstimer = 0;
c->useruserinfo[0] = '\0';
//c->cause = -1;
c->causecode = -1;
c->causeloc = -1;
c->sugcallstate = Q931_CALL_STATE_NOT_SET;
c->aoc_units = -1;
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL);
c->peercallstate = Q931_CALL_STATE_NULL;
q931_clr_subcommands(ctrl);
ctrl->ev.hangup.subcmds = &ctrl->subcmds;
ctrl->ev.hangup.channel = q931_encode_channel(c);
ctrl->ev.hangup.cause = c->cause;
ctrl->ev.hangup.cref = c->cr;
ctrl->ev.hangup.call = c->master_call;
ctrl->ev.hangup.aoc_units = c->aoc_units;
ctrl->ev.hangup.call_held = NULL;
ctrl->ev.hangup.call_active = NULL;
libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo));
if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
pri_message(ctrl, "clearing, alive %d, hangupack %d\n", c->alive, c->sendhangupack);
}
/* Free resources */
if (c->alive) {
ctrl->ev.e = PRI_EVENT_HANGUP;
res = Q931_RES_HAVEEVENT;
c->alive = 0;
} else if (c->sendhangupack) {
res = Q931_RES_HAVEEVENT;
ctrl->ev.e = PRI_EVENT_HANGUP_ACK;
pri_hangup(ctrl, c, c->cause);
} else {
res = 0;
pri_hangup(ctrl, c, c->cause);
}
return res;
}
/* Handle T309 timeout for an active call. */
static void pri_dl_down_timeout(void *data)
{
struct q931_call *c = data;
struct pri *ctrl = c->pri;
if (ctrl->debug & PRI_DEBUG_Q931_STATE)
pri_message(ctrl, DBGHEAD "Timed out waiting for data link re-establishment\n", DBGINFO);
c->cause = PRI_CAUSE_DESTINATION_OUT_OF_ORDER;
if (pri_internal_clear(c) == Q931_RES_HAVEEVENT)
ctrl->schedev = 1;
}
/* Handle Layer 2 down event for a non active call. */
static void pri_dl_down_cancelcall(void *data)
{
struct q931_call *c = data;
struct pri *ctrl = c->pri;
if (ctrl->debug & PRI_DEBUG_Q931_STATE)
pri_message(ctrl, DBGHEAD "Cancel non active call after data link failure\n", DBGINFO);
c->cause = PRI_CAUSE_DESTINATION_OUT_OF_ORDER;
if (pri_internal_clear(c) == Q931_RES_HAVEEVENT)
ctrl->schedev = 1;
}
/* Receive an indication from Layer 2 */
void q931_dl_indication(struct pri *ctrl, int event)
{
q931_call *cur = NULL;
/* Just return if T309 is not enabled. */
if (!ctrl || ctrl->timers[PRI_TIMER_T309] < 0)
return;
switch (event) {
case PRI_EVENT_DCHAN_DOWN:
pri_message(ctrl, DBGHEAD "link is DOWN\n", DBGINFO);
cur = *ctrl->callpool;
while(cur) {
if (cur->ourcallstate == Q931_CALL_STATE_ACTIVE) {
/* For a call in Active state, activate T309 only if there is no timer already running. */
if (!cur->retranstimer) {
pri_message(ctrl, DBGHEAD "activate T309 for call %d on channel %d\n", DBGINFO, cur->cr, cur->channelno);
cur->retranstimer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T309], pri_dl_down_timeout, cur);
}
} else if (cur->ourcallstate != Q931_CALL_STATE_NULL) {
/* For a call that is not in Active state, schedule internal clearing of the call 'ASAP' (delay 0). */
pri_message(ctrl, DBGHEAD "cancel call %d on channel %d in state %d (%s)\n", DBGINFO,
cur->cr, cur->channelno, cur->ourcallstate,
q931_call_state_str(cur->ourcallstate));
pri_schedule_del(ctrl, cur->retranstimer);
cur->retranstimer = pri_schedule_event(ctrl, 0, pri_dl_down_cancelcall, cur);
}
cur = cur->next;
}
break;
case PRI_EVENT_DCHAN_UP:
pri_message(ctrl, DBGHEAD "link is UP\n", DBGINFO);
cur = *ctrl->callpool;
while(cur) {
if (cur->ourcallstate == Q931_CALL_STATE_ACTIVE && cur->retranstimer) {
pri_message(ctrl, DBGHEAD "cancel T309 for call %d on channel %d\n", DBGINFO, cur->cr, cur->channelno);
pri_schedule_del(ctrl, cur->retranstimer);
cur->retranstimer = 0;
q931_status(ctrl, cur, PRI_CAUSE_NORMAL_UNSPECIFIED);
} else if (cur->ourcallstate != Q931_CALL_STATE_NULL &&
cur->ourcallstate != Q931_CALL_STATE_DISCONNECT_REQUEST &&
cur->ourcallstate != Q931_CALL_STATE_DISCONNECT_INDICATION &&
cur->ourcallstate != Q931_CALL_STATE_RELEASE_REQUEST) {
/* The STATUS message sent here is not required by Q.931, but it may help anyway. */
q931_status(ctrl, cur, PRI_CAUSE_NORMAL_UNSPECIFIED);
}
cur = cur->next;
}
break;
default:
pri_message(ctrl, DBGHEAD "unexpected event %d.\n", DBGINFO, event);
}
}
int q931_call_getcrv(struct pri *ctrl, q931_call *call, int *callmode)
{
if (callmode)
*callmode = call->cr & 0x7;
return ((call->cr & 0x7fff) >> 3);
}
int q931_call_setcrv(struct pri *ctrl, q931_call *call, int crv, int callmode)
{
call->cr = (crv << 3) & 0x7fff;
call->cr |= (callmode & 0x7);
return 0;
}