/*
 *
 * ===================================
 * HARP  |  Host ATM Research Platform
 * ===================================
 *
 *
 * This Host ATM Research Platform ("HARP") file (the "Software") is
 * made available by Network Computing Services, Inc. ("NetworkCS")
 * "AS IS".  NetworkCS does not provide maintenance, improvements or
 * support of any kind.
 *
 * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
 * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
 * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
 * In no event shall NetworkCS be responsible for any damages, including
 * but not limited to consequential damages, arising from or relating to
 * any use of the Software or related support.
 *
 * Copyright 1994-1998 Network Computing Services, Inc.
 *
 * Copies of this Software may be made, however, the above copyright
 * notice must be reproduced on all copies.
 *
 *	@(#) $FreeBSD: src/sys/netatm/uni/unisig_vc_state.c,v 1.6 2000/01/17 20:49:59 mks Exp $
 *
 */

/*
 * ATM Forum UNI 3.0/3.1 Signalling Manager
 * ----------------------------------------
 *
 * VC state machine
 *
 */

#include <netatm/kern_include.h>

#include <netatm/uni/unisig_var.h>
#include <netatm/uni/unisig_msg.h>

#ifndef lint
__RCSID("@(#) $FreeBSD: src/sys/netatm/uni/unisig_vc_state.c,v 1.6 2000/01/17 20:49:59 mks Exp $");
#endif


/*
 * Local functions
 */
static int	unisig_vc_invalid __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act01 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act02 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act03 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act04 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act05 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act06 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act07 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act08 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act09 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act10 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act11 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act12 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act13 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act14 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act15 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act16 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act17 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act18 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act19 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act20 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act21 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act22 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act23 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act24 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act25 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act26 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act27 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act28 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act29 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act30 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_act31 __P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *));
static int	unisig_vc_clear_call __P((struct unisig *,
			struct unisig_vccb *,
			struct unisig_msg *,
			int));


/*
 * State table
 */
static int	unisig_vc_states[21][17] = {
/* 0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16 */
{  0,  2, 99,  5, 99, 99,  0, 99, 12, 99,  0, 14,  0,  3,  0,  0,  0 },
{ 29,  4, 99, 17, 99, 99, 17, 99, 17, 99, 17, 17, 17,  0,  0,  0,  0 },
{ 29,  6, 99,  6, 99, 99, 17, 99, 17, 99, 17, 17, 17,  0,  0,  0,  0 },
{ 29, 17, 99, 17, 99, 99, 17, 99, 10, 99, 17, 17, 17,  0,  0,  0,  0 },
{  8, 17, 99, 17, 99, 99, 17, 99, 17, 99, 17, 17, 17,  0,  0,  0,  0 },
{ 29,  7, 99, 15, 99, 99, 15, 99, 15, 99, 15, 16, 17,  0,  0,  0,  0 },
{ 19,  3, 99,  3, 99, 99,  3, 99,  3, 99,  3, 13,  3,  0,  0,  0,  0 },
{ 21, 21, 99, 21, 99, 99, 21, 99, 21, 99, 21, 21, 21,  0,  0,  0,  0 },
{ 22, 22, 99, 22, 99, 99, 22, 99, 22, 99, 22, 22, 22,  0,  0,  0,  0 },
{ 29, 17, 99, 17, 99, 99, 17, 99, 17, 99, 23, 17, 17,  0,  0,  0,  0 },
{ 29, 17, 99, 17, 99, 99, 17, 99, 17, 99, 17, 17, 17,  0,  0,  0,  0 },
{ 29, 17, 99, 17, 99, 99, 17, 99, 17, 99, 17, 17, 17,  0,  0,  0,  0 },
{ 29, 17, 99, 17, 99, 99, 17, 99, 17, 99, 17, 17, 17,  0,  0,  0,  0 },
{ 29, 17, 99, 17, 99, 99, 17, 99, 17, 99, 17, 17, 17,  0,  0,  0,  0 },
{  1, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99 },
{ 99, 25, 99, 25, 99, 99,  9, 99, 25, 99, 25, 25, 25, 25, 31, 25, 25 },
{ 99, 25, 99, 25, 99, 99, 11, 99, 25, 99, 25, 25, 25, 25, 19, 25, 25 },
{ 99, 12, 99, 12, 99, 99, 25, 99, 12, 99, 12, 19, 19, 30, 19, 99, 99 },
{ 99, 12, 99, 12, 99, 99, 12, 99, 12, 99, 12,  3,  3,  3, 24, 26, 26 },
{ 99,  3, 99,  3, 99, 99, 30, 99,  3, 99, 18,  3,  3,  0, 19, 27, 19 },
{ 99,  7, 99,  7, 99, 99, 30, 99,  7, 99, 19, 19, 19, 20, 19, 19, 28 }
};


/*
 * Action vector
 *
 * A given state, action pair selects an action number from the
 * state table.  This vector holds the address of the action routine
 * for each action number.
 */
#define	MAX_ACTION	32
static int (*unisig_vc_act_vec[MAX_ACTION])
		__P((struct unisig *, struct unisig_vccb *,
			struct unisig_msg *)) = {
	unisig_vc_invalid,
	unisig_vc_act01,	
	unisig_vc_act02,
	unisig_vc_act03,
	unisig_vc_act04,
	unisig_vc_act05,
	unisig_vc_act06,
	unisig_vc_act07,
	unisig_vc_act08,
	unisig_vc_act09,
	unisig_vc_act10,
	unisig_vc_act11,
	unisig_vc_act12,
	unisig_vc_act13,
	unisig_vc_act14,
	unisig_vc_act15,
	unisig_vc_act16,
	unisig_vc_act17,
	unisig_vc_act18,
	unisig_vc_act19,
	unisig_vc_act20,
	unisig_vc_act21,
	unisig_vc_act22,
	unisig_vc_act23,
	unisig_vc_act24,
	unisig_vc_act25,
	unisig_vc_act26,
	unisig_vc_act27,
	unisig_vc_act28,
	unisig_vc_act29,
	unisig_vc_act30,
	unisig_vc_act31
};


