/*
 * Copyright (c) 1996-2003
 *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
 * 	All rights reserved.
 *
 * Author: Hartmut Brandt <harti@freebsd.org>
 *
 * 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.
 *
 * $Begemot: libunimsg/netnatm/sig/sig_reset.c,v 1.11 2004/08/05 07:11:03 brandt Exp $
 *
 * Reset-start and reset-respond
 */

#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>

static void response_restart(struct uni *, struct uni_msg *, struct uni_all *);
static void response_status(struct uni *, struct uni_msg *, struct uni_all *);

static void response_t317(struct uni *);

static void response_error(struct uni *, struct uniapi_reset_error_response *,
    uint32_t cookie);
static void response_response(struct uni *, struct uniapi_reset_response *,
    uint32_t);

static void start_request(struct uni *, struct uniapi_reset_request *,
    uint32_t);

static void start_t316(struct uni *);

static void start_restart_ack(struct uni *, struct uni_msg *, struct uni_all *);
static void start_status(struct uni *, struct uni_msg *, struct uni_all *);

static int restart_forward(struct uni *, const struct uni_all *);

#define DEF_PRIV_SIG(NAME, FROM)	[SIG##NAME] =	"SIG"#NAME,
static const char *const start_sigs[] = {
	DEF_START_SIGS
};
#undef DEF_PRIV_SIG

#define DEF_PRIV_SIG(NAME, FROM)	[SIG##NAME] =	"SIG"#NAME,
static const char *const respond_sigs[] = {
	DEF_RESPOND_SIGS
};
#undef DEF_PRIV_SIG

TIMER_FUNC_UNI(t317, t317_func)
TIMER_FUNC_UNI(t316, t316_func)

/*
 * Reset-Start process.
 */
void
uni_sig_start(struct uni *uni, u_int sig, uint32_t cookie,
    struct uni_msg *m, struct uni_all *u)
{
	if (sig >= SIGS_END) {
		VERBOSE(uni, UNI_FAC_ERR, 1, "Signal %d outside of range to "
		    "Reset-Start", sig);
		if (m)
			uni_msg_destroy(m);
		if (u)
			UNI_FREE(u);
		return;
	}

	VERBOSE(uni, UNI_FAC_RESTART, 1,
	    "Signal %s in state %u of Reset-Start; cookie %u",
	    start_sigs[sig], uni->glob_start, cookie);

	switch (sig) {

	/*
	 * User requests
	 */
	  case SIGS_RESET_request:
		start_request(uni,
		    uni_msg_rptr(m, struct uniapi_reset_request *), cookie);
		uni_msg_destroy(m);
		break;

	/*
	 * Timers
	 */
	  case SIGS_T316:
		start_t316(uni);
		break;

	/*
	 * SAAL
	 */
	  case SIGS_RESTART_ACK:
		start_restart_ack(uni, m, u);
		uni_msg_destroy(m);
		UNI_FREE(u);
		break;

	  case SIGS_STATUS:
		start_status(uni, m, u);
		uni_msg_destroy(m);
		UNI_FREE(u);
		break;

	  case SIGS_END:
		break;
	}
}

/*
 * Reset-request from USER.
 *
 * Q.2931:Reset-Start 1/2
 */
static void
start_request(struct uni *uni, struct uniapi_reset_request *req, uint32_t cookie)
{
	struct uni_all *resp;
	int err;

	if (uni->glob_start != UNI_CALLSTATE_REST0) {
		uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALLSTATE, cookie, 0);
		return;
	}

	if ((resp = UNI_ALLOC()) == NULL) {
		uniapi_uni_error(uni, UNIAPI_ERROR_NOMEM, cookie, 0);
		return;
	}

	MK_MSG_ORIG(resp, UNI_RESTART, 0, 0);
	resp->u.restart.restart = req->restart;
	resp->u.restart.connid = req->connid;

	if (restart_forward(uni, resp))
		return;

	uni->connid_start = req->connid;
	uni->restart_start = req->restart;

	if ((err = uni_send_output(resp, uni)) != 0)
		uniapi_uni_error(uni, UNIAPI_ERROR_ENCODING, cookie, 0);
	UNI_FREE(resp);
	if (err)
		return;

	uni->cnt316 = 0;
	TIMER_START_UNI(uni, t316, uni->timer316);
	uni->glob_start = UNI_CALLSTATE_REST1;

	VERBOSE(uni, UNI_FAC_RESTART, 1, "Reset-Start state := 1");


	uniapi_uni_error(uni, UNIAPI_OK, cookie, 0);
}

