ETSI Call Waiting support.

Add the ability to announce a call to an endpoint when there are no B
channels available.  A call waiting call is a SETUP message with no B
channel selected.

Relevant specification: EN 300 056, EN 300 057, EN 300 058

Review:	https://reviewboard.asterisk.org/r/569/


git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@1746 2fbb986a-6c06-0410-b554-c9c1f0a7f128
This commit is contained in:
Richard Mudgett 2010-05-28 18:43:57 +00:00
parent 9bb285fdc1
commit e4b8bed7e9
5 changed files with 132 additions and 24 deletions

View File

@ -98,6 +98,7 @@
#define PRI_EVENT_RETRIEVE 24 /* RETRIEVE request received */
#define PRI_EVENT_RETRIEVE_ACK 25 /* RETRIEVE_ACKNOWLEDGE received */
#define PRI_EVENT_RETRIEVE_REJ 26 /* RETRIEVE_REJECT received */
#define PRI_EVENT_CONNECT_ACK 27 /* CONNECT_ACKNOWLEDGE received */
/* Simple states */
#define PRI_STATE_DOWN 0
@ -1160,6 +1161,13 @@ struct pri_event_retrieve_rej {
struct pri_subcommands *subcmds;
};
struct pri_event_connect_ack {
int e;
int channel;
q931_call *call;
struct pri_subcommands *subcmds;
};
typedef union {
int e;
pri_event_generic gen; /* Generic view */
@ -1184,6 +1192,7 @@ typedef union {
struct pri_event_retrieve retrieve;
struct pri_event_retrieve_ack retrieve_ack;
struct pri_event_retrieve_rej retrieve_rej;
struct pri_event_connect_ack connect_ack;
} pri_event;
struct pri;
@ -1284,10 +1293,33 @@ int pri_keypad_facility(struct pri *pri, q931_call *call, const char *digits);
Set non-isdn to non-zero if you are not connecting to ISDN equipment */
int pri_need_more_info(struct pri *pri, q931_call *call, int channel, int nonisdn);
/* Answer the call on the given channel (ignored if you called acknowledge already).
/* Answer(CONNECT) the call on the given channel.
Set non-isdn to non-zero if you are not connecting to ISDN equipment */
int pri_answer(struct pri *pri, q931_call *call, int channel, int nonisdn);
/*!
* \brief Send the manual CONNECT_ACKNOWLEDGE message.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param channel Selected channel to assign to the call waiting call.
* Zero if do not include the channel id ie in the CONNECT_ACKNOWLEDGE message.
*
* \retval 0 on success.
* \retval -1 on error.
*/
int pri_connect_ack(struct pri *ctrl, q931_call *call, int channel);
/*!
* \brief Set the manual CONNECT_ACKNOWLEDGE message enable flag.
*
* \param ctrl D channel controller.
* \param enable TRUE to enable manual CONNECT_ACKNOWLEDGE message feature.
*
* \return Nothing
*/
void pri_connect_ack_enable(struct pri *ctrl, int enable);
/*!
* \brief Give connected line information to a call
* \note Could be used instead of pri_sr_set_caller_party() before calling pri_setup().

17
pri.c
View File

@ -507,6 +507,7 @@ char *pri_event2str(int id)
{ PRI_EVENT_RETRIEVE, "Retrieve" },
{ PRI_EVENT_RETRIEVE_ACK, "Retrieve ACK" },
{ PRI_EVENT_RETRIEVE_REJ, "Retrieve Rej" },
{ PRI_EVENT_CONNECT_ACK, "Connect ACK" },
/* *INDENT-ON* */
};
@ -693,6 +694,22 @@ int pri_answer(struct pri *pri, q931_call *call, int channel, int nonisdn)
return q931_connect(pri, call, channel, nonisdn);
}
int pri_connect_ack(struct pri *ctrl, q931_call *call, int channel)
{
if (!ctrl || !call) {
return -1;
}
return q931_connect_acknowledge(ctrl, call, channel);
}
void pri_connect_ack_enable(struct pri *ctrl, int enable)
{
if (ctrl) {
ctrl = PRI_MASTER(ctrl);
ctrl->manual_connect_ack = enable ? 1 : 0;
}
}
/*!
* \brief Copy the PRI party name to the Q.931 party name structure.
*

View File

@ -108,6 +108,7 @@ struct pri {
unsigned int cc_support:1;/* TRUE if upper layer supports call completion. */
unsigned int transfer_support:1;/* TRUE if the upper layer supports ECT */
unsigned int aoc_support:1;/* TRUE if can send AOC events to the upper layer. */
unsigned int manual_connect_ack:1;/* TRUE if the CONNECT_ACKNOWLEDGE is sent with API call */
/* MDL variables */
int mdl_error;

View File

@ -475,6 +475,7 @@ extern int q931_information(struct pri *pri, q931_call *call, char digit);
extern int q931_keypad_facility(struct pri *pri, q931_call *call, const char *digits);
extern int q931_connect(struct pri *pri, q931_call *call, int channel, int nonisdn);
int q931_connect_acknowledge(struct pri *ctrl, q931_call *call, int channel);
extern int q931_release(struct pri *pri, q931_call *call, int cause);