/*
 * Process an event on a VC
 *
 * Arguments:
 *	usp	pointer to the UNISIG instance
 *	uvp	pointer to the VCCB for the affected VCC
 *	event	a numeric indication of which event has occured
 *	msg	pointer to a signalling message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
unisig_vc_state(usp, uvp, event, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	int			event;
	struct unisig_msg	*msg;
{
	int	action, rc, state;

	/*
	 * Select an action from the state table
	 */
	if (uvp)
		state = uvp->uv_sstate;
	else
		state = UNI_NULL;
	action = unisig_vc_states[event][state];
	if (action >= MAX_ACTION || action < 0)
		panic("unisig_vc_state: invalid action\n");

	/*
	 * Perform the requested action
	 */
	ATM_DEBUG4("unisig_vc_state: uvp=%p, state=%d, event=%d, action=%d\n",
			uvp, state, event, action);
	rc = unisig_vc_act_vec[action](usp, uvp, msg);

	return(rc);
}


/*
 * VC state machine action 0
 * Unexpected action - log an error message
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection (may
		be null)
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_invalid(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	log(LOG_ERR, "unisig_vc_state: unexpected action\n");
	return(EINVAL);
}


/*
 * VC state machine action 1
 * Setup handler called
 *
 * Send SETUP, start timer T303, go to UNI_CALL_INITIATED state
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act01(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int		rc;

	/*
	 * Send the setup message
	 */
	rc = unisig_send_setup(usp, uvp);
	if (rc)
		return(rc);

	/*
	 * Set timer T303
	 */
	uvp->uv_retry = 0;
	UNISIG_VC_TIMER((struct vccb *) uvp, UNI_T303);

	/*
	 * Set the new state
	 */
	uvp->uv_sstate = UNI_CALL_INITIATED;

	/*
	 * Mark the time
	 */
	uvp->uv_tstamp = time_second;

	return(0);
}


/*
 * VC state machine action 2
 * Timeout while waiting for CALL PROCEEDING or CONNECT
 *
 * If this is the second expiration, clear the call.  Otherwise,
 * retransmit the SETUP message and restart T303.
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act02(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int	rc = 0;

	if (uvp->uv_retry) {
		/*
		 * Clear the call
		 */
		rc = unisig_clear_vcc(usp, uvp,
				T_ATM_CAUSE_NO_ROUTE_TO_DESTINATION);
	} else {
		uvp->uv_retry++;
		(void) unisig_send_setup(usp, uvp);
		UNISIG_VC_TIMER((struct vccb *) uvp, UNI_T303);
	}
	
	return(rc);
}


/*
 * VC state machine action 3
 *
 * Clear the call internally
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act03(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int	rc, cause;

	/*
	 * Set cause code
	 */
	if ((msg != NULL) && (msg->msg_ie_caus != NULL)) {
		unisig_cause_attr_from_ie(&uvp->uv_connvc->cvc_attr,
			msg->msg_ie_caus);
		cause = T_ATM_ABSENT;
	} else
		cause = T_ATM_CAUSE_DESTINATION_OUT_OF_ORDER;

	/*
	 * Clear the VCCB
	 */
	rc = unisig_clear_vcc(usp, uvp, cause);

	return(rc);
}


/*
 * VC state machine action 4
 * Received CALL PROCEEDING
 *
 * Start timer T310, go to UNI_CALL_OUT_PROC
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act04(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int			cause, rc, vpi, vci;
	struct atm_pif		*pip = usp->us_pif;
	struct ie_generic	*iep;

	/*
	 * Clear any running timer
	 */
	UNISIG_VC_CANCEL((struct vccb *) uvp);

	/*
	 * Make sure a Connection ID is part of the message
	 */
	if (msg->msg_ie_cnid) {
		vpi = msg->msg_ie_cnid->ie_cnid_vpci;
		vci = msg->msg_ie_cnid->ie_cnid_vci;
	} else {
		iep = (struct ie_generic *)atm_allocate(&unisig_iepool);
		if (!iep)
			return(ENOMEM);
		iep->ie_ident = UNI_IE_CNID;
		iep->ie_err_cause = UNI_IE_CAUS_MISSING;
		MSG_IE_ADD(msg, iep, UNI_MSG_IE_ERR);
		cause = UNI_IE_CAUS_MISSING;
		ATM_DEBUG0("unisig_vc_act04: no CNID in Call Proc\n");
		goto response04;
	}

	/*
	 * Make sure we can handle the specified VPI and VCI
	 */
	if (vpi > pip->pif_maxvpi  || vci > pip->pif_maxvci ||
			vci < UNI_IE_CNID_MIN_VCI) {
		cause = UNI_IE_CAUS_BAD_VCC;
		ATM_DEBUG0("unisig_vc_act04: VPI/VCI invalid\n");
		goto response04;
	}

	/*
	 * Make sure the specified VPI and VCI are not in use
	 */
	if (unisig_find_vpvc(usp, vpi, vci, VCC_OUT)) {
		cause = UNI_IE_CAUS_NA_VCC;
		ATM_DEBUG0("unisig_vc_act04: VPI/VCI in use\n");
		goto response04;
	}

	/*
	 * Start timer T310
	 */
	UNISIG_VC_TIMER((struct vccb *) uvp, UNI_T310);

	/*
	 * Save the specified VPI and VCI
	 */
	uvp->uv_vpi = vpi;
	uvp->uv_vci = vci;

	/*
	 * Set the state
	 */
	uvp->uv_sstate = UNI_CALL_OUT_PROC;

	/*
	 * Mark the time
	 */
	uvp->uv_tstamp = time_second;

	return(0);

response04:
	/*
	 * Initiate call clearing
	 */
	rc = unisig_vc_clear_call(usp, uvp, msg, cause);

	return(rc);
}