/*
 * T316 timeout function
 */
static void
t316_func(struct uni *uni)
{
	uni_enq_start(uni, SIGS_T316, 0, NULL, NULL);
}

/*
 * Q.2931:Reset-Start 1/2
 */
static void
start_t316(struct uni *uni)
{
	if (uni->glob_start != UNI_CALLSTATE_REST1) {
		VERBOSE0(uni, UNI_FAC_ERR, "T316 in state %d",
		    uni->glob_start);
		return;
	}

	if (++uni->cnt316 == uni->init316) {
		struct uni_msg *app;
		struct uniapi_reset_error_indication *resp;

		VERBOSE(uni, UNI_FAC_RESTART, 1, "Reset-Start error");

		resp = ALLOC_API(struct uniapi_reset_error_indication, app);
		if (resp != NULL) {
			resp->source = 0;
			resp->reason = UNIAPI_RESET_ERROR_NO_RESPONSE,

			uni->funcs->uni_output(uni, uni->arg,
			    UNIAPI_RESET_ERROR_indication, 0, app);
		}

		uni->glob_start = UNI_CALLSTATE_REST0;
		VERBOSE(uni, UNI_FAC_RESTART, 1, "Reset-Start state := 0");
	} else {
		struct uni_all *resp;

		if ((resp = UNI_ALLOC()) == NULL)
			return;

		MK_MSG_ORIG(resp, UNI_RESTART, 0, 0);
		resp->u.restart.restart = uni->restart_start;
		resp->u.restart.connid = uni->connid_start;

		(void)uni_send_output(resp, uni);

		UNI_FREE(resp);

		TIMER_START_UNI(uni, t316, uni->timer316);
	}
}

/*
 * Got RESTART_ACK.
 */
static void
start_restart_ack(struct uni *uni, struct uni_msg *m, struct uni_all *u)
{
	enum uni_callstate new_state;
	struct uniapi_reset_confirm *conf;
	struct uni_msg *app;

	if (uni->glob_start == UNI_CALLSTATE_REST0) {
		uni_respond_status_mtype(uni, &u->u.hdr.cref, uni->glob_start,
		    UNI_CAUSE_MSG_INCOMP, UNI_RESTART_ACK);
		return;
	}

	if (uni->glob_start != UNI_CALLSTATE_REST1) {
		ASSERT(0, ("bad global call state in Reset-Start"));
		return;
	}

	/*
	 * If body decoding fails, this is because IEs are wrong.
	 */
	(void)uni_decode_body(m, u, &uni->cx);
	MANDATE_IE(uni, u->u.restart_ack.restart, UNI_IE_RESTART);

	if (IE_ISGOOD(u->u.restart_ack.restart)) {
		/*
		 * Q.2931: 5.5.2.2
		 */
		if (u->u.restart_ack.restart.rclass == UNI_RESTART_ALL &&
		    IE_ISGOOD(u->u.restart_ack.connid)) {
			UNI_SAVE_IERR(&uni->cx, UNI_IE_CONNID,
			    u->u.restart_ack.connid.h.act,
			    UNI_IERR_UNK);
		} else if ((u->u.restart_ack.restart.rclass == UNI_RESTART_PATH ||
			    u->u.restart_ack.restart.rclass == UNI_RESTART_CHANNEL)) {
			MANDATE_IE(uni, u->u.restart_ack.connid, UNI_IE_CONNID);
		}
	}
	/*
	 * Compare the information elements now, because
	 * we may need the new callstate for the status message
	 * below.
	 */
	new_state = UNI_CALLSTATE_REST1;

	if (IE_ISGOOD(u->u.restart_ack.restart) &&
	    IE_ISGOOD(uni->restart_start) &&
	    u->u.restart_ack.restart.rclass == uni->restart_start.rclass &&
	    !IE_ISGOOD(u->u.restart_ack.connid) == !IE_ISGOOD(uni->connid_start) &&
	    (!IE_ISGOOD(uni->connid_start) ||
	       (u->u.restart_ack.connid.vpci == uni->connid_start.vpci &&
	        u->u.restart_ack.connid.vci == uni->connid_start.vci)))
		new_state = UNI_CALLSTATE_REST0;

	switch (uni_verify(uni, u->u.hdr.act)) {
	  case VFY_RAIM:
	  case VFY_RAI:
		uni_respond_status_verify(uni, &u->u.hdr.cref,
		    UNI_CALLSTATE_REST1, NULL, 0);
	  case VFY_I:
		return;

	  case VFY_CLR:
		uni->glob_start = UNI_CALLSTATE_REST0;
		VERBOSE(uni, UNI_FAC_RESTART, 1,
		    "Reset-Start state := 0");
		return;

	  case VFY_RAP:
	  case VFY_RAPU:
		uni_respond_status_verify(uni, &u->u.hdr.cref,
		    new_state, NULL, 0);
	  case VFY_OK:
		break;
	}

	if (new_state == UNI_CALLSTATE_REST1)
		/*
		 * Q.2931: 5.5.1.2/2
		 */
		return;

	/*
	 * Build restart.confirm signal for application
	 */
	if (!IE_ISGOOD(u->u.restart_ack.connid))
		u->u.restart.connid.h.present = 0;


	if ((conf = ALLOC_API(struct uniapi_reset_confirm, app)) == NULL)
		return;
	conf->restart = u->u.restart.restart;
	conf->connid = u->u.restart.connid;

	TIMER_STOP_UNI(uni, t316);

	uni->funcs->uni_output(uni, uni->arg, UNIAPI_RESET_confirm, 0, app);

	uni->glob_start = UNI_CALLSTATE_REST0;
	VERBOSE(uni, UNI_FAC_RESTART, 1, "Reset-Start state := 0");
}