103
q931.c
View File

@ -1063,6 +1063,10 @@ static int transmit_channel_id(int full_ie, struct pri *ctrl, q931_call *call, i
}
if (call->chanflags & FLAG_EXCLUSIVE) {
/* Channel is exclusive */
if (!(ie->data[pos] & 0x03)) {
/* An exclusive no channel id ie is to be discarded. */
return 0;
}
ie->data[pos] |= 0x08;
} else if (!call->chanflags) {
/* Don't need this IE */
@ -5179,20 +5183,6 @@ static int q931_release_complete(struct pri *ctrl, q931_call *c, int 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;
}
/*!
* \brief Find the winning subcall if it exists or current call if not outboundbroadcast.
*
@ -5218,6 +5208,49 @@ struct q931_call *q931_find_winning_call(struct q931_call *call)
return call;
}
static int connect_ack_ies[] = { -1 };
static int connect_ack_w_chan_id_ies[] = { Q931_CHANNEL_IDENT, -1 };
static int gr303_connect_ack_ies[] = { Q931_CHANNEL_IDENT, -1 };
int q931_connect_acknowledge(struct pri *ctrl, q931_call *call, int channel)
{
int *use_ies;
struct q931_call *winner;
winner = q931_find_winning_call(call);
if (!winner) {
return -1;
}
if (winner != call) {
UPDATE_OURCALLSTATE(ctrl, call, Q931_CALL_STATE_ACTIVE);
call->peercallstate = Q931_CALL_STATE_ACTIVE;
}
UPDATE_OURCALLSTATE(ctrl, winner, Q931_CALL_STATE_ACTIVE);
winner->peercallstate = Q931_CALL_STATE_ACTIVE;
if (channel) {
winner->ds1no = (channel & 0xff00) >> 8;
winner->ds1explicit = (channel & 0x10000) >> 16;
winner->channelno = channel & 0xff;
winner->chanflags &= ~FLAG_PREFERRED;
winner->chanflags |= FLAG_EXCLUSIVE;
}
use_ies = NULL;
if (ctrl->subchannel && !ctrl->bri) {
if (ctrl->localtype == PRI_CPE) {
use_ies = gr303_connect_ack_ies;
}
} else if (channel) {
use_ies = connect_ack_w_chan_id_ies;
} else {
use_ies = connect_ack_ies;
}
if (use_ies) {
return send_message(ctrl, winner, Q931_CONNECT_ACKNOWLEDGE, use_ies);
}
return 0;
}
/*!
* \internal
* \brief Send HOLD message response wait timeout.
@ -5693,6 +5726,8 @@ static int __q931_hangup(struct pri *ctrl, q931_call *c, int cause)
release_compl = 1;
break;
}
/* Fall through */
case PRI_CAUSE_INCOMPATIBLE_DESTINATION:
/* See Q.931 Section 5.3.2 a) */
switch (c->ourcallstate) {
case Q931_CALL_STATE_NULL:
@ -5705,6 +5740,13 @@ static int __q931_hangup(struct pri *ctrl, q931_call *c, int cause)
disconnect = 0;
release_compl = 1;
break;
case Q931_CALL_STATE_CONNECT_REQUEST:
/*
* Send RELEASE because the B channel negotiation failed
* for call waiting.
*/
disconnect = 0;
break;
default:
/*
* Send DISCONNECT because some other message
@ -7291,8 +7333,6 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct
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;
@ -7304,7 +7344,12 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct
libpri_copy_string(ctrl->ev.answer.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.answer.useruserinfo));
c->useruserinfo[0] = '\0';
q931_connect_acknowledge(ctrl, c);
if (!PRI_MASTER(ctrl)->manual_connect_ack) {
q931_connect_acknowledge(ctrl, c, 0);
} else {
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CONNECT_REQUEST);
c->peercallstate = Q931_CALL_STATE_CONNECT_REQUEST;
}
if (c->cis_auto_disconnect && c->cis_call) {
/* Make sure WE release when we initiate a signalling only connection */
@ -7390,14 +7435,26 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct
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);
switch (c->ourcallstate) {
default:
if (ctrl->localtype == PRI_NETWORK || ctrl->switchtype == PRI_SWITCH_QSIG) {
q931_status(ctrl, c, PRI_CAUSE_WRONG_MESSAGE);
break;
}
/* Fall through */
case Q931_CALL_STATE_CONNECT_REQUEST:
case Q931_CALL_STATE_ACTIVE:
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_ACTIVE);
c->peercallstate = Q931_CALL_STATE_ACTIVE;
if (PRI_MASTER(ctrl)->manual_connect_ack) {
ctrl->ev.e = PRI_EVENT_CONNECT_ACK;
ctrl->ev.connect_ack.subcmds = &ctrl->subcmds;
ctrl->ev.connect_ack.channel = q931_encode_channel(c);
ctrl->ev.connect_ack.call = c->master_call;
return Q931_RES_HAVEEVENT;
}
break;
}
UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_ACTIVE);
c->peercallstate = Q931_CALL_STATE_ACTIVE;
break;
case Q931_STATUS:
if (missingmand) {