/*
 * VC state machine action 5
 * Timeout in UNI_CALL_OUT_PROC
 * 
 * Clear call towards network
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act05(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int			rc;
	struct unisig_msg	*rls_msg;
	struct ie_generic	*cause_ie;

	/*
	 * Send a RELEASE message
	 */
	rls_msg = (struct unisig_msg *) atm_allocate(&unisig_msgpool);
	if (rls_msg == NULL)
		return(ENOMEM);
	cause_ie = (struct ie_generic *) atm_allocate(&unisig_iepool);
	if (cause_ie == NULL) {
		atm_free(rls_msg);
		return(ENOMEM);
	}

	/*
	 * Fill out the RELEASE message
	 */
	rls_msg->msg_call_ref = uvp->uv_call_ref;
	rls_msg->msg_type = UNI_MSG_RLSE;
	rls_msg->msg_type_flag = 0;
	rls_msg->msg_type_action = 0;
	rls_msg->msg_ie_caus = cause_ie;

	/*
	 * Fill out the cause IE
	 */
	cause_ie->ie_caus_loc = UNI_IE_CAUS_LOC_USER;
	cause_ie->ie_caus_cause = UNI_IE_CAUS_TIMER;
	KM_COPY("310", cause_ie->ie_caus_diagnostic, 3);

	/*
	 * Send the RELEASE message.
	 */
	rc = unisig_send_msg(usp, rls_msg);
	unisig_free_msg(rls_msg);

	/*
	 * Start timer T308
	 */
	UNISIG_VC_TIMER((struct vccb *) uvp, UNI_T308);

	/*
	 * Set the new state
	 */
	uvp->uv_sstate = UNI_RELEASE_REQUEST;
	uvp->uv_ustate = VCCU_CLOSED;

	/*
	 * Mark the time
	 */
	uvp->uv_tstamp = time_second;

	return(rc);
}


/*
 * VC state machine action 6
 * Received CONNECT
 *
 * Send CONNECT ACK, go to UNI_ACTIVE state
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act06(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int			cause, rc, vci, vpi;
	struct atm_pif		*pip = usp->us_pif;
	struct unisig_msg	*cack_msg;
	struct ie_generic	*iep;
	Atm_attributes		*ap;

	/*
	 * Clear any running timer
	 */
	UNISIG_VC_CANCEL((struct vccb *) uvp);

	ap = &uvp->uv_connvc->cvc_attr;

	/*
	 * See if a VPI/VCI is specified
	 */
	if (msg->msg_ie_cnid) {
		/*
		 * Yes--VPI/VCI must be the first specification or must
		 * match what was specified before
		 */
		vpi = msg->msg_ie_cnid->ie_cnid_vpci;
		vci = msg->msg_ie_cnid->ie_cnid_vci;
		if ((uvp->uv_vpi || uvp->uv_vci) &&
				(vpi != uvp->uv_vpi ||
				vci != uvp->uv_vci)) {
			cause = UNI_IE_CAUS_BAD_VCC;
			ATM_DEBUG0("unisig_vc_act06: VPI/VCI invalid\n");
			goto response06;
		}

		/*
		 * Specified VPI/VCI must be within range
		 */
		if (vpi > pip->pif_maxvpi || vci > pip->pif_maxvci ||
				vci < UNI_IE_CNID_MIN_VCI) {
			cause = UNI_IE_CAUS_BAD_VCC;
			ATM_DEBUG0("unisig_vc_act06: VPI/VCI invalid\n");
			goto response06;
		}
		uvp->uv_vpi = vpi;
		uvp->uv_vci = vci;
	} else {
		/*
		 * No--VPI/VCI must have been specified earlier
		 */
		if (!uvp->uv_vpi || !uvp->uv_vci) {
			iep = (struct ie_generic *)atm_allocate(
					&unisig_iepool);
			if (!iep)
				return(ENOMEM);
			iep->ie_ident = UNI_IE_CNID;
			iep->ie_err_cause = UNI_IE_CAUS_MISSING;
			MSG_IE_ADD(msg, iep, UNI_MSG_IE_ERR);
			cause = UNI_IE_CAUS_MISSING;
			ATM_DEBUG0("unisig_vc_act06: CNID missing\n");
			goto response06;
		}
	}

	/*
	 * Handle AAL parameters negotiation
	 */
	if (msg->msg_ie_aalp) {
		struct ie_generic	*aalp = msg->msg_ie_aalp;

		/*
		 * AAL parameters must have been sent in SETUP
		 */
		if ((ap->aal.tag != T_ATM_PRESENT) ||
		    (ap->aal.type != aalp->ie_aalp_aal_type)) {
			cause = UNI_IE_CAUS_IECONTENT;
			goto response06;
		}

		switch (aalp->ie_aalp_aal_type) {

		case UNI_IE_AALP_AT_AAL3:
			/*
			 * Maximum SDU size negotiation
			 */
			if (aalp->ie_aalp_4_fwd_max_sdu == T_ATM_ABSENT)
				break;
			if ((ap->aal.v.aal4.forward_max_SDU_size < 
					aalp->ie_aalp_4_fwd_max_sdu) ||
			    (ap->aal.v.aal4.backward_max_SDU_size <
					aalp->ie_aalp_4_bkwd_max_sdu)) {
				cause = UNI_IE_CAUS_IECONTENT;
				goto response06;
			} else {
				ap->aal.v.aal4.forward_max_SDU_size =
					aalp->ie_aalp_4_fwd_max_sdu;
				ap->aal.v.aal4.backward_max_SDU_size =
					aalp->ie_aalp_4_bkwd_max_sdu;
			}
			break;

		case UNI_IE_AALP_AT_AAL5:
			/*
			 * Maximum SDU size negotiation
			 */
			if (aalp->ie_aalp_5_fwd_max_sdu == T_ATM_ABSENT)
				break;
			if ((ap->aal.v.aal5.forward_max_SDU_size < 
					aalp->ie_aalp_5_fwd_max_sdu) ||
			    (ap->aal.v.aal5.backward_max_SDU_size <
					aalp->ie_aalp_5_bkwd_max_sdu)) {
				cause = UNI_IE_CAUS_IECONTENT;
				goto response06;
			} else {
				ap->aal.v.aal5.forward_max_SDU_size =
					aalp->ie_aalp_5_fwd_max_sdu;
				ap->aal.v.aal5.backward_max_SDU_size =
					aalp->ie_aalp_5_bkwd_max_sdu;
			}
			break;
		}
	}

	/*
	 * Get memory for a CONNECT ACK message
	 */
	cack_msg = (struct unisig_msg *) atm_allocate(&unisig_msgpool);
	if (cack_msg == NULL)
		return(ENOMEM);

	/*
	 * Fill out the CONNECT ACK message
	 */
	cack_msg->msg_call_ref = uvp->uv_call_ref;
	cack_msg->msg_type = UNI_MSG_CACK;
	cack_msg->msg_type_flag = 0;
	cack_msg->msg_type_action = 0;

	/*
	 * Send the CONNECT ACK message
	 */
	rc = unisig_send_msg(usp, cack_msg);
	unisig_free_msg(cack_msg);

	/*
	 * Set the new state
	 */
	uvp->uv_sstate = UNI_ACTIVE;
	uvp->uv_ustate = VCCU_OPEN;

	/*
	 * Mark the time
	 */
	uvp->uv_tstamp = time_second;

	/*
	 * Notify the user that the connection is now active
	 */
	atm_cm_connected(uvp->uv_connvc);

	return(0);

