From e4b8bed7e966a0833b7b5e3b8ddda3da79523741 Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Fri, 28 May 2010 18:43:57 +0000 Subject: [PATCH] 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 --- libpri.h | 34 +++++++++++++++- pri.c | 17 ++++++++ pri_internal.h | 1 + pri_q931.h | 1 + q931.c | 103 ++++++++++++++++++++++++++++++++++++++----------- 5 files changed, 132 insertions(+), 24 deletions(-) diff --git a/libpri.h b/libpri.h index 0e2282f..e5d3ac9 100644 --- a/libpri.h +++ b/libpri.h @@ -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(). diff --git a/pri.c b/pri.c index f4e6333..9c9a276 100644 --- a/pri.c +++ b/pri.c @@ -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. * diff --git a/pri_internal.h b/pri_internal.h index 58e9a34..8f410f0 100644 --- a/pri_internal.h +++ b/pri_internal.h @@ -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; diff --git a/pri_q931.h b/pri_q931.h index 5bfef65..9e6f68b 100644 --- a/pri_q931.h +++ b/pri_q931.h @@ -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); diff --git a/q931.c b/q931.c index 2e9c642..6b138de 100644 --- a/q931.c +++ b/q931.c @@ -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) {