/*
 * 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/netnatm/sig/sig_uni.c,v 1.11 2004/08/05 07:11:03 brandt Exp $
 *
 * Instance handling
 */

#include <netnatm/unimsg.h>
#include <netnatm/saal/sscopdef.h>
#include <netnatm/saal/sscfudef.h>
#include <netnatm/msg/unistruct.h>
#include <netnatm/msg/unimsglib.h>
#include <netnatm/sig/uni.h>
#include <netnatm/sig/unisig.h>

#include <netnatm/sig/unipriv.h>

#ifdef UNICORE
UNICORE
#endif

#define STR(S) [S] = #S
static const char *custat_names[] = {
	STR(CU_STAT0),
	STR(CU_STAT1),
	STR(CU_STAT2),
	STR(CU_STAT3),
};
static const char *globstat_names[] = {
	STR(UNI_CALLSTATE_REST0),
	STR(UNI_CALLSTATE_REST1),
	STR(UNI_CALLSTATE_REST2),
};

static const char *sig_names[] = {
	STR(UNIAPI_ERROR),
	STR(UNIAPI_CALL_CREATED),
	STR(UNIAPI_CALL_DESTROYED),
	STR(UNIAPI_PARTY_CREATED),
	STR(UNIAPI_PARTY_DESTROYED),
	STR(UNIAPI_LINK_ESTABLISH_request),
	STR(UNIAPI_LINK_ESTABLISH_confirm),
	STR(UNIAPI_LINK_RELEASE_request),
	STR(UNIAPI_LINK_RELEASE_confirm),
	STR(UNIAPI_RESET_request),
	STR(UNIAPI_RESET_confirm),
	STR(UNIAPI_RESET_indication),
	STR(UNIAPI_RESET_ERROR_indication),
	STR(UNIAPI_RESET_response),
	STR(UNIAPI_RESET_ERROR_response),
	STR(UNIAPI_RESET_STATUS_indication),
	STR(UNIAPI_SETUP_request),
	STR(UNIAPI_SETUP_indication),
	STR(UNIAPI_SETUP_response),
	STR(UNIAPI_SETUP_confirm),
	STR(UNIAPI_SETUP_COMPLETE_indication),
	STR(UNIAPI_SETUP_COMPLETE_request),
	STR(UNIAPI_ALERTING_request),
	STR(UNIAPI_ALERTING_indication),
	STR(UNIAPI_PROCEEDING_request),
	STR(UNIAPI_PROCEEDING_indication),
	STR(UNIAPI_RELEASE_request),
	STR(UNIAPI_RELEASE_indication),
	STR(UNIAPI_RELEASE_response),
	STR(UNIAPI_RELEASE_confirm),
	STR(UNIAPI_NOTIFY_request),
	STR(UNIAPI_NOTIFY_indication),
	STR(UNIAPI_STATUS_indication),
	STR(UNIAPI_STATUS_ENQUIRY_request),
	STR(UNIAPI_ADD_PARTY_request),
	STR(UNIAPI_ADD_PARTY_indication),
	STR(UNIAPI_PARTY_ALERTING_request),
	STR(UNIAPI_PARTY_ALERTING_indication),
	STR(UNIAPI_ADD_PARTY_ACK_request),
	STR(UNIAPI_ADD_PARTY_ACK_indication),
	STR(UNIAPI_ADD_PARTY_REJ_request),
	STR(UNIAPI_ADD_PARTY_REJ_indication),
	STR(UNIAPI_DROP_PARTY_request),
	STR(UNIAPI_DROP_PARTY_indication),
	STR(UNIAPI_DROP_PARTY_ACK_request),
	STR(UNIAPI_DROP_PARTY_ACK_indication),
	STR(UNIAPI_ABORT_CALL_request),
};

static const char *verb_names[] = {
# define UNI_DEBUG_DEFINE(D) [UNI_FAC_##D] #D,
	UNI_DEBUG_FACILITIES
# undef UNI_DEBUG_DEFINE
};

const char *
uni_facname(enum uni_verb fac)
{
	static char buf[40];

	if (fac >= UNI_MAXFACILITY) {
		sprintf(buf, "FAC%u", fac);
		return (buf);
	}
	return (verb_names[fac]);
}

const char *
uni_signame(enum uni_sig sig)
{
	static char buf[40];

	if (sig >= UNIAPI_MAXSIG) {
		sprintf(buf, "UNIAPI_SIG%u", sig);
		return (buf);
	}
	return (sig_names[sig]);
}