response06:
	/*
	 * Initiate call clearing
	 */
	rc = unisig_vc_clear_call(usp, uvp, msg, cause);

	return(rc);
}


/*
 * VC state machine action 7
 * Abort routine called or signalling SAAL session reset while in
 * one of the call setup states
 *
 * Clear the call, send RELEASE COMPLETE, notify the user.
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act07(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int			rc;

	/*
	 * Clear any running timer
	 */
	UNISIG_VC_CANCEL((struct vccb *) uvp);

	/*
	 * Send a RELEASE COMPLETE message rejecting the connection
	 */
	rc = unisig_send_release_complete(usp, uvp, msg,
			UNI_IE_CAUS_TEMP);

	/*
	 * Clear the call VCCB
	 */
	uvp->uv_sstate = UNI_FREE;
	uvp->uv_ustate = VCCU_CLOSED;

	/*
	 * Mark the time
	 */
	uvp->uv_tstamp = time_second;

	/*
	 * Notify the user
	 */
	if ((msg != NULL) && (msg->msg_ie_caus != NULL))
		unisig_cause_attr_from_ie(&uvp->uv_connvc->cvc_attr,
			msg->msg_ie_caus);
	else
		unisig_cause_attr_from_user(&uvp->uv_connvc->cvc_attr,
			T_ATM_CAUSE_NORMAL_CALL_CLEARING);
	atm_cm_cleared(uvp->uv_connvc);

	return(rc);
}


/*
 * VC state machine action 8
 * Received SETUP
 *
 * Check call paramaters, notify user that a call has been received,
 * set UNI_CALL_PRESENT state
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act08(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int			cause = 0, rc, vpi, vci;
	struct atm_pif		*pip = usp->us_pif;
	struct atm_nif		*nip;
	Atm_addr_nsap		*nap;
	Atm_attributes		attr;

	ATM_DEBUG3("unisig_vc_act08: usp=%p, uvp=%p, msg=%p\n",
			usp, uvp, msg);

	/*
	 * Make sure that the called address is the right format
	 */
	if (msg->msg_ie_cdad->ie_cdad_plan != UNI_IE_CDAD_PLAN_NSAP) {
		cause = UNI_IE_CAUS_IECONTENT;
		ATM_DEBUG0("unisig_vc_act08: bad address format\n");
		goto response08;
	}

	/*
	 * Make sure that the called address is ours
	 */
	nap = (Atm_addr_nsap *) msg->msg_ie_cdad->ie_cdad_addr.address;
	if (bcmp(usp->us_addr.address, nap,	/* XXX */
			sizeof(Atm_addr_nsap)-1)) {
		cause = UNI_IE_CAUS_IECONTENT;
		ATM_DEBUG0("unisig_vc_act08: address not mine\n");
		goto response08;
	}

	/*
	 * Find the right NIF for the given selector byte
	 */
	nip = pip->pif_nif;
	while (nip && nip->nif_sel != nap->aan_sel) {
		nip = nip->nif_pnext;
	}
	if (!nip) {
		cause = UNI_IE_CAUS_IECONTENT;
		ATM_DEBUG0("unisig_vc_act08: bad selector byte\n");
		goto response08;
	}

	/*
	 * See if we recognize the specified AAL
	 */
	if (msg->msg_ie_aalp->ie_aalp_aal_type != UNI_IE_AALP_AT_AAL3 &&
			msg->msg_ie_aalp->ie_aalp_aal_type !=
			UNI_IE_AALP_AT_AAL5) {
		cause = UNI_IE_CAUS_UAAL;
		ATM_DEBUG0("unisig_vc_act08: bad AAL\n");
		goto response08;
	}

	/*
	 * Should verify that we can handle requested
	 * connection QOS
	 */

	/*
	 * Make sure the specified VPI/VCI is valid
	 */
	vpi = msg->msg_ie_cnid->ie_cnid_vpci;
	vci = msg->msg_ie_cnid->ie_cnid_vci;
	if (vpi > pip->pif_maxvpi ||
			vci > pip->pif_maxvci ||
			vci < UNI_IE_CNID_MIN_VCI) {
		cause = UNI_IE_CAUS_BAD_VCC;
		ATM_DEBUG0("unisig_vc_act08: VPI/VCI invalid\n");
		goto response08;
	}

	/*
	 * Make sure the specified VPI/VCI isn't in use already
	 */
	if (unisig_find_vpvc(usp, vpi, vci, VCC_IN)) {
		cause = UNI_IE_CAUS_NA_VCC;
		ATM_DEBUG0("unisig_vc_act08: VPI/VCI in use\n");
		goto response08;
	}

	/*
	 * Make sure it's a point-to-point connection
	 */
	if (msg->msg_ie_bbcp->ie_bbcp_conn_config !=
			UNI_IE_BBCP_CC_PP) {
		cause = UNI_IE_CAUS_NI_BC;
		ATM_DEBUG0("unisig_vc_act08: conn not pt-pt\n");
		goto response08;
	}

	/*
	 * Fill in the VCCB fields that we can at this point
	 */
	uvp->uv_type = VCC_SVC | VCC_IN | VCC_OUT;
	uvp->uv_proto = pip->pif_sigmgr->sm_proto;
	uvp->uv_sstate = UNI_CALL_PRESENT;
	uvp->uv_ustate = VCCU_POPEN;
	uvp->uv_pif = pip;
	uvp->uv_nif = nip;
	uvp->uv_vpi = msg->msg_ie_cnid->ie_cnid_vpci;
	uvp->uv_vci = msg->msg_ie_cnid->ie_cnid_vci;
	uvp->uv_tstamp = time_second;

	/*
	 * Copy the connection attributes from the SETUP message
	 * to an attribute block
	 */
	KM_ZERO(&attr, sizeof(attr));
	attr.nif = nip;
	attr.aal.tag = T_ATM_ABSENT;
	attr.traffic.tag = T_ATM_ABSENT;
	attr.bearer.tag = T_ATM_ABSENT;
	attr.bhli.tag = T_ATM_ABSENT;
	attr.blli.tag_l2 = T_ATM_ABSENT;
	attr.blli.tag_l3 = T_ATM_ABSENT;
	attr.llc.tag = T_ATM_ABSENT;
	attr.called.tag = T_ATM_ABSENT;
	attr.calling.tag = T_ATM_ABSENT;
	attr.qos.tag = T_ATM_ABSENT;
	attr.transit.tag = T_ATM_ABSENT;
	attr.cause.tag = T_ATM_ABSENT;
	unisig_save_attrs(usp, msg, &attr);

	/*
	 * Notify the connection manager of the new VCC
	 */
	ATM_DEBUG0("unisig_vc_act08: notifying user of connection\n");
	rc = atm_cm_incoming((struct vccb *)uvp, &attr);
	if (rc)
		goto response08;

	/*
	 * Wait for the connection recipient to issue an accept
	 * or reject
	 */
	return(0);

