1172 lines
27 KiB
C
1172 lines
27 KiB
C
/*
|
|
* Copyright (c) 1996-2003
|
|
* Fraunhofer Institute for Open Communication Systems (FhG Fokus).
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* Author: Hartmut Brandt <harti@freebsd.org>
|
|
*
|
|
* $Begemot: libunimsg/atm/sig/sig_coord.c,v 1.6 2003/09/24 10:27:50 hbb Exp $
|
|
*
|
|
* Coordinator
|
|
*/
|
|
|
|
#include <netnatm/unimsg.h>
|
|
#include <netnatm/saal/sscfudef.h>
|
|
#include <netnatm/msg/unistruct.h>
|
|
#include <netnatm/msg/unimsglib.h>
|
|
#include <netnatm/sig/uni.h>
|
|
|
|
#include <netnatm/sig/unipriv.h>
|
|
#include <netnatm/sig/unimkmsg.h>
|
|
|
|
#define STR(S) [S] #S
|
|
static const char *const cunames[] = {
|
|
STR(CU_STAT0),
|
|
STR(CU_STAT1),
|
|
STR(CU_STAT2),
|
|
STR(CU_STAT3),
|
|
};
|
|
|
|
#define DEF_PRIV_SIG(NAME, FROM) [SIG##NAME] "SIG"#NAME,
|
|
static const char *const coord_sigs[] = {
|
|
DEF_COORD_SIGS
|
|
};
|
|
#undef DEF_PRIV_SIG
|
|
|
|
static void sig_all_calls(struct uni *, u_int sig);
|
|
static void set_custat(struct uni *, enum cu_stat);
|
|
|
|
static void input_dummy(struct uni *uni, struct uni_msg *m, struct uni_all *u);
|
|
static void input_global(struct uni *uni, struct uni_msg *m, struct uni_all *u);
|
|
static void input_unknown(struct uni *uni, struct uni_msg *m, struct uni_all *u);
|
|
static void input_cobi(struct call *c, struct uni_msg *m, struct uni_all *u);
|
|
static void input_call(struct call *c, struct uni_msg *m, struct uni_all *u);
|
|
|
|
TIMER_FUNC_UNI(t309, t309_func)
|
|
|
|
/*
|
|
* All those 'bogus signal' printouts are not specified in the SDLs.
|
|
*/
|
|
|
|
|
|
/*
|
|
* SAAL-ESTABLISH.indication
|
|
*
|
|
* This means either a resynchronisation or error-recovery or
|
|
* an incoming SSCOP connection.
|
|
*/
|
|
static void
|
|
coord_saal_establish_indication(struct uni *uni)
|
|
{
|
|
switch (uni->custat) {
|
|
|
|
case CU_STAT0: /* Q.2931:Coord-U 4/10 */
|
|
case CU_STAT3: /* Q.2931:Coord-U 5/10 */
|
|
sig_all_calls(uni, SIGC_LINK_ESTABLISH_indication);
|
|
set_custat(uni, CU_STAT3);
|
|
break;
|
|
|
|
case CU_STAT1:
|
|
case CU_STAT2:
|
|
VERBOSE0(uni, UNI_FAC_COORD,
|
|
"signal saal_establish.indication in CU%u", uni->custat);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0, ("CU_STAT*"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* SAAL-ESTABLISH.confirm
|
|
*/
|
|
static void
|
|
coord_saal_establish_confirm(struct uni *uni)
|
|
{
|
|
switch (uni->custat) {
|
|
|
|
case CU_STAT0:
|
|
case CU_STAT2:
|
|
VERBOSE0(uni, UNI_FAC_COORD,
|
|
"signal saal_establish.confirm in CU%u", uni->custat);
|
|
break;
|
|
|
|
case CU_STAT1:
|
|
/*
|
|
* Q.2931:Co-ord-U 4/10
|
|
*/
|
|
TIMER_STOP_UNI(uni, t309);
|
|
sig_all_calls(uni, SIGC_LINK_ESTABLISH_confirm);
|
|
uni->funcs->uni_output(uni, uni->arg,
|
|
UNIAPI_LINK_ESTABLISH_confirm, 0, NULL);
|
|
set_custat(uni, CU_STAT3);
|
|
break;
|
|
|
|
case CU_STAT3:
|
|
/*
|
|
* Q.2931:Coord-U 5/10
|
|
*/
|
|
sig_all_calls(uni, SIGC_LINK_ESTABLISH_confirm);
|
|
uni->funcs->uni_output(uni, uni->arg,
|
|
UNIAPI_LINK_ESTABLISH_confirm, 0, NULL);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0, ("CU_STAT*"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* SAAL-RELEASE.confirm
|
|
*/
|
|
static void
|
|
coord_saal_release_confirm(struct uni *uni)
|
|
{
|
|
switch (uni->custat) {
|
|
|
|
case CU_STAT0:
|
|
case CU_STAT1:
|
|
case CU_STAT3:
|
|
VERBOSE0(uni, UNI_FAC_COORD,
|
|
"signal saal_release.confirm in CU%u", uni->custat);
|
|
break;
|
|
|
|
case CU_STAT2:
|
|
/*
|
|
* Q.2931:Coord-U 5/10
|
|
*/
|
|
uni->funcs->uni_output(uni, uni->arg,
|
|
UNIAPI_LINK_RELEASE_confirm, 0, NULL);
|
|
set_custat(uni, CU_STAT0);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0, ("CU_STAT*"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* SAAL failure.
|
|
*/
|
|
static void
|
|
coord_saal_release_indication(struct uni *uni)
|
|
{
|
|
switch (uni->custat) {
|
|
|
|
case CU_STAT0:
|
|
case CU_STAT2:
|
|
VERBOSE0(uni, UNI_FAC_COORD,
|
|
"signal saal_release.indication in CU%u", uni->custat);
|
|
break;
|
|
|
|
case CU_STAT1:
|
|
case CU_STAT3:
|
|
/*
|
|
* Q.2931:Coord-U 4/10
|
|
* Q.2931:Coord-U 5/10
|
|
*/
|
|
sig_all_calls(uni, SIGC_LINK_RELEASE_indication);
|
|
set_custat(uni, CU_STAT0);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0, ("CU_STAT*"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Link-establish.request from USER. This can also come from
|
|
* a call instance. In this case 'cookie' is zero.
|
|
*/
|
|
static void
|
|
coord_link_establish_request(struct uni *uni, u_int32_t cookie)
|
|
{
|
|
switch (uni->custat) {
|
|
|
|
case CU_STAT0:
|
|
/*
|
|
* Q.2931:Coord-U 4/10
|
|
*/
|
|
uni->funcs->saal_output(uni, uni->arg,
|
|
SAAL_ESTABLISH_request, NULL);
|
|
if (!TIMER_ISACT(uni, t309))
|
|
TIMER_START_UNI(uni, t309, uni->timer309);
|
|
set_custat(uni, CU_STAT1);
|
|
if (cookie)
|
|
uniapi_uni_error(uni, UNIAPI_OK, cookie, 0);
|
|
break;
|
|
|
|
case CU_STAT1:
|
|
/*
|
|
* Q.2931:Coord-U 4/10
|
|
* This is probably missing from the delay field.
|
|
*/
|
|
uni_delenq_coord(uni, SIGO_LINK_ESTABLISH_request,
|
|
cookie, NULL);
|
|
break;
|
|
|
|
case CU_STAT2:
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALLSTATE, cookie, 0);
|
|
if (cookie == 0)
|
|
VERBOSE0(uni, UNI_FAC_COORD,
|
|
"signal link-establish.request in CU%u",
|
|
uni->custat);
|
|
break;
|
|
|
|
case CU_STAT3:
|
|
/*
|
|
* Q.2931:Coord-U 5/10
|
|
*/
|
|
uni->funcs->uni_output(uni, uni->arg,
|
|
UNIAPI_LINK_ESTABLISH_confirm, 0, NULL);
|
|
uniapi_uni_error(uni, UNIAPI_OK, cookie, 0);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0, ("CU_STAT*"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Link-release.request from user
|
|
*/
|
|
static void
|
|
coord_link_release_request(struct uni *uni, u_int cookie)
|
|
{
|
|
switch (uni->custat) {
|
|
|
|
case CU_STAT0:
|
|
case CU_STAT1:
|
|
case CU_STAT2:
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALLSTATE, cookie, 0);
|
|
break;
|
|
|
|
case CU_STAT3:
|
|
/*
|
|
* Q.2931:Coord-U 5/10
|
|
*/
|
|
uni->funcs->saal_output(uni, uni->arg,
|
|
SAAL_RELEASE_request, NULL);
|
|
set_custat(uni, CU_STAT2);
|
|
uniapi_uni_error(uni, UNIAPI_OK, cookie, 0);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0, ("CU_STAT*"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* T309 timeout signal
|
|
*/
|
|
static void
|
|
coord_t309(struct uni *uni)
|
|
{
|
|
switch (uni->custat) {
|
|
|
|
case CU_STAT0:
|
|
case CU_STAT1:
|
|
/*
|
|
* Q.2931:Coord-U 4/10
|
|
*/
|
|
sig_all_calls(uni, SIGC_LINK_ESTABLISH_ERROR_indication);
|
|
set_custat(uni, CU_STAT0);
|
|
/* this is not in the SDLs, but how will the call control
|
|
* know, that starting the LINK has failed otherwise? */
|
|
uni->funcs->uni_output(uni, uni->arg,
|
|
UNIAPI_LINK_RELEASE_confirm, 0, NULL);
|
|
break;
|
|
|
|
case CU_STAT2:
|
|
case CU_STAT3:
|
|
VERBOSE0(uni, UNI_FAC_COORD,
|
|
"signal T309 in CU%u", uni->custat);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0, ("CU_STAT*"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Message from SAAL
|
|
*/
|
|
static void
|
|
coord_saal_data_indication(struct uni *uni, struct uni_msg *m)
|
|
{
|
|
struct uni_all *u;
|
|
struct call *c;
|
|
|
|
memset(&uni->cause, 0, sizeof(uni->cause));
|
|
if ((u = UNI_ALLOC()) == NULL) {
|
|
uni_msg_destroy(m);
|
|
return;
|
|
}
|
|
if (uni_decode_head(m, u, &uni->cx)) {
|
|
VERBOSE(uni, UNI_FAC_COORD, 2, "bogus message - ignored");
|
|
uni_msg_destroy(m);
|
|
UNI_FREE(u);
|
|
return;
|
|
}
|
|
if (u->u.hdr.cref.cref == CREF_DUMMY) {
|
|
if (uni->cx.q2932) {
|
|
input_dummy(uni, m, u);
|
|
} else {
|
|
VERBOSE(uni, UNI_FAC_COORD, 2, "dummy cref - ignored");
|
|
UNI_FREE(u);
|
|
uni_msg_destroy(m);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (u->u.hdr.cref.cref == CREF_GLOBAL)
|
|
input_global(uni, m, u);
|
|
else if ((c = uni_find_call(uni, &u->u.hdr.cref)) == NULL)
|
|
input_unknown(uni, m, u);
|
|
else if (c->type == CALL_COBI)
|
|
input_cobi(c, m, u);
|
|
else
|
|
input_call(c, m, u);
|
|
}
|
|
|
|
/*
|
|
* Message with global call reference
|
|
*
|
|
* Q.2931:Coord-U (X) 7/10
|
|
*/
|
|
static void
|
|
input_global(struct uni *uni, struct uni_msg *m, struct uni_all *u)
|
|
{
|
|
VERBOSE(uni, UNI_FAC_COORD, 2, "GLOB MTYPE = %x", u->mtype);
|
|
|
|
switch (u->mtype) {
|
|
|
|
default:
|
|
/*
|
|
* Q.2931:Coord-U 7/10
|
|
* Q.2931: 5.6.3.2e
|
|
* Amd4: 29e
|
|
*/
|
|
uni_respond_status(uni, &u->u.hdr.cref,
|
|
u->u.hdr.cref.flag ? uni->glob_start : uni->glob_respond,
|
|
UNI_CAUSE_CREF_INV);
|
|
break;
|
|
|
|
case UNI_RESTART:
|
|
if (u->u.hdr.cref.flag) {
|
|
/*
|
|
* Q.2931:Coord-U 7/10 (5.6.3.2h)
|
|
*/
|
|
uni_respond_status(uni, &u->u.hdr.cref,
|
|
uni->glob_start, UNI_CAUSE_CREF_INV);
|
|
break;
|
|
}
|
|
uni_enq_resp(uni, SIGR_RESTART, 0, m, u);
|
|
return;
|
|
|
|
case UNI_RESTART_ACK:
|
|
if (!u->u.hdr.cref.flag) {
|
|
/*
|
|
* Q.2931:Coord-U 7/10 (5.6.3.2h)
|
|
* Note, that the SDL diagram contains an error.
|
|
* The error with the 'YES' label should go to the
|
|
* box below 'OTHER'.
|
|
*/
|
|
uni_respond_status(uni, &u->u.hdr.cref,
|
|
uni->glob_respond, UNI_CAUSE_CREF_INV);
|
|
break;
|
|
}
|
|
uni_enq_start(uni, SIGS_RESTART_ACK, 0, m, u);
|
|
return;
|
|
|
|
case UNI_STATUS:
|
|
if (u->u.hdr.cref.flag)
|
|
uni_enq_start(uni, SIGS_STATUS, 0, m, u);
|
|
else
|
|
uni_enq_resp(uni, SIGR_STATUS, 0, m, u);
|
|
return;
|
|
}
|
|
uni_msg_destroy(m);
|
|
UNI_FREE(u);
|
|
}
|
|
|
|
/*
|
|
* Q.2931:Coord-U 8/10
|
|
*
|
|
* Message for an unknown call reference
|
|
*/
|
|
static void
|
|
input_unknown(struct uni *uni, struct uni_msg *m, struct uni_all *u)
|
|
{
|
|
struct uni_all *resp;
|
|
struct call *c;
|
|
u_int cause = UNI_CAUSE_CREF_INV;
|
|
|
|
VERBOSE(uni, UNI_FAC_COORD, 2, "UNKNOWN MTYPE = %x", u->mtype);
|
|
|
|
switch (u->mtype) {
|
|
|
|
default:
|
|
/*
|
|
* This message type is entirly unknown
|
|
*
|
|
* 5.6.4 and 5.7.1 are only when the call is not in the
|
|
* NULL state. This means, 5.6.3.2a takes over.
|
|
*/
|
|
break;
|
|
|
|
case UNI_SETUP:
|
|
if (u->u.hdr.cref.flag)
|
|
/*
|
|
* 5.6.3.2c
|
|
*/
|
|
goto drop;
|
|
if ((c = uni_create_call(uni, u->u.hdr.cref.cref, 0, 0)) != NULL) {
|
|
uni_enq_call(c, SIGC_SETUP, 0, m, u);
|
|
return;
|
|
}
|
|
goto drop;
|
|
|
|
case UNI_RELEASE_COMPL:
|
|
/*
|
|
* 5.6.3.2c
|
|
*/
|
|
goto drop;
|
|
|
|
case UNI_STATUS:
|
|
/*
|
|
* 5.6.12
|
|
*
|
|
* The SDLs don't use the verify procedure and don't
|
|
* handle the case of an invalid callstate - we
|
|
* ignore the message, if the callstate is not good.
|
|
*/
|
|
(void)uni_decode_body(m, u, &uni->cx);
|
|
if (!IE_ISGOOD(u->u.status.callstate))
|
|
goto drop;
|
|
if (u->u.status.callstate.state == UNI_CALLSTATE_U0)
|
|
goto drop;
|
|
cause = UNI_CAUSE_MSG_INCOMP;
|
|
break;
|
|
|
|
case UNI_STATUS_ENQ:
|
|
if ((resp = UNI_ALLOC()) == NULL)
|
|
goto drop;
|
|
|
|
(void)uni_decode_body(m, u, &uni->cx);
|
|
MK_MSG_RESP(resp, UNI_STATUS, &u->u.hdr.cref);
|
|
MK_IE_CALLSTATE(resp->u.status.callstate, UNI_CALLSTATE_U0);
|
|
MK_IE_CAUSE(resp->u.status.cause, UNI_CAUSE_LOC_USER,
|
|
UNI_CAUSE_STATUS);
|
|
|
|
if (IE_ISGOOD(u->u.status_enq.epref)) {
|
|
/* reflect epref as required by L3MU_PO */
|
|
resp->u.status.epref = u->u.status_enq.epref;
|
|
MK_IE_EPREF(resp->u.status.epref,
|
|
u->u.status_enq.epref.epref,
|
|
!u->u.status_enq.epref.flag);
|
|
MK_IE_EPSTATE(resp->u.status.epstate, UNI_EPSTATE_NULL);
|
|
}
|
|
|
|
(void)uni_send_output(resp, uni);
|
|
|
|
UNI_FREE(resp);
|
|
goto drop;
|
|
|
|
case UNI_COBISETUP:
|
|
if (u->u.hdr.cref.flag)
|
|
/*
|
|
* 5.6.3.2c (probably)
|
|
*/
|
|
goto drop;
|
|
if ((c = uni_create_call(uni, u->u.hdr.cref.cref, 0, 0)) != NULL) {
|
|
uni_enq_call(c, SIGC_COBISETUP, 0, m, u);
|
|
return;
|
|
}
|
|
goto drop;
|
|
}
|
|
|
|
/*
|
|
* 5.6.3.2a)
|
|
*
|
|
* Respond with a RELEASE COMPLETE
|
|
*/
|
|
if ((resp = UNI_ALLOC()) == NULL)
|
|
goto drop;
|
|
|
|
MK_MSG_RESP(resp, UNI_RELEASE_COMPL, &u->u.hdr.cref);
|
|
MK_IE_CAUSE(resp->u.release_compl.cause[0], UNI_CAUSE_LOC_USER, cause);
|
|
if (uni_diag(cause, UNI_CODING_ITU) == UNI_DIAG_MTYPE)
|
|
ADD_CAUSE_MTYPE(resp->u.release_compl.cause[0], u->mtype);
|
|
|
|
(void)uni_send_output(resp, uni);
|
|
|
|
UNI_FREE(resp);
|
|
|
|
drop:
|
|
UNI_FREE(u);
|
|
uni_msg_destroy(m);
|
|
}
|
|
|
|
static void
|
|
input_cobi(struct call *c __unused, struct uni_msg *m, struct uni_all *u)
|
|
{
|
|
/* XXX */
|
|
UNI_FREE(u);
|
|
uni_msg_destroy(m);
|
|
}
|
|
|
|
static void
|
|
input_dummy(struct uni *uni __unused, struct uni_msg *m, struct uni_all *u)
|
|
{
|
|
/* XXX */
|
|
UNI_FREE(u);
|
|
uni_msg_destroy(m);
|
|
}
|
|
|
|
static void
|
|
input_call(struct call *c, struct uni_msg *m, struct uni_all *u)
|
|
{
|
|
VERBOSE(c->uni, UNI_FAC_COORD, 2, "CALL MTYPE = %x %d/%s",
|
|
u->mtype, c->cref, c->mine ? "mine":"his");
|
|
|
|
switch (u->mtype) {
|
|
|
|
case UNI_SETUP:
|
|
/*
|
|
* Ignored
|
|
*/
|
|
break;
|
|
|
|
case UNI_CALL_PROC:
|
|
uni_enq_call(c, SIGC_CALL_PROC, 0, m, u);
|
|
return;
|
|
|
|
case UNI_ALERTING:
|
|
uni_enq_call(c, SIGC_ALERTING, 0, m, u);
|
|
return;
|
|
|
|
case UNI_RELEASE:
|
|
uni_enq_call(c, SIGC_RELEASE, 0, m, u);
|
|
return;
|
|
|
|
case UNI_RELEASE_COMPL:
|
|
uni_enq_call(c, SIGC_RELEASE_COMPL, 0, m, u);
|
|
return;
|
|
|
|
case UNI_CONNECT:
|
|
uni_enq_call(c, SIGC_CONNECT, 0, m, u);
|
|
return;
|
|
|
|
case UNI_CONNECT_ACK:
|
|
uni_enq_call(c, SIGC_CONNECT_ACK, 0, m, u);
|
|
return;
|
|
|
|
case UNI_NOTIFY:
|
|
uni_enq_call(c, SIGC_NOTIFY, 0, m, u);
|
|
return;
|
|
|
|
case UNI_STATUS:
|
|
uni_enq_call(c, SIGC_STATUS, 0, m, u);
|
|
return;
|
|
|
|
case UNI_STATUS_ENQ:
|
|
uni_enq_call(c, SIGC_STATUS_ENQ, 0, m, u);
|
|
return;
|
|
|
|
case UNI_ADD_PARTY:
|
|
uni_enq_call(c, SIGC_ADD_PARTY, 0, m, u);
|
|
return;
|
|
|
|
case UNI_PARTY_ALERTING:
|
|
uni_enq_call(c, SIGC_PARTY_ALERTING, 0, m, u);
|
|
return;
|
|
|
|
case UNI_ADD_PARTY_ACK:
|
|
uni_enq_call(c, SIGC_ADD_PARTY_ACK, 0, m, u);
|
|
return;
|
|
|
|
case UNI_ADD_PARTY_REJ:
|
|
uni_enq_call(c, SIGC_ADD_PARTY_REJ, 0, m, u);
|
|
return;
|
|
|
|
case UNI_DROP_PARTY:
|
|
uni_enq_call(c, SIGC_DROP_PARTY, 0, m, u);
|
|
return;
|
|
|
|
case UNI_DROP_PARTY_ACK:
|
|
uni_enq_call(c, SIGC_DROP_PARTY_ACK, 0, m, u);
|
|
return;
|
|
|
|
default:
|
|
uni_enq_call(c, SIGC_UNKNOWN, 0, m, u);
|
|
return;
|
|
}
|
|
UNI_FREE(u);
|
|
uni_msg_destroy(m);
|
|
}
|
|
|
|
|
|
/*
|
|
* This macro tries to implement the delaying behaviour for
|
|
* message from the API when we are in the Awaiting-Establish state.
|
|
* In this state, the message is delayed. If we drop back to CU 0,
|
|
* everything gets unqueued and errors are returned for all that stuff.
|
|
* If we progess to CUSTAT2 we process the requests.
|
|
*/
|
|
#define COMMON_DELAY(SIG, COOKIE) \
|
|
if (uni->custat == CU_STAT0 || uni->custat == CU_STAT2) {\
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BADCU, \
|
|
COOKIE, 0); \
|
|
break; \
|
|
} \
|
|
if (uni->custat == CU_STAT1) { \
|
|
uni_delenq_coord(uni, SIG, COOKIE, msg); \
|
|
break; \
|
|
}
|
|
|
|
/*
|
|
* Signal handler of the coordinator
|
|
*/
|
|
void
|
|
uni_sig_coord(struct uni *uni, enum coord_sig sig, u_int32_t cookie,
|
|
struct uni_msg *msg)
|
|
{
|
|
struct call *c;
|
|
|
|
if (sig >= SIGO_END) {
|
|
VERBOSE(uni, UNI_FAC_ERR, 1, "Signal %d outside of range to "
|
|
"Coord", sig);
|
|
if (msg)
|
|
uni_msg_destroy(msg);
|
|
return;
|
|
}
|
|
|
|
VERBOSE(uni, UNI_FAC_COORD, 1, "Signal %s in state %s",
|
|
coord_sigs[sig], cunames[uni->custat]);
|
|
|
|
switch (sig) {
|
|
|
|
case SIGO_END:
|
|
break;
|
|
|
|
case SIGO_DATA: /* delayed output */
|
|
if (uni->custat == CU_STAT0 || uni->custat == CU_STAT1)
|
|
break; /* drop */
|
|
if (uni->custat == CU_STAT1)
|
|
uni_delenq_coord(uni, SIGO_DATA, cookie, msg);/* ??? */
|
|
else
|
|
uni->funcs->saal_output(uni, uni->arg,
|
|
SAAL_DATA_request, msg);
|
|
msg = NULL;
|
|
break;
|
|
|
|
/*
|
|
* SAAL signals
|
|
*/
|
|
case SIGO_SAAL_ESTABLISH_indication:
|
|
coord_saal_establish_indication(uni);
|
|
break;
|
|
|
|
case SIGO_SAAL_ESTABLISH_confirm:
|
|
coord_saal_establish_confirm(uni);
|
|
break;
|
|
|
|
case SIGO_SAAL_RELEASE_confirm:
|
|
coord_saal_release_confirm(uni);
|
|
break;
|
|
|
|
case SIGO_SAAL_RELEASE_indication:
|
|
coord_saal_release_indication(uni);
|
|
break;
|
|
|
|
case SIGO_SAAL_DATA_indication:
|
|
coord_saal_data_indication(uni, msg);
|
|
msg = NULL;
|
|
break;
|
|
|
|
case SIGO_SAAL_UDATA_indication:
|
|
VERBOSE0(uni, UNI_FAC_ERR, "SAAL_UDATA_indication");
|
|
break;
|
|
|
|
/*
|
|
* Signals from USER
|
|
*/
|
|
case SIGO_LINK_ESTABLISH_request:
|
|
coord_link_establish_request(uni, cookie);
|
|
break;
|
|
|
|
case SIGO_LINK_RELEASE_request:
|
|
coord_link_release_request(uni, cookie);
|
|
break;
|
|
|
|
case SIGO_RESET_request:
|
|
uni_enq_start(uni, SIGS_RESET_request, cookie, msg, NULL);
|
|
msg = NULL;
|
|
if (uni->custat == CU_STAT0) {
|
|
uni->funcs->saal_output(uni, uni->arg,
|
|
SAAL_ESTABLISH_request, NULL);
|
|
if (!TIMER_ISACT(uni, t309))
|
|
TIMER_START_UNI(uni, t309, uni->timer309);
|
|
set_custat(uni, CU_STAT1);
|
|
}
|
|
break;
|
|
|
|
case SIGO_RESET_ERROR_response:
|
|
COMMON_DELAY(SIGO_RESET_ERROR_response, cookie);
|
|
uni_enq_resp(uni, SIGR_RESET_ERROR_response, cookie, msg, NULL);
|
|
msg = NULL;
|
|
break;
|
|
|
|
case SIGO_RESET_response:
|
|
COMMON_DELAY(SIGO_RESET_response, cookie);
|
|
uni_enq_resp(uni, SIGR_RESET_response, cookie, msg, NULL);
|
|
msg = NULL;
|
|
break;
|
|
|
|
case SIGO_SETUP_request:
|
|
if ((c = uni_create_new_call(uni, cookie)) != NULL) {
|
|
uni_enq_call(c, SIGC_SETUP_request, cookie, msg, NULL);
|
|
msg = NULL;
|
|
if (uni->custat == CU_STAT0) {
|
|
uni->funcs->saal_output(uni, uni->arg,
|
|
SAAL_ESTABLISH_request, NULL);
|
|
if (!TIMER_ISACT(uni, t309))
|
|
TIMER_START_UNI(uni, t309, uni->timer309);
|
|
set_custat(uni, CU_STAT1);
|
|
}
|
|
} else {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_NOMEM, cookie,
|
|
UNI_CALLSTATE_U0);
|
|
}
|
|
break;
|
|
|
|
case SIGO_PROCEEDING_request:
|
|
{
|
|
struct uniapi_proceeding_request *arg =
|
|
uni_msg_rptr(msg, struct uniapi_proceeding_request *);
|
|
|
|
COMMON_DELAY(SIGO_PROCEEDING_request, cookie);
|
|
if ((c = uni_find_call(uni, &arg->call_proc.hdr.cref)) != NULL) {
|
|
uni_enq_call(c, SIGC_PROCEEDING_request, cookie, msg, NULL);
|
|
msg = NULL;
|
|
} else {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALL, cookie,
|
|
UNI_CALLSTATE_U0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SIGO_ALERTING_request:
|
|
{
|
|
struct uniapi_alerting_request *arg =
|
|
uni_msg_rptr(msg, struct uniapi_alerting_request *);
|
|
|
|
COMMON_DELAY(SIGO_ALERTING_request, cookie);
|
|
if ((c = uni_find_call(uni, &arg->alerting.hdr.cref)) != NULL) {
|
|
uni_enq_call(c, SIGC_ALERTING_request, cookie, msg, NULL);
|
|
msg = NULL;
|
|
} else {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALL, cookie,
|
|
UNI_CALLSTATE_U0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SIGO_SETUP_response:
|
|
{
|
|
struct uniapi_setup_response *arg =
|
|
uni_msg_rptr(msg, struct uniapi_setup_response *);
|
|
|
|
COMMON_DELAY(SIGO_SETUP_response, cookie);
|
|
if ((c = uni_find_call(uni, &arg->connect.hdr.cref)) != NULL) {
|
|
uni_enq_call(c, SIGC_SETUP_response, cookie, msg, NULL);
|
|
msg = NULL;
|
|
} else {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALL, cookie,
|
|
UNI_CALLSTATE_U0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SIGO_SETUP_COMPLETE_request:
|
|
{
|
|
struct uniapi_setup_complete_request *arg =
|
|
uni_msg_rptr(msg, struct uniapi_setup_complete_request *);
|
|
|
|
COMMON_DELAY(SIGO_SETUP_COMPLETE_request, cookie);
|
|
if ((c = uni_find_call(uni, &arg->connect_ack.hdr.cref)) != NULL) {
|
|
uni_enq_call(c, SIGC_SETUP_COMPLETE_request,
|
|
cookie, msg, NULL);
|
|
msg = NULL;
|
|
} else {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALL, cookie,
|
|
UNI_CALLSTATE_U0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SIGO_RELEASE_request:
|
|
{
|
|
struct uniapi_release_request *arg =
|
|
uni_msg_rptr(msg, struct uniapi_release_request *);
|
|
|
|
COMMON_DELAY(SIGO_RELEASE_request, cookie);
|
|
if ((c = uni_find_call(uni, &arg->release.hdr.cref)) != NULL) {
|
|
uni_enq_call(c, SIGC_RELEASE_request, cookie, msg, NULL);
|
|
msg = NULL;
|
|
} else {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALL, cookie,
|
|
UNI_CALLSTATE_U0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SIGO_RELEASE_response:
|
|
{
|
|
struct uniapi_release_response *arg =
|
|
uni_msg_rptr(msg, struct uniapi_release_response *);
|
|
|
|
COMMON_DELAY(SIGO_RELEASE_response, cookie);
|
|
if ((c = uni_find_call(uni, &arg->release_compl.hdr.cref)) != NULL) {
|
|
uni_enq_call(c, SIGC_RELEASE_response, cookie, msg, NULL);
|
|
msg = NULL;
|
|
} else {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALL, cookie,
|
|
UNI_CALLSTATE_U0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SIGO_NOTIFY_request:
|
|
{
|
|
struct uniapi_notify_request *arg =
|
|
uni_msg_rptr(msg, struct uniapi_notify_request *);
|
|
|
|
COMMON_DELAY(SIGO_NOTIFY_request, cookie);
|
|
if ((c = uni_find_call(uni, &arg->notify.hdr.cref)) != NULL) {
|
|
uni_enq_call(c, SIGC_NOTIFY_request, cookie, msg, NULL);
|
|
msg = NULL;
|
|
} else {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALL, cookie,
|
|
UNI_CALLSTATE_U0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SIGO_STATUS_ENQUIRY_request:
|
|
{
|
|
struct uniapi_status_enquiry_request *arg =
|
|
uni_msg_rptr(msg, struct uniapi_status_enquiry_request *);
|
|
|
|
COMMON_DELAY(SIGO_STATUS_ENQUIRY_request, cookie);
|
|
if ((c = uni_find_call(uni, &arg->cref)) != NULL) {
|
|
uni_enq_call(c, SIGC_STATUS_ENQUIRY_request, cookie, msg, NULL);
|
|
msg = NULL;
|
|
} else {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALL, cookie,
|
|
UNI_CALLSTATE_U0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SIGO_ADD_PARTY_request:
|
|
{
|
|
struct uniapi_add_party_request *arg =
|
|
uni_msg_rptr(msg, struct uniapi_add_party_request *);
|
|
|
|
COMMON_DELAY(SIGO_ADD_PARTY_request, cookie);
|
|
if ((c = uni_find_call(uni, &arg->add.hdr.cref)) != NULL) {
|
|
if (c->type != CALL_ROOT) {
|
|
uniapi_call_error(c, UNIAPI_ERROR_BAD_CTYPE,
|
|
cookie);
|
|
break;
|
|
}
|
|
uni_enq_call(c, SIGC_ADD_PARTY_request, cookie, msg, NULL);
|
|
msg = NULL;
|
|
} else {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALL, cookie,
|
|
UNI_CALLSTATE_U0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SIGO_PARTY_ALERTING_request:
|
|
{
|
|
struct uniapi_party_alerting_request *arg =
|
|
uni_msg_rptr(msg, struct uniapi_party_alerting_request *);
|
|
|
|
COMMON_DELAY(SIGO_PARTY_ALERTING_request, cookie);
|
|
if ((c = uni_find_call(uni, &arg->alert.hdr.cref)) != NULL) {
|
|
if (c->type != CALL_LEAF) {
|
|
uniapi_call_error(c, UNIAPI_ERROR_BAD_CTYPE,
|
|
cookie);
|
|
break;
|
|
}
|
|
uni_enq_call(c, SIGC_PARTY_ALERTING_request, cookie, msg, NULL);
|
|
msg = NULL;
|
|
} else {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALL, cookie,
|
|
UNI_CALLSTATE_U0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SIGO_ADD_PARTY_ACK_request:
|
|
{
|
|
struct uniapi_add_party_ack_request *arg =
|
|
uni_msg_rptr(msg, struct uniapi_add_party_ack_request *);
|
|
|
|
COMMON_DELAY(SIGO_ADD_PARTY_ACK_request, cookie);
|
|
if ((c = uni_find_call(uni, &arg->ack.hdr.cref)) != NULL) {
|
|
if (c->type != CALL_LEAF) {
|
|
uniapi_call_error(c, UNIAPI_ERROR_BAD_CTYPE,
|
|
cookie);
|
|
break;
|
|
}
|
|
uni_enq_call(c, SIGC_ADD_PARTY_ACK_request, cookie, msg, NULL);
|
|
msg = NULL;
|
|
} else {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALL, cookie,
|
|
UNI_CALLSTATE_U0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SIGO_ADD_PARTY_REJ_request:
|
|
{
|
|
struct uniapi_add_party_rej_request *arg =
|
|
uni_msg_rptr(msg, struct uniapi_add_party_rej_request *);
|
|
|
|
COMMON_DELAY(SIGO_ADD_PARTY_REJ_request, cookie);
|
|
if ((c = uni_find_call(uni, &arg->rej.hdr.cref)) != NULL) {
|
|
if (c->type != CALL_LEAF) {
|
|
uniapi_call_error(c, UNIAPI_ERROR_BAD_CTYPE,
|
|
cookie);
|
|
break;
|
|
}
|
|
uni_enq_call(c, SIGC_ADD_PARTY_REJ_request, cookie, msg, NULL);
|
|
msg = NULL;
|
|
} else {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALL, cookie,
|
|
UNI_CALLSTATE_U0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SIGO_DROP_PARTY_request:
|
|
{
|
|
struct uniapi_drop_party_request *arg =
|
|
uni_msg_rptr(msg, struct uniapi_drop_party_request *);
|
|
|
|
COMMON_DELAY(SIGO_DROP_PARTY_request, cookie);
|
|
if ((c = uni_find_call(uni, &arg->drop.hdr.cref)) != NULL) {
|
|
if (c->type != CALL_ROOT && c->type != CALL_LEAF) {
|
|
uniapi_call_error(c, UNIAPI_ERROR_BAD_CTYPE,
|
|
cookie);
|
|
break;
|
|
}
|
|
uni_enq_call(c, SIGC_DROP_PARTY_request, cookie, msg, NULL);
|
|
msg = NULL;
|
|
} else {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALL, cookie,
|
|
UNI_CALLSTATE_U0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SIGO_DROP_PARTY_ACK_request:
|
|
{
|
|
struct uniapi_drop_party_ack_request *arg =
|
|
uni_msg_rptr(msg, struct uniapi_drop_party_ack_request *);
|
|
|
|
COMMON_DELAY(SIGO_DROP_PARTY_ACK_request, cookie);
|
|
if ((c = uni_find_call(uni, &arg->ack.hdr.cref)) != NULL) {
|
|
if (c->type != CALL_ROOT && c->type != CALL_LEAF) {
|
|
uniapi_call_error(c, UNIAPI_ERROR_BAD_CTYPE,
|
|
cookie);
|
|
break;
|
|
}
|
|
uni_enq_call(c, SIGC_DROP_PARTY_ACK_request, cookie, msg, NULL);
|
|
msg = NULL;
|
|
} else {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALL, cookie,
|
|
UNI_CALLSTATE_U0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SIGO_ABORT_CALL_request:
|
|
{
|
|
struct uniapi_abort_call_request *arg =
|
|
uni_msg_rptr(msg, struct uniapi_abort_call_request *);
|
|
|
|
if ((c = uni_find_call(uni, &arg->cref)) != NULL) {
|
|
uni_enq_call(c, SIGC_ABORT_CALL_request, cookie, NULL, NULL);
|
|
} else {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALL, cookie,
|
|
UNI_CALLSTATE_U0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Call-Control
|
|
*/
|
|
case SIGO_CALL_DESTROYED:
|
|
uni->funcs->uni_output(uni, uni->arg,
|
|
UNIAPI_CALL_DESTROYED, 0, msg);
|
|
msg = NULL;
|
|
break;
|
|
|
|
/*
|
|
* ResetRespond
|
|
*/
|
|
case SIGO_RESET_indication:
|
|
uni->funcs->uni_output(uni, uni->arg,
|
|
UNIAPI_RESET_indication, 0, msg);
|
|
msg = NULL;
|
|
break;
|
|
|
|
/*
|
|
* Timeouts
|
|
*/
|
|
case SIGO_T309:
|
|
coord_t309(uni);
|
|
break;
|
|
|
|
}
|
|
if (msg != NULL)
|
|
uni_msg_destroy(msg);
|
|
}
|
|
|
|
/*
|
|
* Send a signal to all call instances
|
|
*/
|
|
static void
|
|
sig_all_calls(struct uni *uni, u_int sig)
|
|
{
|
|
struct call *call;
|
|
|
|
TAILQ_FOREACH(call, &uni->calls, link)
|
|
uni_enq_call(call, sig, 0, NULL, NULL);
|
|
}
|
|
|
|
/*
|
|
* Set a new coordinator state - this moves all delayed coordinator
|
|
* signals from the delayed queue to the signal queue.
|
|
*/
|
|
static int
|
|
cufilt(struct sig *s, void *arg __unused)
|
|
{
|
|
return (s->type == SIG_COORD);
|
|
}
|
|
|
|
static void
|
|
set_custat(struct uni *uni, enum cu_stat nstate)
|
|
{
|
|
if (uni->custat != nstate) {
|
|
uni->custat = nstate;
|
|
uni_undel(uni, cufilt, NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* T309 timeout function
|
|
*/
|
|
static void
|
|
t309_func(struct uni *uni)
|
|
{
|
|
uni_enq_coord(uni, SIGO_T309, 0, NULL);
|
|
}
|
|
|
|
/*
|
|
* Respond with a status message
|
|
*/
|
|
void
|
|
uni_respond_status(struct uni *uni, struct uni_cref *cref,
|
|
enum uni_callstate cs, enum uni_cause c1)
|
|
{
|
|
struct uni_all *resp;
|
|
|
|
if ((resp = UNI_ALLOC()) == NULL)
|
|
return;
|
|
|
|
MK_MSG_RESP(resp, UNI_STATUS, cref);
|
|
MK_IE_CALLSTATE(resp->u.status.callstate, cs);
|
|
MK_IE_CAUSE(resp->u.status.cause, UNI_CAUSE_LOC_USER, c1);
|
|
|
|
(void)uni_send_output(resp, uni);
|
|
|
|
UNI_FREE(resp);
|
|
}
|
|
|
|
/*
|
|
* Respond with a status message
|
|
*/
|
|
void
|
|
uni_respond_status_mtype(struct uni *uni, struct uni_cref *cref,
|
|
enum uni_callstate cs, enum uni_cause c1, u_int mtype)
|
|
{
|
|
struct uni_all *resp;
|
|
|
|
if((resp = UNI_ALLOC()) == NULL)
|
|
return;
|
|
|
|
MK_MSG_RESP(resp, UNI_STATUS, cref);
|
|
MK_IE_CALLSTATE(resp->u.status.callstate, cs);
|
|
MK_IE_CAUSE(resp->u.status.cause, UNI_CAUSE_LOC_USER, c1);
|
|
ADD_CAUSE_MTYPE(resp->u.status.cause, mtype);
|
|
|
|
(void)uni_send_output(resp, uni);
|
|
|
|
UNI_FREE(resp);
|
|
}
|
|
|
|
/*
|
|
* Send a message. If we are in CUSTAT1, delay the message if we
|
|
* are in CUSTAT3 send it, else drop it.
|
|
*/
|
|
int
|
|
uni_send_output(struct uni_all *u, struct uni *uni)
|
|
{
|
|
struct uni_msg *m;
|
|
int err;
|
|
|
|
if (uni->custat == CU_STAT0 || uni->custat == CU_STAT2)
|
|
return (0);
|
|
|
|
m = uni_msg_alloc(1024);
|
|
if ((err = uni_encode(m, u, &uni->cx)) != 0) {
|
|
VERBOSE0(uni, UNI_FAC_ERR, "uni_encode failed: %08x", err);
|
|
uni_msg_destroy(m);
|
|
return (-1);
|
|
}
|
|
if (uni->custat == CU_STAT1)
|
|
uni_delenq_coord(uni, SIGO_DATA, 0, m);
|
|
else
|
|
uni->funcs->saal_output(uni, uni->arg, SAAL_DATA_request, m);
|
|
return (0);
|
|
}
|