/*
 * Reset-Start got a STATUS message.
 *
 * Q.2931: Reset-Start 2/2
 *
 * In Q.2931 only CALLSTATE_REST1 is allowed, this seems silly and to contradict
 * 5.6.12. So allow it in any state.
 *
 * The following states are considered compatible:
 *
 *  Sender   Receiver(we)
 *  ------   --------
 *  Rest0     Rest0	this is the normal state OK!
 *  Rest2     Rest0	this may be the result of no answer from the API
 *			on the remote end and the us finally timing out. ERROR!
 *  Rest2     Rest1	this is normal. OK!
 *  Rest0     Rest1	RESTART_ACK was probably lost. OK!
 *
 * All others are wrong.
 */
static void
start_status(struct uni *uni, struct uni_msg *m, struct uni_all *u)
{
	(void)uni_decode_body(m, u, &uni->cx);
	MANDATE_IE(uni, u->u.status.callstate, UNI_IE_CALLSTATE);
	MANDATE_IE(uni, u->u.status.cause, UNI_IE_CAUSE);
	switch (uni_verify(uni, u->u.hdr.act)) {
	  case VFY_CLR:
		uni->glob_start = UNI_CALLSTATE_REST0;
		VERBOSE(uni, UNI_FAC_RESTART, 1, "Reset-Start state := 0");
		return;

	  case VFY_RAIM:
	  case VFY_RAI:
	  case VFY_RAP:
	  case VFY_RAPU:
		uni_respond_status_verify(uni, &u->u.hdr.cref, uni->glob_start,
		    NULL, 0);
	  case VFY_I:
	  case VFY_OK:
		break;
	}
	if (!IE_ISGOOD(u->u.status.callstate)) {
		/*
		 * As a result of the strange handling above, we must
		 * process a STATUS with an invalid or missing callstate!
		 */
		return;
	}
	if ((u->u.status.callstate.state == UNI_CALLSTATE_REST0 &&
	     uni->glob_start == UNI_CALLSTATE_REST0) ||
	    (u->u.status.callstate.state == UNI_CALLSTATE_REST0 &&
	     uni->glob_start == UNI_CALLSTATE_REST1) ||
	    (u->u.status.callstate.state == UNI_CALLSTATE_REST2 &&
	     uni->glob_start == UNI_CALLSTATE_REST1)) {
		/*
		 * Implementation dependend procedure:
		 * Inform the API
		 */
		struct uniapi_reset_status_indication *resp;
		struct uni_msg *app;

		resp = ALLOC_API(struct uniapi_reset_status_indication, app);
		if (resp == NULL)
			return;
		resp->cref = u->u.hdr.cref;
		resp->callstate = u->u.status.callstate;
		if (IE_ISGOOD(u->u.status.cause))
			resp->cause = u->u.status.cause;

		uni->funcs->uni_output(uni, uni->arg,
		    UNIAPI_RESET_STATUS_indication, 0, app);
		
	} else {
		struct uniapi_reset_error_indication *resp;
		struct uni_msg *app;

		resp = ALLOC_API(struct uniapi_reset_error_indication, app);
		if (resp != NULL) {
			resp->source = 0;
			resp->reason = UNIAPI_RESET_ERROR_PEER_INCOMP_STATE,

			uni->funcs->uni_output(uni, uni->arg,
			    UNIAPI_RESET_ERROR_indication, 0, app);
		}
	}
}