response08:
	ATM_DEBUG1("unisig_vc_act08: reject with cause=%d\n", cause);

	/*
	 * Clear the VCCB state
	 */
	uvp->uv_sstate = UNI_NULL;

	/*
	 * Mark the time
	 */
	uvp->uv_tstamp = time_second;

	/*
	 * Some problem was detected with the request.  Send a Q.2931
	 * message rejecting the connection.
	 */
	rc = unisig_send_release_complete(usp, uvp, msg, cause);

	return(rc);
}


/*
 * VC state machine action 9
 * Accept routine called by user
 *
 * Send CONNECT, start timer T313, go to UNI_CONNECT_REQUEST state
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act09(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int			rc;
	struct unisig_msg	*conn_msg;

	conn_msg = (struct unisig_msg *) atm_allocate(&unisig_msgpool);
	if (conn_msg == NULL)
		return(ENOMEM);

	/*
	 * Fill out the response
	 */
	conn_msg->msg_call_ref = uvp->uv_call_ref;
	conn_msg->msg_type = UNI_MSG_CONN;
	conn_msg->msg_type_flag = 0;
	conn_msg->msg_type_action = 0;

	/*
	 * Send the CONNECT message.  If the send fails, the other
	 * side will eventually time out and close the connection.
	 */
	rc = unisig_send_msg(usp, conn_msg);
	unisig_free_msg(conn_msg);
	if (rc) {
		return(rc);
	}

	/*
	 * Start timer T313
	 */
	UNISIG_VC_TIMER((struct vccb *) uvp, UNI_T313);

	/*
	 * Set the new state
	 */
	uvp->uv_sstate = UNI_CONNECT_REQUEST;

	/*
	 * Mark the time
	 */
	uvp->uv_tstamp = time_second;

	return(0);
}


/*
 * VC state machine action 10
 * Received CONNECT ACK
 *
 * Go to UNI_ACTIVE state
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act10(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	/*
	 * Clear any running timer
	 */
	UNISIG_VC_CANCEL((struct vccb *) uvp);

	/*
	 * Set the state
	 */
	uvp->uv_sstate = UNI_ACTIVE;
	uvp->uv_ustate = VCCU_OPEN;

	/*
	 * Mark the time
	 */
	uvp->uv_tstamp = time_second;

	/*
	 * Notify the user that the call is up
	 */
	atm_cm_connected(uvp->uv_connvc);

	return (0);
}


/*
 * VC state machine action 11
 * Reject handler called
 *
 * Send RELEASE COMPLETE, clear the call
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act11(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int			rc, cause;

	/*
	 * Send generic cause code if one is not already set
	 */
	if (uvp->uv_connvc->cvc_attr.cause.tag == T_ATM_PRESENT)
		cause = T_ATM_ABSENT;
	else
		cause = T_ATM_CAUSE_CALL_REJECTED;

	/*
	 * Send a RELEASE COMPLETE message
	 */
	rc = unisig_send_release_complete(usp, uvp, msg, cause);

	/*
	 * Clear the call VCCB
	 */
	uvp->uv_sstate = UNI_FREE;
	uvp->uv_ustate = VCCU_CLOSED;

	/*
	 * Mark the time
	 */
	uvp->uv_tstamp = time_second;

	return(rc);
}


/*
 * VC state machine action 12
 * Release or abort routine called
 *
 * Send RELEASE, start timer T308, go to UNI_RELEASE_REQUEST state
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act12(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int			rc;

	/*
	 * Clear any running timer
	 */
	UNISIG_VC_CANCEL((struct vccb *) uvp);

	/*
	 * Send the RELEASE message
	 */
	rc = unisig_vc_clear_call(usp, uvp, (struct unisig_msg *)NULL,
			T_ATM_ABSENT);

	return(rc);
}


/*
 * VC state machine action 13
 * RELEASE COMPLETE received
 *
 * Clear the call
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act13(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	/*
	 * Clear any running timer
	 */
	UNISIG_VC_CANCEL((struct vccb *) uvp);

	/*
	 * Set the state
	 */
	uvp->uv_sstate = UNI_FREE;
	if (uvp->uv_ustate != VCCU_ABORT)
		uvp->uv_ustate = VCCU_CLOSED;

	/*
	 * Mark the time
	 */
	uvp->uv_tstamp = time_second;

	/*
	 * Notify the user that the call is now closed
	 */
	if (msg->msg_ie_caus != NULL)
		unisig_cause_attr_from_ie(&uvp->uv_connvc->cvc_attr,
			msg->msg_ie_caus);
	atm_cm_cleared(uvp->uv_connvc);

	return(0);
}


