/*-
 * ===================================
 * 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.
 */

/*
 * ATM Forum UNI 3.0/3.1 Signalling Manager
 * ----------------------------------------
 *
 * Protocol processing module
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/sys/netatm/uni/unisig_util.c,v 1.12 2005/01/07 01:45:37 imp Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <netatm/port.h>
#include <netatm/queue.h>
#include <netatm/atm.h>
#include <netatm/atm_sys.h>
#include <netatm/atm_sap.h>
#include <netatm/atm_cm.h>
#include <netatm/atm_if.h>
#include <netatm/atm_vc.h>
#include <netatm/atm_sigmgr.h>
#include <netatm/atm_stack.h>
#include <netatm/atm_pcb.h>
#include <netatm/atm_var.h>

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

/*
 * Free a UNISIG signalling message
 *
 * Free the passed message and any IEs that are attached to it
 *
 * Arguments:
 *	msg	pointer to UNISIG protocol instance
 *
 * Returns:
 *	none
 *
 */
void
unisig_free_msg(msg)
	struct unisig_msg	*msg;
{
	int			i;
	struct ie_generic	*ie, *ienxt;

	ATM_DEBUG1("unisig_free_msg: msg=%p\n", msg);

	/*
	 * First free all the IEs
	 */
	for (i=0; i<UNI_MSG_IE_CNT; i++) {
		ie = msg->msg_ie_vec[i];
		while (ie) {
			ienxt = ie->ie_next;
			uma_zfree(unisig_ie_zone, ie);
			ie = ienxt;
		}
	}

	/*
	 * Finally, free the message structure itself
	 */
	uma_zfree(unisig_msg_zone, msg);
}

/*
 * Verify a VCCB
 *
 * Search UNISIG's VCCB queue to verify that a VCCB belongs to UNISIG.
 *
 * Arguments:
 *	usp	pointer to UNISIG protocol instance
 *	svp	pointer to a VCCB
 *
 * Returns:
 *	TRUE	the VCCB belongs to UNISIG
 *	FALSE	the VCCB doesn't belong to UNISIG
 *
 */
int
unisig_verify_vccb(usp, uvp)
	struct unisig		*usp;
	struct unisig_vccb	*uvp;

{
	struct unisig_vccb	*utp, *uvnext;

	for (utp = Q_HEAD(usp->us_vccq, struct unisig_vccb);
			utp; utp = uvnext){
		uvnext = Q_NEXT(utp, struct unisig_vccb, uv_sigelem);
		if (uvp == utp) {
			return(TRUE);
		}
	}
	return(FALSE);
}


/*
 * Find a connection
 *
 * Find a VCCB given the call reference
 *
 * Arguments:
 *	usp	pointer to UNISIG protocol instance
 *	cref	the call reference to search for
 *
 * Returns:
 *	0	there is no such VCCB
 *	uvp	the address of the VCCB
 *
 */
struct unisig_vccb *
unisig_find_conn(usp, cref)
	struct unisig	*usp;
	u_int		cref;

{
	struct unisig_vccb	*uvp, *uvnext;

	for (uvp = Q_HEAD(usp->us_vccq, struct unisig_vccb); uvp;
			uvp = uvnext){
		uvnext = Q_NEXT(uvp, struct unisig_vccb, uv_sigelem);
		if (uvp->uv_call_ref == cref)
			break;
	}
	return(uvp);
}


/*
 * Find a VCCB
 *
 * Find a VCCB given the VPI and VCI.
 *
 * Arguments:
 *	usp	pointer to UNISIG protocol instance
 *	vpi	the VPI to search for
 *	vci	the VCI to search for
 *	dir	the direction of the VCC (VCC_IN, VCC_OUT, or both).
 *		If dir is set to zero, return the address of any VCCB
 *		with the given VPI/VCI, regardless of direction.
 *
 * Returns:
 *	0	there is no such VCCB
 *	uvp	the address of the VCCB
 *
 */
struct unisig_vccb *
unisig_find_vpvc(usp, vpi, vci, dir)
	struct unisig	*usp;
	int		vpi, vci;
	u_char		dir;

{
	struct unisig_vccb	*uvp, *uvnext;

	for (uvp = Q_HEAD(usp->us_vccq, struct unisig_vccb); uvp;
			uvp = uvnext){
		uvnext = Q_NEXT(uvp, struct unisig_vccb, uv_sigelem);
		if (uvp->uv_vpi == vpi &&
				uvp->uv_vci == vci &&
				(uvp->uv_type & dir) == dir)
			break;
	}
	return(uvp);
}


/*
 * Allocate a call reference value
 *
 * Arguments:
 *	usp	pointer to UNISIG protocol instance
 *
 * Returns:
 *	0	call reference not available
 *	cref	the call reference value
 *
 */
int
unisig_alloc_call_ref(usp)
	struct unisig	*usp;

{
	int	cref;