struct unicx *
uni_context(struct uni *uni)
{
	return (&uni->cx);
}

static void
uni_init(struct uni *uni)
{
	uni->working = 0;
	uni->cref_alloc = 12;
	uni->custat = CU_STAT0;
	uni->glob_start = UNI_CALLSTATE_REST0;
	uni->glob_respond = UNI_CALLSTATE_REST0;
}

static void
uni_stop(struct uni *uni)
{
	struct call *c;

	while ((c = TAILQ_FIRST(&uni->calls)) != NULL) {
		TAILQ_REMOVE(&uni->calls, c, link);
		uni_destroy_call(c, 1);
	}

	SIGQ_CLEAR(&uni->workq);
	SIGQ_CLEAR(&uni->delq);
}

/*
 * INSTANCE HANDLING
 */
struct uni *
uni_create(void *arg, const struct uni_funcs *funcs)
{
	struct uni *uni;

	if ((uni = INS_ALLOC()) == NULL)
		return (NULL);

	uni_init(uni);

	uni->funcs = funcs;
	uni->arg = arg;
	uni->proto = UNIPROTO_UNI40U;
	uni->sb_tb = 0;
	TAILQ_INIT(&uni->workq);
	TAILQ_INIT(&uni->delq);
	TIMER_INIT_UNI(uni, t309);
	uni->timer309 = UNI_T309_DEFAULT;
	TAILQ_INIT(&uni->calls);
	uni_initcx(&uni->cx);
	TIMER_INIT_UNI(uni, t317);
	TIMER_INIT_UNI(uni, t316);

	uni->timer301 = UNI_T301_DEFAULT;
	uni->init303 = UNI_T303_CNT_DEFAULT;
	uni->timer303 = UNI_T303_DEFAULT;
	uni->init308 = UNI_T308_CNT_DEFAULT;
	uni->timer308 = UNI_T308_DEFAULT;
	uni->timer310 = UNI_T310U_DEFAULT;
	uni->timer313 = UNI_T313_DEFAULT;
	uni->init316 = UNI_T316_CNT_DEFAULT;
	uni->timer316 = UNI_T316_DEFAULT;
	uni->timer317 = UNI_T317_DEFAULT;
	uni->timer322 = UNI_T322_DEFAULT;
	uni->init322 = UNI_T322_CNT_DEFAULT;
	uni->timer397 = UNI_T397_DEFAULT;
	uni->timer398 = UNI_T398_DEFAULT;
	uni->timer399 = UNI_T399U_DEFAULT;

	return (uni);
}

void 
uni_destroy(struct uni *uni)
{
	uni_stop(uni);

	TIMER_DESTROY_UNI(uni, t309);
	TIMER_DESTROY_UNI(uni, t316);
	TIMER_DESTROY_UNI(uni, t317);

	INS_FREE(uni);
}

void
uni_reset(struct uni *uni)
{
	uni_stop(uni);
	uni_init(uni);
}


/*
 * DISPATCH SSCOP SIGNAL
 */
void
uni_saal_input(struct uni *uni, enum saal_sig sig, struct uni_msg *m)
{
	switch (sig) {

	  case SAAL_ESTABLISH_indication:
		if (m != NULL)
			uni_msg_destroy(m);
		uni_enq_coord(uni, SIGO_SAAL_ESTABLISH_indication, 0, NULL);
		break;

	  case SAAL_ESTABLISH_confirm:
		if (m != NULL)
			uni_msg_destroy(m);
		uni_enq_coord(uni, SIGO_SAAL_ESTABLISH_confirm, 0, NULL);
		break;

	  case SAAL_RELEASE_confirm:
		if (m != NULL)
			uni_msg_destroy(m);
		uni_enq_coord(uni, SIGO_SAAL_RELEASE_confirm, 0, NULL);
		break;

	  case SAAL_RELEASE_indication:
		if (m != NULL)
			uni_msg_destroy(m);
		uni_enq_coord(uni, SIGO_SAAL_RELEASE_indication, 0, NULL);
		break;

	  case SAAL_DATA_indication:
		uni_enq_coord(uni, SIGO_SAAL_DATA_indication, 0, m);
		break;

	  case SAAL_UDATA_indication:
		uni_enq_coord(uni, SIGO_SAAL_UDATA_indication, 0, m);
		break;

	  default:
		VERBOSE(uni, UNI_FAC_ERR, 1, "bogus saal signal %u", sig);
		if (m != NULL)
			uni_msg_destroy(m);
		break;
	}
}