/*
 * VC state machine action 14
 * Timer expired while waiting for RELEASE COMPLETE
 *
 * If this is the second expiration, just clear the call.  Otherwise,
 * retransmit the RELEASE message and restart timer T308.
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act14(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int			rc;

	/*
	 * Check the retry count
	 */
	if (uvp->uv_retry) {
		/*
		 * Clear the connection
		 */
		rc = unisig_clear_vcc(usp, uvp,
				T_ATM_CAUSE_NORMAL_CALL_CLEARING);
	} else {
		/*
		 * Increment the retry count
		 */
		uvp->uv_retry++;

		/*
		 * Resend the RELEASE message
		 */
		rc = unisig_send_release(usp, uvp,
				(struct unisig_msg *)0, T_ATM_ABSENT);
		if (rc)
			return(rc);

		/*
		 * Restart timer T308
		 */
		UNISIG_VC_TIMER((struct vccb *) uvp, UNI_T308);
	}

	return(0);
}


/*
 * VC state machine action 15
 * RELEASE received in UNI_ACTIVE state
 *
 * Send RELEASE COMPLETE, go to UNI_FREE, notify the user
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act15(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int			cause, rc;
	struct ie_generic	*iep;

	/*
	 * Clear any running timer
	 */
	UNISIG_VC_CANCEL((struct vccb *) uvp);

	/*
	 * If there was no Cause IE, flag an error
	 */
	if (!msg->msg_ie_caus) {
		cause = UNI_IE_CAUS_MISSING;
		for (iep=msg->msg_ie_err; iep; iep=iep->ie_next) {
			if (iep->ie_ident == UNI_IE_CAUS &&
					iep->ie_err_cause ==
					UNI_IE_CAUS_IECONTENT) {
				cause = UNI_IE_CAUS_IECONTENT;
			}
		}
		if (cause == UNI_IE_CAUS_MISSING) {
			iep = (struct ie_generic *)atm_allocate(
					&unisig_iepool);
			if (!iep)
				return(ENOMEM);
			iep->ie_ident = UNI_IE_CNID;
			iep->ie_err_cause = UNI_IE_CAUS_MISSING;
			MSG_IE_ADD(msg, iep, UNI_MSG_IE_ERR);
		}
	} else {
		cause = UNI_IE_CAUS_NORM_UNSP;
	}

	/*
	 * Send a RELEASE COMPLETE message
	 */
	rc = unisig_send_release_complete(usp, uvp, msg, cause);

	/*
	 * Set the state
	 */
	uvp->uv_sstate = UNI_FREE;
	uvp->uv_ustate = VCCU_CLOSED;

	/*
	 * Mark the time
	 */
	uvp->uv_tstamp = time_second;

	/*
	 * Notify the user that the call is cleared
	 */
	if (msg->msg_ie_caus != NULL)
		unisig_cause_attr_from_ie(&uvp->uv_connvc->cvc_attr,
			msg->msg_ie_caus);
	else
		unisig_cause_attr_from_user(&uvp->uv_connvc->cvc_attr,
			T_ATM_CAUSE_UNSPECIFIED_NORMAL);
	atm_cm_cleared(uvp->uv_connvc);

	return(rc);
}


/*
 * VC state machine action 16
 * RELEASE received in UNI_RELEASE_REQUEST state
 *
 * Clear the call
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act16(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int	rc;

	/*
	 * Clear any running timer
	 */
	UNISIG_VC_CANCEL((struct vccb *) uvp);

	/*
	 * Clear the VCCB
	 */
	rc = unisig_clear_vcc(usp, uvp, T_ATM_ABSENT);

	return(rc);
}


/*
 * VC state machine action 17
 * Protocol error
 *
 * Send a STATUS message with cause 101, "message not compatible with
 * call state"
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act17(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int			rc;

	ATM_DEBUG3("unisig_vc_perror: usp=%p, uvp=%p, msg=%p\n",
			usp, uvp, msg);

	/*
	 * Clear any running timer
	 */
	UNISIG_VC_CANCEL((struct vccb *) uvp);

	/*
	 * Send a STATUS message
	 */
	rc = unisig_send_status(usp, uvp, msg, UNI_IE_CAUS_STATE);

	return(rc ? rc : EINVAL);
}


/*
 * VC state machine action 18
 * Signalling AAL connection has been lost
 *
 * Start timer T309.  If the timer expires before the SAAL connection
 * comes back, the VCC will be cleared.
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act18(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	/*
	 * Clear any running timer
	 */
	UNISIG_VC_CANCEL((struct vccb *) uvp);

	/*
	 * Start timer T309
	 */
	UNISIG_VC_TIMER((struct vccb *) uvp, UNI_T309);

	/*
	 * Set new state
	 */
	uvp->uv_sstate = UNI_SSCF_RECOV;

	return(0);
}


/*
 * VC state machine action 19
 * Ignore the event
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act19(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	return(0);
}


/*
 * VC state machine action 20
 * SSCF establish indication in UNI_SSCF_RECOV state -- signalling
 * AAL has come up after an outage
 *
 * Send STATUS ENQ to make sure we're in compatible state with other end
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act20(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int			rc;
	struct unisig_msg	*stat_msg;

	/*
	 * Clear any running timer
	 */
	UNISIG_VC_CANCEL((struct vccb *) uvp);

	/*
	 * Get memory for a STATUS ENQUIRY message
	 */
	stat_msg = (struct unisig_msg *)atm_allocate(&unisig_msgpool);
	if (stat_msg == NULL)
		return(ENOMEM);

	/*
	 * Fill out the message
	 */
	stat_msg->msg_call_ref = uvp->uv_call_ref;
	stat_msg->msg_type = UNI_MSG_SENQ;
	stat_msg->msg_type_flag = 0;
	stat_msg->msg_type_action = 0;

	/*
	 * Send the STATUS ENQUIRY message
	 */
	rc = unisig_send_msg(usp, stat_msg);
	unisig_free_msg(stat_msg);

	/*
	 * Return to active state
	 */
	uvp->uv_sstate = UNI_ACTIVE;

	return(rc);
}