	/*
	 * Get the next call reference value
	 */
	cref = usp->us_cref;

	/*
	 * Make sure it hasn't got too large
	 */
	if (cref >= UNI_MSG_CALL_REF_DUMMY) {
		/* XXX */
		log(LOG_ERR, "uni: call reference limit reached\n");
		return(0);
	}
	
	/*
	 * Bump the call reference value
	 */
	usp->us_cref++;

	return(cref);
}


/*
 * Print an ATM address
 *
 * Convert an ATM address into an ASCII string suitable for printing.
 *
 * Arguments:
 *	p	pointer to an ATM address
 *
 * Returns:
 *	the address of a string with the ASCII representation of the
 *	address.  This routine returns the address of a statically-
 *	allocated buffer, so if repeated calls to this routine are made,
 *	each call will destroy the result of the previous call.
 *
 */
char *
unisig_addr_print(p)
	Atm_addr		*p;
{
	int		i;
	char		*fp, *op, t_buff[16];
	u_char		*cp;
	static char	strbuff[256];

	static char	nf_DCC[] = "0xX.XX.X.XXX.XX.XX.XX.XXXXXX.X";
	static char	nf_ICD[] = "0xX.XX.X.XXX.XX.XX.XX.XXXXXX.X";
	static char	nf_E164[] = "0xX.XXXXXXXX.XX.XX.XXXXXX.X";

	union {
		int	w;
		char	c[4];
	} u1, u2;

	/*
	 * Clear the print buffer
	 */
	bzero(strbuff, sizeof(strbuff));

	/*
	 * Select appropriate printing format
	 */
	switch(p->address_format) {
	case T_ATM_ENDSYS_ADDR:
		/*
		 * Select format by NSAP type
		 */
		switch(((Atm_addr_nsap *)p->address)->aan_afi) {
		default:
		case AFI_DCC:
			fp = nf_DCC;
			break;
		case AFI_ICD:
			fp = nf_ICD;
			break;
		case AFI_E164:
			fp = nf_E164;
			break;
		}

		/*
		 * Loop through the format string, converting the NSAP
		 * to ASCII
		 */
		cp = (u_char *) p->address;
		op = strbuff;
		while (*fp) {
			if (*fp == 'X') {
				/*
				 * If format character is an 'X', put a
				 * two-digit hex representation of the
				 * NSAP byte in the output buffer
				 */
				snprintf(t_buff, sizeof(t_buff),
					"%x", *cp + 512);
				strcpy(op, &t_buff[strlen(t_buff)-2]);
				op++; op++;
				cp++;
			} else {
				/*
				 * If format character isn't an 'X',
				 * just copy it to the output buffer
				 */
				*op = *fp;
				op++;
			}
			fp++;
		}

		break;

	case T_ATM_E164_ADDR:
		/*
		 * Print the IA5 characters of the E.164 address
		 */
		for(i=0; i<p->address_length; i++) {
			snprintf(strbuff + strlen(strbuff),
			    sizeof(strbuff) - strlen(strbuff), "%c",
				((Atm_addr_e164 *)p->address)->aae_addr[i]);
		}
		break;

	case T_ATM_SPANS_ADDR:
		/*
		 * Get address into integers
		 */
		u1.c[0] = ((Atm_addr_spans *)p->address)->aas_addr[0];
		u1.c[1] = ((Atm_addr_spans *)p->address)->aas_addr[1];
		u1.c[2] = ((Atm_addr_spans *)p->address)->aas_addr[2];
		u1.c[3] = ((Atm_addr_spans *)p->address)->aas_addr[3];
		u2.c[0] = ((Atm_addr_spans *)p->address)->aas_addr[4];
		u2.c[1] = ((Atm_addr_spans *)p->address)->aas_addr[5];
		u2.c[2] = ((Atm_addr_spans *)p->address)->aas_addr[6];
		u2.c[3] = ((Atm_addr_spans *)p->address)->aas_addr[7];

		/*
		 * Print the address as two words xxxxx.yyyyyyyy
		 */
		snprintf(strbuff, sizeof(strbuff), "%x.%x", u1.w, u2.w);
		break;

	case T_ATM_ABSENT:
	default:
		strcpy(strbuff, "-");
	}

	return(strbuff);
}


/*
 * Print the contents of a message buffer chain
 *
 * Arguments:
 *	m	pointer to a buffer
 *
 * Returns:
 *	none
 *
 */
void
unisig_print_mbuf(m)
	KBuffer		*m;
{
	int i;
	caddr_t		cp;

	printf("unisig_print_mbuf:\n");
	while (m) { 
		KB_DATASTART(m, cp, caddr_t);
		for (i = 0; i < KB_LEN(m); i++) {
			if (i == 0)
				printf("   bfr=%p: ", m);
			printf("%x ", (u_char)*cp++);
		}
		printf("<end_bfr>\n");
		m = KB_NEXT(m);
	}
}