static struct {
	const char	*name;
	enum uni_sig	sig;
	size_t		arglen;
	u_int		coord_sig;
	u_int		proto;
#define UNIU	0x01
#define UNIN	0x02
#define PNNI	0x04
} maptab[] = {
	{ "LINK-ESTABLISH.request", UNIAPI_LINK_ESTABLISH_request,
	    0,
	    SIGO_LINK_ESTABLISH_request, UNIU | UNIN },
	{ "LINK-RELEASE.request", UNIAPI_LINK_RELEASE_request,
	    0,
	    SIGO_LINK_RELEASE_request, UNIU | UNIN },

	{ "RESET.request", UNIAPI_RESET_request,
	    sizeof(struct uniapi_reset_request),
	    SIGO_RESET_request, UNIU | UNIN },
	{ "RESET-ERROR.response", UNIAPI_RESET_ERROR_response,
	    sizeof(struct uniapi_reset_error_response),
	    SIGO_RESET_ERROR_response, UNIU | UNIN },
	{ "RESET.response", UNIAPI_RESET_response,
	    sizeof(struct uniapi_reset_response),
	    SIGO_RESET_response, UNIU | UNIN },

	{ "SETUP.request", UNIAPI_SETUP_request,
	    sizeof(struct uniapi_setup_request),
	    SIGO_SETUP_request, UNIU | UNIN },
	{ "SETUP.response", UNIAPI_SETUP_response,
	    sizeof(struct uniapi_setup_response),
	    SIGO_SETUP_response, UNIU | UNIN },
	{ "SETUP-COMPLETE.request", UNIAPI_SETUP_COMPLETE_request,
	    sizeof(struct uniapi_setup_complete_request),
	    SIGO_SETUP_COMPLETE_request, UNIN },
	{ "PROCEEDING.request", UNIAPI_PROCEEDING_request,
	    sizeof(struct uniapi_proceeding_request),
	    SIGO_PROCEEDING_request, UNIU | UNIN },
	{ "ALERTING.request", UNIAPI_ALERTING_request,
	    sizeof(struct uniapi_alerting_request),
	    SIGO_ALERTING_request, UNIU | UNIN },
	{ "RELEASE.request", UNIAPI_RELEASE_request,
	    sizeof(struct uniapi_release_request),
	    SIGO_RELEASE_request, UNIU | UNIN },
	{ "RELEASE.response", UNIAPI_RELEASE_response,
	    sizeof(struct uniapi_release_response),
	    SIGO_RELEASE_response, UNIU | UNIN },
	{ "NOTIFY.request", UNIAPI_NOTIFY_request,
	    sizeof(struct uniapi_notify_request),
	    SIGO_NOTIFY_request, UNIU | UNIN },
	{ "STATUS-ENQUIRY.request", UNIAPI_STATUS_ENQUIRY_request,
	    sizeof(struct uniapi_status_enquiry_request),
	    SIGO_STATUS_ENQUIRY_request, UNIU | UNIN },

	{ "ADD-PARTY.request", UNIAPI_ADD_PARTY_request,
	    sizeof(struct uniapi_add_party_request),
	    SIGO_ADD_PARTY_request, UNIU | UNIN },
	{ "ADD-PARTY-ACK.request", UNIAPI_ADD_PARTY_ACK_request,
	    sizeof(struct uniapi_add_party_ack_request),
	    SIGO_ADD_PARTY_ACK_request, UNIU | UNIN },
	{ "ADD-PARTY-REJ.request", UNIAPI_ADD_PARTY_REJ_request,
	    sizeof(struct uniapi_add_party_rej_request),
	    SIGO_ADD_PARTY_REJ_request, UNIU | UNIN },
	{ "PARTY-ALERTING.request", UNIAPI_PARTY_ALERTING_request,
	    sizeof(struct uniapi_party_alerting_request),
	    SIGO_PARTY_ALERTING_request, UNIU | UNIN },
	{ "DROP-PARTY.request", UNIAPI_DROP_PARTY_request,
	    sizeof(struct uniapi_drop_party_request),
	    SIGO_DROP_PARTY_request, UNIU | UNIN },
	{ "DROP-PARTY-ACK.request", UNIAPI_DROP_PARTY_ACK_request,
	    sizeof(struct uniapi_drop_party_ack_request),
	    SIGO_DROP_PARTY_ACK_request, UNIU | UNIN },

	{ "ABORT-CALL.request", UNIAPI_ABORT_CALL_request,
	    sizeof(struct uniapi_abort_call_request),
	    SIGO_ABORT_CALL_request, UNIU | UNIN },

	{ NULL, 0, 0, 0, 0 }
};