/************************************************************/
/*
 * Reset-Respond process.
 */
void
uni_sig_respond(struct uni *uni, u_int sig, uint32_t cookie,
    struct uni_msg *m, struct uni_all *u)
{
	if (sig >= SIGR_END) {
		VERBOSE(uni, UNI_FAC_ERR, 1, "Signal %d outside of range to "
		    "Reset-Respond", sig);
		if (m)
			uni_msg_destroy(m);
		if (u)
			UNI_FREE(u);
		return;
	}

	VERBOSE(uni, UNI_FAC_RESTART, 1,
	    "Signal %s in state %u of Reset-Respond; cookie %u",
	    respond_sigs[sig], uni->glob_respond, cookie);

	switch (sig) {

	/*
	 * SAAL
	 */
	  case SIGR_RESTART:
		response_restart(uni, m, u);
		uni_msg_destroy(m);
		UNI_FREE(u);
		break;

	  case SIGR_STATUS:
		response_status(uni, m, u);
		uni_msg_destroy(m);
		UNI_FREE(u);
		break;

	/*
	 * User
	 */
	  case SIGR_RESET_ERROR_response:
		response_error(uni,
		    uni_msg_rptr(m, struct uniapi_reset_error_response *),
		    cookie);
		uni_msg_destroy(m);
		break;

	  case SIGR_RESET_response:
		response_response(uni,
		    uni_msg_rptr(m, struct uniapi_reset_response *), cookie);
		uni_msg_destroy(m);
		break;

	/*
	 * Timers
	 */
	  case SIGR_T317:
		response_t317(uni);
		return;

	  case SIGR_END:
		break;
	}
}

/*
 * Send a RELEASE_COMPLETE to all affected calls as per
 * F.2.3(3)
 */
static int
restart_forward(struct uni *uni, const struct uni_all *u)
{
	struct call *c;
	struct uni_all *resp;

	if ((resp = UNI_ALLOC()) == NULL)
		return (-1);

	TAILQ_FOREACH(c, &uni->calls, link) {
		if (u->u.restart.restart.rclass == UNI_RESTART_ALL ||
		    (IE_ISPRESENT(c->connid) &&
		    u->u.restart.connid.vpci == c->connid.vpci &&
		    (u->u.restart.restart.rclass == UNI_RESTART_PATH ||
		    u->u.restart.connid.vci == c->connid.vci))) {
			MK_MSG_ORIG(resp, UNI_RELEASE_COMPL, c->cref, c->mine);
			uni_release_compl(c, resp);
		}
	}

	UNI_FREE(resp);
	return (0);
}

/*
 * Respond process got a restart message.
 * Doesn't free the messages.
 */
static void
response_restart(struct uni *uni, struct uni_msg *m, struct uni_all *u)
{
	struct uni_msg *app;
	struct uniapi_reset_indication *ind;

	if (uni->glob_respond == UNI_CALLSTATE_REST0) {
		/*
		 * If body decoding fails, this is because IEs are wrong.
		 */
		(void)uni_decode_body(m, u, &uni->cx);
		MANDATE_IE(uni, u->u.restart.restart, UNI_IE_RESTART);
		if (IE_ISGOOD(u->u.restart.restart)) {
			/*
			 * Q.2931: 5.5.2.2
			 */
			if (u->u.restart.restart.rclass == UNI_RESTART_ALL &&
			   IE_ISGOOD(u->u.restart.connid)) {
				UNI_SAVE_IERR(&uni->cx, UNI_IE_CONNID,
				    u->u.restart.connid.h.act,
				    UNI_IERR_UNK);
			} else if ((u->u.restart.restart.rclass == UNI_RESTART_PATH ||
				   u->u.restart.restart.rclass == UNI_RESTART_CHANNEL)) {
				MANDATE_IE(uni, u->u.restart.connid, UNI_IE_CONNID);
			}
		}
		switch (uni_verify(uni, u->u.hdr.act)) {
		  case VFY_RAIM:
		  case VFY_RAI:
			uni_respond_status_verify(uni, &u->u.hdr.cref,
			    UNI_CALLSTATE_REST0, NULL, 0);
		  case VFY_CLR:
		  case VFY_I:
			return;

		  case VFY_RAP:
		  case VFY_RAPU:
			uni_respond_status_verify(uni, &u->u.hdr.cref,
			    UNI_CALLSTATE_REST2, NULL, 0);
		  case VFY_OK:
			break;
		}
		if (!IE_ISGOOD(u->u.restart.connid))
			u->u.restart.connid.h.present = 0;

		/*
		 * Send a RELEASE_COMPLETE to all affected calls as per
		 * F.2.3(3)
		 */
		if (restart_forward(uni, u))
			return;

		/*
		 * Build restart signal for application
		 */
		if ((ind = ALLOC_API(struct uniapi_reset_indication, app)) == NULL)
			return;

		ind->restart = u->u.restart.restart;
		ind->connid = u->u.restart.connid;

		uni_enq_coord(uni, SIGO_RESET_indication, 0, app);

		TIMER_START_UNI(uni, t317, uni->timer317);
		uni->glob_respond = UNI_CALLSTATE_REST2;

		VERBOSE(uni, UNI_FAC_RESTART, 1, "Reset-Respond state := 2");


	} else if (uni->glob_respond == UNI_CALLSTATE_REST2) {
		/*
		 * No need to decode the message. It is unexpected in this
		 * state so return a status.
		 */
		uni_respond_status_mtype(uni, &u->u.hdr.cref, uni->glob_respond,
		    UNI_CAUSE_MSG_INCOMP, UNI_RESTART);


	} else
		ASSERT(0, ("bad global call state in responder"));
}