/*
 * VC state machine action 21
 * STATUS received
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection (may
 *		be NULL)
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act21(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int	cause, rc;

	/*
	 * Ignore a STATUS message with the global call reference
	 */
	if (GLOBAL_CREF(msg->msg_call_ref)) {
		return(0);
	}

	/*
	 * If the network thinks we're in NULL state, clear the VCC
	 */
	if (msg->msg_ie_clst->ie_clst_state == UNI_NULL) {
		if (uvp) {
			(void)unisig_clear_vcc(usp, uvp,
					T_ATM_CAUSE_DESTINATION_OUT_OF_ORDER);
		}
		return(0);
	}

	/*
	 * If we are in NULL state, send a RELEASE COMPLETE
	 */
	if (!uvp || (uvp->uv_sstate == UNI_FREE) ||
			(uvp->uv_sstate == UNI_NULL)) {
		rc = unisig_send_release_complete(usp,
				uvp, msg, UNI_IE_CAUS_STATE);
		return(rc);
	}

	/*
	 * If the reported state doesn't match our state, close the VCC
	 * unless we're in UNI_RELEASE_REQUEST or UNI_RELEASE_IND
	 */
	if (msg->msg_ie_clst->ie_clst_state != uvp->uv_sstate) {
		if (uvp->uv_sstate == UNI_RELEASE_REQUEST ||
				uvp->uv_sstate == UNI_RELEASE_IND) {
			return(0);
		}
		rc = unisig_clear_vcc(usp, uvp,
			T_ATM_CAUSE_MESSAGE_INCOMPATIBLE_WITH_CALL_STATE);
	}

	/*
	 * States match, check for an error on one of our messages
	 */
	cause = msg->msg_ie_caus->ie_caus_cause;
	if (cause == UNI_IE_CAUS_MISSING ||
			cause == UNI_IE_CAUS_MTEXIST ||
			cause == UNI_IE_CAUS_IEEXIST ||
			cause == UNI_IE_CAUS_IECONTENT ||
			cause == UNI_IE_CAUS_STATE) {
		ATM_DEBUG2("unisig_vc_act21: error %d on message 0x%x\n",
				cause,
				msg->msg_ie_caus->ie_caus_diagnostic[0]);
		if (uvp) {
			(void)unisig_clear_vcc(usp, uvp, cause);
		}
	}

	return(0);
}


/*
 * VC state machine action 22
 * Received STATUS ENQ
 *
 * Send STATUS with cause 30 "response to STATUS ENQUIRY" and
 * current state
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection (may
 *		be NULL)
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act22(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int			rc;
	struct unisig_msg	*status;
	struct ie_generic	*callst_ie, *cause_ie;

	ATM_DEBUG3("unisig_vc_perror: usp=%p, uvp=%p, msg=%p\n",
			usp, uvp, msg);

	/*
	 * Get memory for a STATUS message
	 */
	status = (struct unisig_msg *) atm_allocate(&unisig_msgpool);
	if (status == NULL)
		return(ENOMEM);
	callst_ie = (struct ie_generic *) atm_allocate(&unisig_iepool);
	if (callst_ie == NULL) {
		atm_free(status);
		return(ENOMEM);
	}
	cause_ie = (struct ie_generic *) atm_allocate(&unisig_iepool);
	if (cause_ie == NULL) {
		atm_free(status);
		atm_free(callst_ie);
		return(ENOMEM);
	}

	/*
	 * Fill out the response
	 */
	if (uvp) {
		status->msg_call_ref = uvp->uv_call_ref;
	} else if (msg) {
		if (msg->msg_call_ref & UNI_MSG_CALL_REF_RMT)
			status->msg_call_ref = msg->msg_call_ref &
					UNI_MSG_CALL_REF_MASK;
		else
			status->msg_call_ref = msg->msg_call_ref |
					UNI_MSG_CALL_REF_RMT;
	} else {
		status->msg_call_ref = UNI_MSG_CALL_REF_GLOBAL;
	}
	status->msg_type = UNI_MSG_STAT;
	status->msg_type_flag = 0;
	status->msg_type_action = 0;
	status->msg_ie_clst = callst_ie;
	status->msg_ie_caus = cause_ie;

	/*
	 * Fill out the call state IE
	 */
	callst_ie->ie_ident = UNI_IE_CLST;
	callst_ie->ie_coding = 0;
	callst_ie->ie_flag = 0;
	callst_ie->ie_action = 0;
	if (uvp) {
		switch(uvp->uv_sstate) {
		case UNI_FREE:
			callst_ie->ie_clst_state = UNI_NULL;
			break;
		default:
			callst_ie->ie_clst_state = uvp->uv_sstate;
		}
	} else {
		callst_ie->ie_clst_state = UNI_NULL;
	}

	/*
	 * Fill out the cause IE
	 */
	cause_ie->ie_ident = UNI_IE_CAUS;
	cause_ie->ie_coding = 0;
	cause_ie->ie_flag = 0;
	cause_ie->ie_action = 0;
	cause_ie->ie_caus_loc = UNI_IE_CAUS_LOC_USER;
	cause_ie->ie_caus_cause = UNI_IE_CAUS_SENQ;

	/*
	 * Send the STATUS message
	 */
	rc = unisig_send_msg(usp, status);
	unisig_free_msg(status);
	return(rc);
}