void
uni_uni_input(struct uni *uni, enum uni_sig sig, uint32_t cookie,
    struct uni_msg *m)
{
	u_int i;

	for (i = 0; maptab[i].name != NULL; i++) {
		if (maptab[i].sig == sig) {
			if (uni->proto == UNIPROTO_UNI40U) {
				if (!(maptab[i].proto & UNIU))
					uniapi_uni_error(uni,
					    UNIAPI_ERROR_BAD_SIGNAL, cookie, 0);
			} else if(uni->proto == UNIPROTO_UNI40N) {
				if (!(maptab[i].proto & UNIN))
					uniapi_uni_error(uni,
					    UNIAPI_ERROR_BAD_SIGNAL, cookie, 0);
			} else if(uni->proto == UNIPROTO_PNNI10) {
				if (!(maptab[i].proto & PNNI))
					uniapi_uni_error(uni,
					    UNIAPI_ERROR_BAD_SIGNAL, cookie, 0);
			} else {
				uniapi_uni_error(uni,
				    UNIAPI_ERROR_BAD_SIGNAL, cookie, 0);
			}
			if (uni_msg_len(m) != maptab[i].arglen) {
				VERBOSE(uni, UNI_FAC_ERR, 1, "bogus data in %s"
				    " (expecting %zu, got %zu)", maptab[i].name,
				    maptab[i].arglen, uni_msg_len(m));
				uni_msg_destroy(m);
				uniapi_uni_error(uni, UNIAPI_ERROR_BAD_ARG,
				    cookie, 0);
				return;
			}
			if (maptab[i].arglen == 0) {
				uni_msg_destroy(m);
				m = NULL;
			}
			VERBOSE(uni, UNI_FAC_API, 1, "got signal %s - "
			    "delivering to Coord", maptab[i].name);
			uni_enq_coord(uni, maptab[i].coord_sig, cookie, m);
			return;
		}
	}
	VERBOSE(uni, UNI_FAC_ERR, 1, "bogus uni signal %u", sig);
	uni_msg_destroy(m);
	uniapi_uni_error(uni, UNIAPI_ERROR_BAD_SIGNAL, cookie, 0);
}
#undef UNIU
#undef UNIN
#undef PNNI

/**************************************************************/

void
uni_work(struct uni *uni)
{
	struct sig *s;

	if (uni->working)
		return;
	uni->working = 1;

	while ((s = TAILQ_FIRST(&uni->workq)) != NULL) {
		TAILQ_REMOVE(&uni->workq, s, link);
		switch (s->type) {

		  case SIG_COORD:
			uni_sig_coord(uni, s->sig, s->cookie, s->msg);
			break;

		  case SIG_RESET_START:
			uni_sig_start(uni, s->sig, s->cookie, s->msg, s->u);
			break;

		  case SIG_RESET_RESP:
			uni_sig_respond(uni, s->sig, s->cookie, s->msg, s->u);
			break;

		  case SIG_CALL:
			uni_sig_call(s->call, s->sig, s->cookie, s->msg, s->u);
			break;

		  case SIG_PARTY:
			uni_sig_party(s->party, s->sig, s->cookie, s->msg, s->u);
			break;

		  default:
			ASSERT(0, ("bad signal type"));
		}
		SIG_FREE(s);
	}

	uni->working = 0;
}

/*
 * Enqueue a signal in the working queue
 */
void
uni_enq_sig(struct uni *uni, u_int type, struct call *call,
    struct party *party, uint32_t sig, uint32_t cookie,
    struct uni_msg *msg, struct uni_all *u)
{
	struct sig *s;

	if ((s = SIG_ALLOC()) != NULL) {
		s->type = type;
		s->sig = sig;
		s->cookie = cookie;
		s->msg = msg;
		s->call = call;
		s->party = party;
		s->u = u;
		TAILQ_INSERT_TAIL(&uni->workq, s, link);
	}
}

/*
 * Enqueue a signal in the delayed queue
 */
void
uni_delenq_sig(struct uni *uni, u_int type, struct call *call,
    struct party *party, uint32_t sig, uint32_t cookie,
    struct uni_msg *msg, struct uni_all *u)
{
	struct sig *s;