static void
response_t317(struct uni *uni)
{
	struct uniapi_reset_error_indication *resp;
	struct uni_msg *app;

	if (uni->glob_respond != UNI_CALLSTATE_REST2) {
		VERBOSE0(uni, UNI_FAC_ERR, "T317 in state %d",
		    uni->glob_respond);
		return;
	}

	VERBOSE(uni, UNI_FAC_RESTART, 1, "Reset-Respond error");

	if ((resp = ALLOC_API(struct uniapi_reset_error_indication, app)) != NULL) {
		resp->source = 1;
		resp->reason = UNIAPI_RESET_ERROR_NO_CONFIRM;

		uni->funcs->uni_output(uni, uni->arg,
		    UNIAPI_RESET_ERROR_indication, 0, app);
	}

	uni->glob_respond = UNI_CALLSTATE_REST0;
	VERBOSE(uni, UNI_FAC_RESTART, 1, "Reset-Respond state := 0");
}

/*
 * Error response from USER
 */
static void
response_error(struct uni *uni, struct uniapi_reset_error_response *c,
    uint32_t cookie)
{
	struct uni_all *resp;

	if (uni->glob_respond != UNI_CALLSTATE_REST2) {
		uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALLSTATE, cookie, 0);
		return;
	}

	if ((resp = UNI_ALLOC()) == NULL) {
		uniapi_uni_error(uni, UNIAPI_ERROR_NOMEM, cookie, 0);
		return;
	}

	MK_MSG_ORIG(resp, UNI_STATUS, 0, 1);
	MK_IE_CALLSTATE(resp->u.status.callstate, UNI_CALLSTATE_REST2);

	if (IE_ISGOOD(c->cause))
		resp->u.status.cause = c->cause;
	else {
		MK_IE_CAUSE(resp->u.status.cause, UNI_CAUSE_LOC_USER,
		    UNI_CAUSE_CHANNEL_NEX);
		if (IE_ISGOOD(uni->connid_respond))
			ADD_CAUSE_CHANNID(resp->u.status.cause,
			    uni->connid_respond.vpci,
			    uni->connid_respond.vci);
	}

	if (uni_send_output(resp, uni) != 0) {
		uniapi_uni_error(uni, UNIAPI_ERROR_ENCODING, cookie, 0);
		UNI_FREE(resp);
		return;
	}

	uniapi_uni_error(uni, UNIAPI_OK, cookie, 0);
}

/*
 * Reset-response from user.
 */