/*
 * VC state machine action 23
 * Received ADD PARTY
 *
 * We don't support multipoint connections, so send an ADD PARTY REJECT
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act23(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int			rc;
	struct unisig_msg	*apr_msg;

	/*
	 * Get memory for the ADD PARTY REJECT message
	 */
	apr_msg = (struct unisig_msg *) atm_allocate(&unisig_msgpool);
	if (apr_msg == NULL)
		return(ENOMEM);

	/*
	 * Fill out the message
	 */
	if (msg->msg_call_ref & UNI_MSG_CALL_REF_RMT)
		apr_msg->msg_call_ref = msg->msg_call_ref &
				UNI_MSG_CALL_REF_MASK;
	else
		apr_msg->msg_call_ref = msg->msg_call_ref |
				UNI_MSG_CALL_REF_RMT;
	apr_msg->msg_type = UNI_MSG_ADPR;
	apr_msg->msg_type_flag = 0;
	apr_msg->msg_type_action = 0;

	/*
	 * Use the endpoint reference IE from the received message
	 */
	apr_msg->msg_ie_eprf = msg->msg_ie_eprf;

	/*
	 * Send the ADD PARTY REJECT message
	 */
	rc = unisig_send_msg(usp, apr_msg);
	apr_msg->msg_ie_eprf = NULL;
	unisig_free_msg(apr_msg);

	return(rc);
}


/*
 * VC state machine action 24
 * User error
 *
 * Return EALREADY
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act24(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	return(EALREADY);
}


/*
 * VC state machine action 25
 * User error
 *
 * Return EINVAL
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act25(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	return(EINVAL);
}


/*
 * VC state machine action 26
 * PVC abort
 *
 * The abort handler was called to abort a PVC.  Clear the VCCB and
 * notify the user.
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act26(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int	rc;

	/*
	 * Clear any running timer
	 */
	UNISIG_VC_CANCEL((struct vccb *) uvp);

	/*
	 * Close the VCCB
	 */
	rc = unisig_close_vcc(usp, uvp);
	if (rc)
		return(rc);

	/*
	 * Notify the user
	 */
	if (uvp->uv_connvc->cvc_attr.cause.tag != T_ATM_PRESENT)
		unisig_cause_attr_from_user(&uvp->uv_connvc->cvc_attr,
			T_ATM_CAUSE_NORMAL_CALL_CLEARING);

	atm_cm_cleared(uvp->uv_connvc);

	return(0);
}


/*
 * VC state machine action 27
 * Signalling AAL failure
 *
 * Change PVC state to UNI_PVC_ACT_DOWN.
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act27(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	/*
	 * Set the state
	 */
	uvp->uv_sstate = UNI_PVC_ACT_DOWN;

	/*
	 * Mark the time
	 */
	uvp->uv_tstamp = time_second;

	return(0);
}


/*
 * VC state machine action 28
 * Signalling AAL established
 *
 * Set PVC state to UNI_PVC_ACTIVE.
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act28(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	/*
	 * Set the state
	 */
	uvp->uv_sstate = UNI_PVC_ACTIVE;

	/*
	 * Mark the time
	 */
	uvp->uv_tstamp = time_second;

	return(0);
}


/*
 * VC state machine action 29
 * Protocol error
 *
 * Send a RELEASE COMPLETE message with cause 81, "invalid call
 * reference value"
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection (may
 *		be NULL)
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act29(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	int			rc;

	/*
	 * Send a RELEASE COMPLETE message
	 */
	rc = unisig_send_release_complete(usp, uvp, msg,
			UNI_IE_CAUS_CREF);

	return(rc);
}


/*
 * VC state machine action 30
 * Release routine called while SSCF session down, or SSCF session
 * reset or lost while in UNI_CALL_PRESENT
 *
 * Go to UNI_FREE state
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act30(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	/*
	 * Clear any running timer
	 */
	UNISIG_VC_CANCEL((struct vccb *) uvp);

	/*
	 * Clear the call state
	 */
	uvp->uv_sstate = UNI_FREE;
	uvp->uv_ustate = VCCU_CLOSED;

	/*
	 * Mark the time
	 */
	uvp->uv_tstamp = time_second;

	return(0);
}


/*
 * VC state machine action 31
 * Accept handler called in UNI_FREE state.
 *
 * The call was in UNI_CALL_PRESENT state when it was closed because
 * of an SSCF failure.  Return an error indication.  The accept
 * handler will free the VCCB and return the proper code to the
 * caller.
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to a UNISIG message structure
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_act31(usp, uvp, msg)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
{
	return(ENETDOWN);
}


/*
 * Initiate clearing a call by sending a RELEASE message.
 *
 * Arguments:
 *	usp	pointer to protocol instance block
 *	uvp	pointer to the VCCB for the affected connection
 *	msg	pointer to UNI signalling message that the RELEASE
 *		responds to (may be NULL)
 *	cause	the reason for clearing the call;  a value of
 *		T_ATM_ABSENT indicates that the cause code is
 *		in the VCC's ATM attributes block
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
static int
unisig_vc_clear_call(usp, uvp, msg, cause)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;
	struct unisig_msg	*msg;
	int			cause;
{
	int			rc;

	/*
	 * Clear the retry count
	 */
	uvp->uv_retry = 0;

	/*
	 * Make sure the ATM attributes block has a valid cause code,
	 * if needed
	 */
	if (cause == T_ATM_ABSENT &&
			uvp->uv_connvc->cvc_attr.cause.tag !=
			T_ATM_PRESENT) {
		uvp->uv_connvc->cvc_attr.cause.tag = T_ATM_PRESENT;
		uvp->uv_connvc->cvc_attr.cause.v.coding_standard =
				T_ATM_ITU_CODING;
		uvp->uv_connvc->cvc_attr.cause.v.location =
				T_ATM_LOC_USER;
		uvp->uv_connvc->cvc_attr.cause.v.cause_value =
			usp->us_proto == ATM_SIG_UNI30 ? 
				T_ATM_CAUSE_UNSPECIFIED_NORMAL :
				T_ATM_CAUSE_NORMAL_CALL_CLEARING;
	}

	/*
	 * Send a RELEASE message
	 */
	rc = unisig_send_release(usp, uvp, msg, cause);
	if (rc)
		return(rc);

	/*
	 * Start timer T308
	 */
	UNISIG_VC_TIMER((struct vccb *) uvp, UNI_T308);

	/*
	 * Set the VCCB state
	 */
	uvp->uv_sstate = UNI_RELEASE_REQUEST;
	if (uvp->uv_ustate != VCCU_ABORT)
		uvp->uv_ustate = VCCU_CLOSED;

	/*
	 * Mark the time
	 */
	uvp->uv_tstamp = time_second;

	return(0);
}