	if ((s = SIG_ALLOC()) != NULL) {
		s->type = type;
		s->sig = sig;
		s->cookie = cookie;
		s->msg = msg;
		s->call = call;
		s->party = party;
		s->u = u;
		TAILQ_INSERT_TAIL(&uni->delq, s, link);
	}
}

/**************************************************************/

void
uniapi_uni_error(struct uni *uni, uint32_t reason, uint32_t cookie,
    uint32_t state)
{
	struct uni_msg *resp;
	struct uniapi_error *err;

	if (cookie == 0)
		return;

	resp = uni_msg_alloc(sizeof(struct uniapi_error));
	err = uni_msg_wptr(resp, struct uniapi_error *);
	resp->b_wptr += sizeof(struct uniapi_error);

	err->reason = reason;
	err->state = state;

	uni->funcs->uni_output(uni, uni->arg, UNIAPI_ERROR, cookie, resp);
}

void
uniapi_call_error(struct call *c, uint32_t reason, uint32_t cookie)
{
	uniapi_uni_error(c->uni, reason, cookie, callstates[c->cstate].ext);
}
void
uniapi_party_error(struct party *p, uint32_t reason, uint32_t cookie)
{
	uniapi_uni_error(p->call->uni, reason, cookie,
	    callstates[p->call->cstate].ext);
}

/**************************************************************/
void
uni_status(struct uni *uni, void *arg)
{
	uni->funcs->status(uni, uni->arg, arg,
	    "working: %s\n", uni->working ? "yes" : "no");
	uni->funcs->status(uni, uni->arg, arg,
	    "work queue: %sempty\n", TAILQ_EMPTY(&uni->workq)? "" : "not ");
	uni->funcs->status(uni, uni->arg, arg,
	    "delayed work queue: %sempty\n",
	    TAILQ_EMPTY(&uni->delq)? "" : "not ");
	uni->funcs->status(uni, uni->arg, arg,
	    "coordinator: %s\n", custat_names[uni->custat]);
	uni->funcs->status(uni, uni->arg, arg,
	    "reset-start: %s\n", globstat_names[uni->glob_start]);
	uni->funcs->status(uni, uni->arg, arg,
	    "reset-respond: %s\n", globstat_names[uni->glob_respond]);
}

void
uni_undel(struct uni *uni, int (*filter)(struct sig *, void *), void *arg)
{
	struct sigqueue		newq;
	struct sig *s, *s1;

	if (TAILQ_EMPTY(&uni->delq))
		return;

	TAILQ_INIT(&newq);

	s = TAILQ_FIRST(&uni->delq);
	while (s != NULL) {
		s1 = TAILQ_NEXT(s, link);
		if ((*filter)(s, arg)) {
			TAILQ_REMOVE(&uni->delq, s, link);
			TAILQ_INSERT_TAIL(&uni->workq, s, link);
		}
		s = s1;
	}
}

void
uni_delsig(struct uni *uni, u_int type, struct call *c, struct party *p)
{
	struct sig *s, *s1;

	s = TAILQ_FIRST(&uni->workq);
	while (s != NULL) {
		s1 = TAILQ_NEXT(s, link);
		if ((type == SIG_CALL && s->type == SIG_CALL &&
		    s->call == c) ||
		    (type == SIG_PARTY && s->type == SIG_PARTY &&
		    s->call == c && s->party == p)) {
			TAILQ_REMOVE(&uni->workq, s, link);
			if (s->msg)
				uni_msg_destroy(s->msg);
			if (s->u)
				UNI_FREE(s->u);
			SIG_FREE(s);
		}
		s = s1;
	}

	s = TAILQ_FIRST(&uni->delq);
	while (s != NULL) {
		s1 = TAILQ_NEXT(s, link);
		if ((type == SIG_CALL && s->type == SIG_CALL &&
		    s->call == c) ||
		    (type == SIG_PARTY && s->type == SIG_PARTY &&
		    s->call == c && s->party == p)) {
			TAILQ_REMOVE(&uni->delq, s, link);
			if (s->msg)
				uni_msg_destroy(s->msg);
			if (s->u)
				UNI_FREE(s->u);
			SIG_FREE(s);						\
		}
		s = s1;
	}
}

/**************************************************************/