static void
response_response(struct uni *uni, struct uniapi_reset_response *arg,
    uint32_t cookie)
{
	struct uni_all *resp;

	if (uni->glob_respond != UNI_CALLSTATE_REST2) {
		uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALLSTATE, cookie, 0);
		return;
	}

	if (!IE_ISGOOD(arg->restart)) {
		uniapi_uni_error(uni, UNIAPI_ERROR_MISSING_IE, cookie, 0);
		return;
	}

	if ((resp = UNI_ALLOC()) == NULL) {
		uniapi_uni_error(uni, UNIAPI_ERROR_NOMEM, cookie, 0);
		return;
	}

	TIMER_STOP_UNI(uni, t317);

	MK_MSG_ORIG(resp, UNI_RESTART_ACK, 0, 1);
	resp->u.restart.restart = arg->restart;
	if (IE_ISGOOD(arg->connid))
		resp->u.restart.connid = arg->connid;

	if (uni_send_output(resp, uni) != 0) {
		uniapi_uni_error(uni, UNIAPI_ERROR_ENCODING, cookie, 0);
		UNI_FREE(resp);
		return;
	}

	UNI_FREE(resp);

	uni->glob_respond = UNI_CALLSTATE_REST0;
	VERBOSE(uni, UNI_FAC_RESTART, 1, "Reset-Respond state := 0");

	uniapi_uni_error(uni, UNIAPI_OK, cookie, 0);
}

/*
 * Reset-Response got a STATUS message.
 *
 * Q.2931: Reset-Response 2/2
 *
 * In Q.2931 only CALLSTATE_REST2 is allowed, this seems silly and to contradict
 * 5.6.12. So allow it in any state.
 *
 * The following states are considered compatible:
 *
 *  Sender   Receiver
 *  ------   --------
 *  Rest0     Rest0	this is the normal state OK!
 *  Rest0     Rest2	this may be the result of no answer from the API
 *			and the Sender finally timing out. ERROR!
 *  Rest1     Rest2	this is normal. OK!
 *  Rest1     Rest0	RESTART_ACK was probably lost. OK!
 *
 * All others are wrong.
 */
static void
response_status(struct uni *uni, struct uni_msg *m, struct uni_all *u)
{
	(void)uni_decode_body(m, u, &uni->cx);
	MANDATE_IE(uni, u->u.status.callstate, UNI_IE_CALLSTATE);
	MANDATE_IE(uni, u->u.status.cause, UNI_IE_CAUSE);
	switch (uni_verify(uni, u->u.hdr.act)) {
	  case VFY_CLR:
		if (uni->proto == UNIPROTO_UNI40U) {
			uni->glob_respond = UNI_CALLSTATE_REST0;
			VERBOSE(uni, UNI_FAC_RESTART, 1,
			    "Reset-Respond state := 0");
			return;
		}
		break;

	  case VFY_RAIM:
	  case VFY_RAI:
	  case VFY_RAP:
	  case VFY_RAPU:
		uni_respond_status_verify(uni, &u->u.hdr.cref,
		    uni->glob_respond, NULL, 0);
	  case VFY_I:
	  case VFY_OK:
		break;
	}
	if (!IE_ISGOOD(u->u.status.callstate)) {
		/*
		 * As a result of the strange handling above, we must
		 * process a STATUS with an invalid or missing callstate!
		 */
		return;
	}
	if ((u->u.status.callstate.state == UNI_CALLSTATE_REST0 &&
	     uni->glob_respond == UNI_CALLSTATE_REST0) ||
	    (u->u.status.callstate.state == UNI_CALLSTATE_REST1 &&
	     uni->glob_respond == UNI_CALLSTATE_REST0) ||
	    (u->u.status.callstate.state == UNI_CALLSTATE_REST1 &&
	     uni->glob_respond == UNI_CALLSTATE_REST2)) {
		/*
		 * Implementation dependend procedure:
		 * Inform the API
		 */
		struct uniapi_reset_status_indication *resp;
		struct uni_msg *app;

		resp = ALLOC_API(struct uniapi_reset_status_indication, app);
		if (resp == NULL)
			return;

		resp->cref = u->u.hdr.cref;
		resp->callstate = u->u.status.callstate;
		if (IE_ISGOOD(u->u.status.cause))
			resp->cause = u->u.status.cause;

		uni->funcs->uni_output(uni, uni->arg,
		    UNIAPI_RESET_STATUS_indication, 0, app);
		
	} else {
		struct uniapi_reset_error_indication *resp;
		struct uni_msg *app;

		resp = ALLOC_API(struct uniapi_reset_error_indication, app);
		if (resp != NULL) {
			resp->source = 1;
			resp->reason = UNIAPI_RESET_ERROR_PEER_INCOMP_STATE,

			uni->funcs->uni_output(uni, uni->arg,
			    UNIAPI_RESET_ERROR_indication, 0, app);
		}
	}
}

/*
 * T317 timeout function
 */
static void
t317_func(struct uni *uni)
{
	uni_enq_resp(uni, SIGR_T317, 0, NULL, NULL);
}