void
uni_get_config(const struct uni *uni, struct uni_config *config)
{
	config->proto = uni->proto;

	config->popt = 0;
	if (uni->cx.q2932)
		config->popt |= UNIPROTO_GFP;

	config->option = 0;
	if (uni->cx.git_hard)
		config->option |= UNIOPT_GIT_HARD;
	if (uni->cx.bearer_hard)
		config->option |= UNIOPT_BEARER_HARD;
	if (uni->cx.cause_hard)
		config->option |= UNIOPT_CAUSE_HARD;
	if (uni->sb_tb)
		config->popt |= UNIPROTO_SB_TB;

	config->timer301 = uni->timer301;
	config->timer303 = uni->timer303;
	config->init303 = uni->init303;
	config->timer308 = uni->timer308;
	config->init308 = uni->init308;
	config->timer309 = uni->timer309;
	config->timer310 = uni->timer310;
	config->timer313 = uni->timer313;
	config->timer316 = uni->timer316;
	config->init316 = uni->init316;
	config->timer317 = uni->timer317;
	config->timer322 = uni->timer322;
	config->init322 = uni->init322;
	config->timer397 = uni->timer397;
	config->timer398 = uni->timer398;
	config->timer399 = uni->timer399;
}

void
uni_set_config(struct uni *uni, const struct uni_config *config,
    uint32_t *mask, uint32_t *popt_mask, uint32_t *opt_mask)
{
	int idle;

	idle = TAILQ_EMPTY(&uni->calls) &&
	    TAILQ_EMPTY(&uni->workq) &&
	    TAILQ_EMPTY(&uni->delq);

	if ((*mask & UNICFG_PROTO) && idle) {
		switch (config->proto) {

		  case UNIPROTO_UNI40U:
		  case UNIPROTO_UNI40N:
		  /* case UNIPROTO_PNNI10: XXX */
			uni->proto = config->proto;
			*mask &= ~UNICFG_PROTO;
			break;
		}
	}
	if (*popt_mask & UNIPROTO_GFP) {
		if (config->popt & UNIPROTO_GFP) {
			uni->cx.q2932 = 1;
			*popt_mask &= ~UNIPROTO_GFP;
		} else {
			if (!uni->cx.q2932 || idle) {
				uni->cx.q2932 = 0;
				*popt_mask &= ~UNIPROTO_GFP;
			}
		}
	}
	if (*popt_mask & UNIPROTO_SB_TB) {
		uni->sb_tb = ((config->popt & UNIPROTO_SB_TB) != 0);
		*popt_mask &= ~UNIPROTO_SB_TB;
	}
	if (*opt_mask & UNIOPT_GIT_HARD) {
		uni->cx.git_hard = ((config->option & UNIOPT_GIT_HARD) != 0);
		*opt_mask &= ~UNIOPT_GIT_HARD;
	}
	if (*opt_mask & UNIOPT_BEARER_HARD) {
		uni->cx.bearer_hard = ((config->option & UNIOPT_BEARER_HARD) != 0);
		*opt_mask &= ~UNIOPT_BEARER_HARD;
	}
	if (*opt_mask & UNIOPT_CAUSE_HARD) {
		uni->cx.cause_hard = ((config->option & UNIOPT_CAUSE_HARD) != 0);
		*opt_mask &= ~UNIOPT_CAUSE_HARD;
	}

#define SET_TIMER(NAME,name)						\
	if (*mask & UNICFG_##NAME) {					\
		uni->name = config->name;				\
		*mask &= ~UNICFG_##NAME;				\
	}

	SET_TIMER(TIMER301, timer301);
	SET_TIMER(TIMER303, timer303);
	SET_TIMER(INIT303,  init303);
	SET_TIMER(TIMER308, timer308);
	SET_TIMER(INIT308,  init308);
	SET_TIMER(TIMER309, timer309);
	SET_TIMER(TIMER310, timer310);
	SET_TIMER(TIMER313, timer313);
	SET_TIMER(TIMER316, timer316);
	SET_TIMER(INIT316,  init316);
	SET_TIMER(TIMER317, timer317);
	SET_TIMER(TIMER322, timer322);
	SET_TIMER(INIT322,  init322);
	SET_TIMER(TIMER397, timer397);
	SET_TIMER(TIMER398, timer398);
	SET_TIMER(TIMER399, timer399);

#undef SET_TIMER
}

void
uni_set_debug(struct uni *uni, enum uni_verb fac, u_int level)
{
	uni->debug[fac] = level;
}

u_int
uni_get_debug(const struct uni *uni, enum uni_verb fac)
{
	return (uni->debug[fac]);
}

u_int
uni_getcustate(const struct uni *uni)
{
	return (uni->custat);
}
