/*
 *
 * ===================================
 * 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/spans/spans_if.c,v 1.6 1999/08/28 00:48:49 peter Exp $
 *
 */

/*
 * SPANS Signalling Manager
 * ---------------------------
 *
 * External interfaces to SPANS manager.  Includes support for
 * running as a loadable kernel module.
 *
 */

#ifndef ATM_SPANS_MODULE
#include "opt_atm.h"
#endif

#include <netatm/kern_include.h>

#include "spans_xdr.h"
#include <netatm/spans/spans_var.h>

#ifndef lint
__RCSID("@(#) $FreeBSD: src/sys/netatm/spans/spans_if.c,v 1.6 1999/08/28 00:48:49 peter Exp $");
#endif

/*
 * Global variables
 */
struct sp_info	spans_vcpool = {
	"spans vcc pool",		/* si_name */
	sizeof(struct spans_vccb),	/* si_blksiz */
	10,				/* si_blkcnt */
	50				/* si_maxallow */
};

struct sp_info	spans_msgpool = {
	"spans message pool",		/* si_name */
	sizeof(spans_msg),		/* si_blksiz */
	10,				/* si_blkcnt */
	50				/* si_maxallow */
};

/*
 * Local functions
 */
static int	spans_start __P((void));
static int	spans_stop __P((void));
static int	spans_attach __P((struct sigmgr *, struct atm_pif *));
static int	spans_detach __P((struct atm_pif *));
static int	spans_setup __P((Atm_connvc *, int *));
static int	spans_release __P((struct vccb *, int *));
static int	spans_accept __P((struct vccb *, int *));
static int	spans_reject __P((struct vccb *, int *));
static int	spans_ioctl __P((int, caddr_t, caddr_t));

/*
 * Local variables
 */
static struct sigmgr	*spans_mgr = NULL;


/*
 * Initialize SPANS processing
 *
 * This will be called during module loading.  We'll just register
 * the SPANS protocol descriptor and wait for a SPANS ATM interface
 * to come online.
 *
 * Arguments:
 *	none
 *
 * Returns:
 *	0	startup was successful
 *	errno	startup failed - reason indicated
 *
 */
static int
spans_start()
{
	int	err = 0;

	/*
	 * Verify software version
	 */
	if (atm_version != ATM_VERSION) {
		log(LOG_ERR, "version mismatch: spans=%d.%d kernel=%d.%d\n",
				ATM_VERS_MAJ(ATM_VERSION),
				ATM_VERS_MIN(ATM_VERSION),
				ATM_VERS_MAJ(atm_version),
				ATM_VERS_MIN(atm_version));
		return (EINVAL);
	}

	/*
	 * Allocate protocol definition structure
	 */
	spans_mgr = (struct sigmgr *)KM_ALLOC(sizeof(struct sigmgr),
			M_DEVBUF, M_NOWAIT);
	if (spans_mgr == NULL) {
		err = ENOMEM;
		goto done;
	}
	KM_ZERO(spans_mgr, sizeof(struct sigmgr));

	/*
	 * Initialize protocol invariant values
	 */
	spans_mgr->sm_proto = ATM_SIG_SPANS;
	spans_mgr->sm_attach = spans_attach;
	spans_mgr->sm_detach = spans_detach;
	spans_mgr->sm_setup = spans_setup;
	spans_mgr->sm_release = spans_release;
	spans_mgr->sm_accept = spans_accept;
	spans_mgr->sm_reject = spans_reject;
	spans_mgr->sm_free = spans_free;
	spans_mgr->sm_ioctl = spans_ioctl;

	/*
	 * Register ourselves with system
	 */
	err = atm_sigmgr_register(spans_mgr);
	if (err)
		goto done;

	/*
	 * Start up Connectionless Service
	 */
	err = spanscls_start();
	if (err)
		goto done;

done:
	return (err);
}


/*
 * Halt SPANS processing
 *
 * This should be called just prior to unloading the module from
 * memory.  All SPANS interfaces must be deregistered before the
 * protocol can be shutdown.
 *
 * Arguments:
 *	none
 *
 * Returns:
 *	0	startup was successful
 *	errno	startup failed - reason indicated
 *
 */
static int
spans_stop()
{
	int	err = 0;
	int	s = splnet();

	/*
	 * Is protocol even set up?
	 */
	if (spans_mgr) {

		/*
		 * Any protocol instances still registered?
		 */
		if (spans_mgr->sm_prinst) {

			/* Yes, can't stop now */
			err = EBUSY;
			goto done;
		}

		/*
		 * Stop Connectionless Service
		 */
		spanscls_stop();

		/*
		 * De-register from system
		 */
		err = atm_sigmgr_deregister(spans_mgr);

		/*
		 * Free up protocol block
		 */
		KM_FREE(spans_mgr, sizeof(struct sigmgr), M_DEVBUF);
		spans_mgr = NULL;

		/*
		 * Free up our storage pools
		 */
		atm_release_pool(&spans_vcpool);
		atm_release_pool(&spans_msgpool);
	} else
		err = ENXIO;

done:
	(void) splx(s);
	return (err);
}


/*
 * Attach a SPANS-controlled interface
 *
 * Each ATM physical interface must be attached with the signalling
 * manager for the interface's signalling protocol (via the
 * atm_sigmgr_attach function).  This function will handle the
 * attachment for SPANS-controlled interfaces.  A new SPANS protocol
 * instance will be created and then we'll just sit around waiting for
 * status or connection requests.
 *
 * Function must be called at splnet.
 *
 * Arguments:
 *	smp	pointer to SPANS signalling manager control block
 *	pip	pointer to ATM physical interface control block
 *
 * Returns:
 *	0	attach successful
 *	errno	attach failed - reason indicated
 *
 */
static int
spans_attach(smp, pip)
	struct sigmgr	*smp;
	struct atm_pif	*pip;
{
	int		err = 0, n = 0, s;
	struct spans	*spp = NULL;
	struct atm_nif	*np;

	ATM_DEBUG2("spans_attach: smp=%p, pip=%p\n", smp, pip);

	/*
	 * Count network interfaces attached to the physical interface.
	 * If there are more or less than one, we have big problems.
	 */
	np = pip->pif_nif;
	while (np) {
		n++;
		np = np->nif_pnext;
	}
	if (n != 1) {
		err = ETOOMANYREFS;
		goto done;
	}

	/*
	 * Allocate SPANS protocol instance control block
	 */
	spp = (struct spans *)KM_ALLOC(sizeof(struct spans),
			M_DEVBUF, M_NOWAIT);
	if (spp == NULL) {
		err = ENOMEM;
		goto done;
	}
	KM_ZERO(spp, sizeof(struct spans));

	/*
	 * Set variables in SPANS protocol instance control block
	 */
	spp->sp_state = SPANS_INIT;
	spp->sp_h_epoch = time_second;
	spp->sp_s_epoch = 0;
	spp->sp_addr.address_format = T_ATM_ABSENT;
	spp->sp_addr.address_length = 0;
	spp->sp_subaddr.address_format = T_ATM_ABSENT;
	spp->sp_subaddr.address_length = 0;
	spp->sp_probe_ct = 0;
	spp->sp_alloc_vci = SPANS_MIN_VCI;
	spp->sp_alloc_vpi = SPANS_VPI;
	spp->sp_min_vci = SPANS_MIN_VCI;
	spp->sp_max_vci = pip->pif_maxvci;

	/*
	 * Link instance into manager's chain
	 */
	LINK2TAIL((struct siginst *)spp, struct siginst, smp->sm_prinst,
			si_next);

	/*
	 * Link in interface
	 */
	spp->sp_pif = pip;
	pip->pif_sigmgr = smp;
	pip->pif_siginst = (struct siginst *) spp;

	/*
	 * Kick-start the SPANS protocol
	 */
	SPANS_TIMER(spp, 0);

	/*
	 * Notify Connectionless Service
	 */
	err = spanscls_attach(spp);

	/*
	 * Log the fact that we've attached
	 */
	if (!err)
		log(LOG_INFO, "spans: attached to interface %s%d\n",
				pip->pif_name, pip->pif_unit);

done:
	/*
	 * Reset our work if attach fails
	 */
	if (err) {
		if (spp) {
			SPANS_CANCEL(spp);
			UNLINK((struct siginst *)spp, struct siginst,
					smp->sm_prinst, si_next);
			KM_FREE(spp, sizeof(struct spans), M_DEVBUF);
		}
		s = splimp();
		pip->pif_sigmgr = NULL;
		pip->pif_siginst = NULL;
		(void) splx(s);
	}

	return (err);
}


/*
 * Detach a SPANS-controlled interface
 *
 * Each ATM physical interface may be detached from its signalling
 * manager (via the atm_sigmgr_detach function).  This function will
 * handle the detachment for all SPANS-controlled interfaces.  All
 * circuits will be immediately terminated.
 *
 * Function must be called at splnet.
 *
 * Arguments:
 *	pip	pointer to ATM physical interface control block
 *
 * Returns:
 *	0	detach successful
 *	errno	detach failed - reason indicated
 *
 */
static int
spans_detach(pip)
	struct atm_pif	*pip;
{
	struct spans		*spp;
	struct vccb		*vcp, *vnext;
	Atm_connection		*cop;
	int			err;

	ATM_DEBUG1("spans_detach: pip=%p\n", pip);

	/*
	 * Get SPANS protocol instance
	 */
	spp = (struct spans *)pip->pif_siginst;

	/*
	 * Return an error if we're already detaching
	 */
	if (spp->sp_state == SPANS_DETACH) {
		return(EALREADY);
	}

	/*
	 * Cancel any outstanding timer
	 */
	SPANS_CANCEL(spp);

	/*
	 * Notify Connectionless Service
	 */
	spanscls_detach(spp);

	/*
	 * Terminate all of our VCCs
	 */
	for (vcp = Q_HEAD(spp->sp_vccq, struct vccb); vcp; vcp = vnext) {

		vnext = Q_NEXT(vcp, struct vccb, vc_sigelem);

		/*
		 * Don't close the signalling VCC yet
		 */
		if (vcp->vc_connvc && vcp->vc_connvc->cvc_conn ==
				spp->sp_conn)
			continue;

		/*
		 * Close VCC and notify owner
		 */
		err = spans_clear_vcc(spp, (struct spans_vccb *)vcp);
		if (err) {
			log(LOG_ERR, "spans: error %d clearing VCCB %p\n",
					err, vcp);
		}
	}

	/*
	 * Now close the SPANS signalling VCC
	 */
	if ((cop = spp->sp_conn) != NULL) {
		err = atm_cm_release(cop, &spans_cause);
		if (err)
			ATM_DEBUG2(
					"spans_detach: close failed for SPANS signalling channel; cop=%p, err=%d\n",
					cop, err);
	}
	

	/*
	 * Get rid of protocol instance if there are no VCCs queued
	 */
	if (Q_HEAD(spp->sp_vccq, struct vccb) == NULL) {
		struct sigmgr   *smp = pip->pif_sigmgr;

		pip->pif_sigmgr = NULL;
		pip->pif_siginst = NULL;
		UNLINK((struct siginst *)spp, struct siginst,
				smp->sm_prinst, si_next);
		KM_FREE(spp, sizeof(struct spans), M_DEVBUF);
	} else {
		/*
		 * Otherwise, wait for protocol instance to be freed
		 * during spans_free processing for the last queued VCC.
		 */
		spp->sp_state = SPANS_DETACH;
	}

	/*
	 * Log the fact that we've detached
	 */
	log(LOG_INFO, "spans: detached from interface %s%d\n",
			pip->pif_name, pip->pif_unit);

	return (0);
}


/*
 * Open a SPANS ATM Connection
 *
 * All service user requests to open a VC connection (via
 * atm_open_connection) over an ATM interface attached to the SPANS
 * signalling manager are handled here.
 *
 * Function will be called at splnet.
 *
 * Arguments:
 *	cvp	pointer to user's requested connection parameters
 *	errp	pointer to an int for extended error information
 *
 * Returns:
 *	CALL_PROCEEDING	connection establishment is in progress
 *	CALL_FAILED	connection establishment failed
 *	CALL_CONNECTED	connection has been successfully established
 *
 */
static int
spans_setup(cvp, errp)
	Atm_connvc	*cvp;
	int		*errp;
{
	struct atm_pif	*pip = cvp->cvc_attr.nif->nif_pif;
	struct spans	*spp = (struct spans *)pip->pif_siginst;
	int		rc = 0;

	ATM_DEBUG1("spans_setup: cvp=%p\n", cvp);

	/*
	 * Intialize the returned error code
	 */
	*errp = 0;

	/*
	 * Open the connection
	 */
	switch (cvp->cvc_attr.called.addr.address_format) {
	case T_ATM_PVC_ADDR:
		/*
		 * Create a PVC
		 */
		*errp = spans_open_vcc(spp, cvp);
		rc = (*errp ? CALL_FAILED : CALL_CONNECTED);
		break;

	case T_ATM_SPANS_ADDR:

		/*
		 * Create an SVC
		 */
		*errp = spans_open_vcc(spp, cvp);
		rc = (*errp ? CALL_FAILED : CALL_PROCEEDING);
		break;

	default:
		*errp = EPROTONOSUPPORT;
		rc = CALL_FAILED;
	}

	return (rc);
}


/*
 * Close a SPANS ATM Connection
 *
 * All service user requests to terminate a previously open VC
 * connection (via the atm_close_connection function), which is running
 * over an interface attached to the SPANS signalling manager, are
 * handled here.
 *
 * Function will be called at splnet.
 *
 * Arguments:
 *	vcp	pointer to connection's VC control block
 *	errp	pointer to an int for extended error information
 *
 * Returns:
 *	CALL_PROCEEDING	connection termination is in progress
 *	CALL_FAILED	connection termination failed
 *	CALL_CLEARED	connection has been successfully terminated
 *
 */
static int
spans_release(vcp, errp)
	struct vccb	*vcp;
	int		*errp;
{
	int		rc = 0;
	struct atm_pif	*pip = vcp->vc_pif;
	struct spans	*spp = (struct spans *)pip->pif_siginst;

	ATM_DEBUG1("spans_release: vcp=%p\n", vcp);

	/*
	 * Initialize returned error code
	 */
	*errp = 0;

	/*
	 * Make sure VCC is open
	 */
	if ((vcp->vc_sstate == SPANS_VC_NULL) ||
			(vcp->vc_sstate == SPANS_VC_CLOSE) ||
			(vcp->vc_sstate == SPANS_VC_FREE) ||
			(vcp->vc_ustate == VCCU_NULL) ||
			(vcp->vc_ustate == VCCU_CLOSED)) {
		*errp = EALREADY;
		return(CALL_FAILED);
	}

	/*
	 * Validate the connection type (PVC or SVC)
	 */
	if (!(vcp->vc_type & (VCC_PVC | VCC_SVC))) {
		*errp = EPROTONOSUPPORT;
		return(CALL_FAILED);
	}

	/*
	 * Close the VCCB
	 */
	*errp = spans_close_vcc(spp, (struct spans_vccb *)vcp, FALSE);

	/*
	 * Set the return code
	 */
	if (vcp->vc_type & VCC_PVC) {
		rc = (*errp ? CALL_FAILED : CALL_CLEARED);
	} else {
		rc = (*errp ? CALL_FAILED : CALL_PROCEEDING);
	}

	return (rc);
}


/*
 * Accept a SPANS Open from a remote host
 *
 * A user calls this routine (via the atm_accept_call function)
 * after it is notified that an open request was received for it.
 *
 * Function will be called at splnet.
 *
 * Arguments:
 *	vcp	pointer to user's VCCB
 *	errp	pointer to an int for extended error information
 *
 * Returns:
 *	CALL_PROCEEDING	connection establishment is in progress
 *	CALL_FAILED	connection establishment failed
 *	CALL_CONNECTED	connection has been successfully established
 *
 */
static int
spans_accept(vcp, errp)
	struct vccb	*vcp;
	int		*errp;
{
	struct atm_pif		*pip = vcp->vc_pif;
	struct spans		*spp = (struct spans *)pip->pif_siginst;
	struct spans_vccb	*svp = (struct spans_vccb *)vcp;

	ATM_DEBUG1("spans_accept: vcp=%p\n", vcp);

	/*
	 * Initialize the returned error code
	 */
	*errp = 0;

	/*
	 * Return an error if we're detaching
	 */
	if (spp->sp_state == SPANS_DETACH) {
		*errp = ENETDOWN;
		ATM_DEBUG0("spans_accept: detaching\n");
		return(CALL_FAILED);
	}

	/*
	 * Respond to the open request
	 */
	*errp = spans_send_open_rsp(spp, svp, SPANS_OK);
	if (*errp) {
		ATM_DEBUG0("spans_accept: spans_send_open_rsp failed\n");
		goto failed;
	}

	/*
	 * Update the VCC states
	 */
	svp->sv_sstate = SPANS_VC_OPEN;
	svp->sv_ustate = VCCU_OPEN;

	return(CALL_CONNECTED);

failed:
	/*
	 * On error, free the VCCB and return CALL_FAILED
	 */
	svp->sv_sstate = SPANS_VC_FREE;
	svp->sv_ustate = VCCU_CLOSED;
	DEQUEUE(svp, struct spans_vccb, sv_sigelem, spp->sp_vccq);
	spans_free((struct vccb *)svp);

	return(CALL_FAILED);
}


/*
 * Reject a SPANS Open from a remote host
 *
 * A user calls this routine (via the atm_reject_call function)
 * after it is notified that an open request was received for it.
 *
 * Function will be called at splnet.
 *
 * Arguments:
 *	vcp	pointer to user's VCCB
 *	errp	pointer to an int for extended error information
 *
 * Returns:
 *	CALL_CLEARED	call request rejected
 *	CALL_FAILED	call rejection failed
 *
 */
static int
spans_reject(vcp, errp)
	struct vccb	*vcp;
	int		*errp;
{
	struct atm_pif		*pip = vcp->vc_pif;
	struct spans		*spp = (struct spans *)pip->pif_siginst;
	struct spans_vccb	*svp = (struct spans_vccb *)vcp;

	ATM_DEBUG1("spans_reject: vcp=%p\n", vcp);

	/*
	 * Initialize the returned error code
	 */
	*errp = 0;

	/*
	 * Return an error if we're detaching
	 */
	if (spp->sp_state == SPANS_DETACH) {
		*errp = ENETDOWN;
		ATM_DEBUG0("spans_reject: detaching\n");
		return(CALL_FAILED);
	}

	ATM_DEBUG1("spans_reject: cause code is %d\n",
			vcp->vc_connvc->cvc_attr.cause.v.cause_value);

	/*
	 * Clean up the VCCB--the connection manager will free it
	 * spans_close_vcc will send a SPANS open response
	 */
	if ((*errp = spans_close_vcc(spp, svp, TRUE)) != 0) {
		ATM_DEBUG0("spans_reject: spans_close_vcc failed\n");
		return(CALL_FAILED);
	}

	return(CALL_CLEARED);
}


/*
 * Abort a SPANS ATM Connection
 *
 * All (non-user) requests to abort a previously open VC connection (via
 * the atm_abort_connection function), which is running over an
 * interface attached to the SPANS signalling manager, are handled here.
 * The VCC owner will be notified of the request, in order to initiate
 * termination of the connection.
 *
 * Function will be called at splnet.
 *
 * Arguments:
 *	vcp	pointer to connection's VC control block
 *
 * Returns:
 *	0	connection release was succesful
 *	errno	connection release failed - reason indicated
 *
 */
int
spans_abort(vcp)
	struct vccb	*vcp;
{

	/*
	 * Make sure VCC is available
	 */
	if ((vcp->vc_sstate == SPANS_VC_NULL) ||
			(vcp->vc_sstate == SPANS_VC_CLOSE) ||
			(vcp->vc_sstate == SPANS_VC_FREE) ||
			(vcp->vc_ustate == VCCU_NULL) ||
			(vcp->vc_ustate == VCCU_CLOSED)) {
		return(EALREADY);
	}

	/*
	 * Only abort once
	 */
	if (vcp->vc_sstate == SPANS_VC_ABORT) {
		return (EALREADY);
	}

	/*
	 * Cancel any timer that might be running
	 */
	SPANS_VC_CANCEL(vcp);

	/*
	 * Set immediate timer to schedule connection termination
	 */
	vcp->vc_sstate = SPANS_VC_ABORT;
	SPANS_VC_TIMER(vcp, 0);

	return (0);
}


/*
 * Free SPANS ATM connection resources
 *
 * All service user requests to free the resources of a closed
 * VCC connection (via the atm_free_connection function), which
 * is running over an interface attached to the SigPVC signalling
 * manager, are handled here.
 *
 * Function will be called at splnet.
 *
 * Arguments:
 *	vcp	pointer to connection's VC control block
 *
 * Returns:
 *	0	connection free was successful
 *	errno	connection free failed - reason indicated
 *
 */
int
spans_free(vcp)
	struct vccb	*vcp;
{
	struct atm_pif *pip = vcp->vc_pif;
	struct spans *spp = (struct spans *)pip->pif_siginst;

	ATM_DEBUG1("spans_free: vcp = %p\n", vcp);

	/*
	 * Make sure VCC has been closed
	 */
	if ((vcp->vc_ustate != VCCU_CLOSED) ||
			(vcp->vc_sstate != SPANS_VC_FREE)) {
		ATM_DEBUG2("spans_free: bad state, sstate=%d, ustate=%d\n",
				vcp->vc_sstate, vcp->vc_ustate);
		return(EEXIST);
	}

	/*
	 * Remove VCCB from protocol queue
	 */
	DEQUEUE(vcp, struct vccb, vc_sigelem, spp->sp_vccq);

	/*
	 * Free VCCB storage
	 */
	vcp->vc_ustate = VCCU_NULL;
	vcp->vc_sstate = SPANS_VC_NULL;
	atm_free((caddr_t)vcp);

	/*
	 * If we're detaching and this was the last VCC queued,
	 * get rid of the protocol instance
	 */
	if ((spp->sp_state == SPANS_DETACH) &&
			(Q_HEAD(spp->sp_vccq, struct vccb) == NULL)) {
		struct sigmgr   *smp = pip->pif_sigmgr;

		pip->pif_sigmgr = NULL;
		pip->pif_siginst = NULL;
		UNLINK((struct siginst *)spp, struct siginst, smp->sm_prinst,
				si_next);
		KM_FREE(spp, sizeof(struct spans), M_DEVBUF);
	}

	return (0);
}


/*
 * SPANS IOCTL support
 *
 * Function will be called at splnet.
 *
 * Arguments:
 *	code	PF_ATM sub-operation code
 *      data    pointer to code specific parameter data area
 *      arg1    pointer to code specific argument
 *
 * Returns:
 *	0	request procesed
 *	errno	error processing request - reason indicated
 *
 */
static int
spans_ioctl(code, data, arg1)
        int		code;
        caddr_t		data;
        caddr_t		arg1;
{
	struct atmdelreq	*adp;
	struct atminfreq	*aip;
	struct spans		*spp;
	struct spans_vccb	*svp;
	struct air_vcc_rsp	rsp;
	Atm_connection		*cop;
	int			buf_len, err = 0, i, vpi, vci;
	caddr_t			buf_addr;


	switch (code) {

	case AIOCS_DEL_PVC:
	case AIOCS_DEL_SVC:
		/*
		 * Delete a VCC
		 */
		adp = (struct atmdelreq *)data;
		spp = (struct spans *)arg1;

		/*
		 * Don't let a user close the SPANS signalling VC or
		 * the SPANS CLS VC
		 */
		vpi = adp->adr_pvc_vpi;
		vci = adp->adr_pvc_vci;
		if ((vpi == SPANS_SIG_VPI && vci == SPANS_SIG_VCI) ||
				(vpi == SPANS_CLS_VPI &&
				vci == SPANS_CLS_VCI))
			return(EINVAL);

		/*
		 * Find requested VCC
		 */
		for (svp = Q_HEAD(spp->sp_vccq, struct spans_vccb); svp;
				svp = Q_NEXT(svp, struct spans_vccb, sv_sigelem)) {
			if ((svp->sv_vpi == vpi) && (svp->sv_vci == vci))
				break;
		}
		if (svp == NULL)
			return (ENOENT);

		/*
		 * Check VCC type
		 */
		switch (code) {
		case AIOCS_DEL_PVC:
			if (!(svp->sv_type & VCC_PVC)) {
				return(EINVAL);
			}
			break;
		case AIOCS_DEL_SVC:
			if (!(svp->sv_type & VCC_SVC)) {
				return(EINVAL);
			}
			break;
		}

		/*
		 * Schedule VCC termination
		 */
		err = spans_abort((struct vccb *)svp);
		break;

	case AIOCS_INF_VCC:
		/*
		 * Return VCC information
		 */
		aip = (struct atminfreq *)data;
		spp = (struct spans *)arg1;

		buf_addr = aip->air_buf_addr;
		buf_len = aip->air_buf_len;

		/*
		 * Loop through the VCC queue
		 */
		for (svp = Q_HEAD(spp->sp_vccq, struct spans_vccb); svp;
				svp = Q_NEXT(svp, struct spans_vccb, sv_sigelem)) {
			/*
			 * Make sure there's room in the user's buffer
			 */
			if (buf_len < sizeof(rsp)) {
				err = ENOSPC;
				break;
			}

			/*
			 * Fill out the response struct for the VCC
			 */
			(void) snprintf(rsp.avp_intf,
				    sizeof(rsp.avp_intf), "%s%d",
					spp->sp_pif->pif_name,
					spp->sp_pif->pif_unit);
			rsp.avp_vpi = svp->sv_vpi;
			rsp.avp_vci = svp->sv_vci;
			rsp.avp_type = svp->sv_type;
			rsp.avp_aal = svp->sv_connvc->cvc_attr.aal.type;
			rsp.avp_sig_proto = svp->sv_proto;
			cop = svp->sv_connvc->cvc_conn;
			if (cop)
				rsp.avp_encaps = cop->co_mpx;
			else
				rsp.avp_encaps = 0;
			rsp.avp_state = svp->sv_sstate;
			KM_ZERO(rsp.avp_owners, sizeof(rsp.avp_owners));
			for (i = 0; cop && i < sizeof(rsp.avp_owners);
					cop = cop->co_next,
					i += T_ATM_APP_NAME_LEN+1) {
				strncpy(&rsp.avp_owners[i],
					cop->co_endpt->ep_getname(cop->co_toku),
					T_ATM_APP_NAME_LEN);
			}
			rsp.avp_daddr.address_format = T_ATM_SPANS_ADDR;
			rsp.avp_daddr.address_length = 
					sizeof(Atm_addr_spans);
			if (svp->sv_type & VCC_OUT) {
				spans_addr_copy(&svp->sv_conn.con_dst,
						rsp.avp_daddr.address);
			} else {
				spans_addr_copy(&svp->sv_conn.con_src,
						rsp.avp_daddr.address);
			}
			rsp.avp_dsubaddr.address_format = T_ATM_ABSENT;
			rsp.avp_dsubaddr.address_length = 0;
			rsp.avp_ipdus = svp->sv_ipdus;
			rsp.avp_opdus = svp->sv_opdus;
			rsp.avp_ibytes = svp->sv_ibytes;
			rsp.avp_obytes = svp->sv_obytes;
			rsp.avp_ierrors = svp->sv_ierrors;
			rsp.avp_oerrors = svp->sv_oerrors;
			rsp.avp_tstamp = svp->sv_tstamp;

			/*
			 * Copy the response into the user's buffer
			 */
			if ((err = copyout((caddr_t)&rsp, buf_addr,
					sizeof(rsp))) != 0)
				break;
			buf_addr += sizeof(rsp);
			buf_len -= sizeof(rsp);
		}

		/*
		 * Update the buffer pointer and length
		 */
		aip->air_buf_addr = buf_addr;
		aip->air_buf_len = buf_len;
		break;

	case AIOCS_ADD_ARP:
	case AIOCS_DEL_ARP:
	case AIOCS_INF_ARP:
	case AIOCS_INF_ASV:
		/*
		 * ARP specific ioctl's
		 */
		err = spansarp_ioctl(code, data, arg1);
		break;

	default:
		err = EOPNOTSUPP;
	}

	return (err);
}


#ifdef ATM_SPANS_MODULE
/*
 *******************************************************************
 *
 * Loadable Module Support
 *
 *******************************************************************
 */
static int	spans_doload __P((void));
static int	spans_dounload __P((void));

/*
 * Generic module load processing
 * 
 * This function is called by an OS-specific function when this
 * module is being loaded.
 *
 * Arguments:
 *	none
 *
 * Returns:
 *	0 	load was successful 
 *	errno	load failed - reason indicated
 *
 */
static int
spans_doload()
{
	int	err = 0;

	/*
	 * Start us up
	 */
	err = spans_start();
	if (err)
		/* Problems, clean up */
		(void)spans_stop();

	return (err);
}


/*
 * Generic module unload processing
 * 
 * This function is called by an OS-specific function when this
 * module is being unloaded.
 *
 * Arguments:
 *	none
 *
 * Returns:
 *	0 	unload was successful 
 *	errno	unload failed - reason indicated
 *
 */
static int
spans_dounload()
{
	int	err = 0;

	/*
	 * OK, try to clean up our mess
	 */
	err = spans_stop();

	return (err);
}


#ifdef sun
/*
 * Loadable driver description
 */
struct vdldrv spans_drv = {
	VDMAGIC_PSEUDO,	/* Pseudo Driver */
	"spans_mod",	/* name */
	NULL,		/* dev_ops */
	NULL,		/* bdevsw */
	NULL,		/* cdevsw */
	0,		/* blockmajor */
	0		/* charmajor */
};


/*
 * Loadable module support entry point
 * 
 * This is the routine called by the vd driver for all loadable module
 * functions for this pseudo driver.  This routine name must be specified
 * on the modload(1) command.  This routine will be called whenever the
 * modload(1), modunload(1) or modstat(1) commands are issued for this
 * module.
 *
 * Arguments:
 *	cmd	vd command code
 *	vdp	pointer to vd driver's structure
 *	vdi	pointer to command-specific vdioctl_* structure
 *	vds	pointer to status structure (VDSTAT only)
 *
 * Returns:
 *	0 	command was successful 
 *	errno	command failed - reason indicated
 *
 */
int
spans_mod(cmd, vdp, vdi, vds)
	int		cmd;
	struct vddrv	*vdp;
	caddr_t		vdi;
	struct vdstat	*vds;
{
	int	err = 0;

	switch (cmd) {

	case VDLOAD:
		/*
		 * Module Load
		 *
		 * We dont support any user configuration
		 */
		err = spans_doload();
		if (err == 0)
			/* Let vd driver know about us */
			vdp->vdd_vdtab = (struct vdlinkage *)&spans_drv;
		break;

	case VDUNLOAD:
		/*
		 * Module Unload
		 */
		err = spans_dounload();
		break;

	case VDSTAT:
		/*
		 * Module Status
		 */

		/* Not much to say at the moment */

		break;

	default:
		log(LOG_ERR, "spans_mod: Unknown vd command 0x%x\n", cmd);
		err = EINVAL;
	}

	return (err);
}
#endif	/* sun */

#ifdef __FreeBSD__

#include <sys/exec.h>
#include <sys/sysent.h>
#include <sys/lkm.h>

/*
 * Loadable miscellaneous module description
 */
MOD_MISC(spans);


/*
 * Loadable module support "load" entry point
 * 
 * This is the routine called by the lkm driver whenever the
 * modload(1) command is issued for this module.
 *
 * Arguments:
 *	lkmtp	pointer to lkm drivers's structure
 *	cmd	lkm command code
 *
 * Returns:
 *	0 	command was successful 
 *	errno	command failed - reason indicated
 *
 */
static int
spans_load(lkmtp, cmd)
	struct lkm_table	*lkmtp;
	int		cmd;
{
	return(spans_doload());
}


/*
 * Loadable module support "unload" entry point
 * 
 * This is the routine called by the lkm driver whenever the
 * modunload(1) command is issued for this module.
 *
 * Arguments:
 *	lkmtp	pointer to lkm drivers's structure
 *	cmd	lkm command code
 *
 * Returns:
 *	0 	command was successful 
 *	errno	command failed - reason indicated
 *
 */
static int
spans_unload(lkmtp, cmd)
	struct lkm_table	*lkmtp;
	int		cmd;
{
	return(spans_dounload());
}


/*
 * Loadable module support entry point
 * 
 * This is the routine called by the lkm driver for all loadable module
 * functions for this driver.  This routine name must be specified
 * on the modload(1) command.  This routine will be called whenever the
 * modload(1), modunload(1) or modstat(1) commands are issued for this
 * module.
 *
 * Arguments:
 *	lkmtp	pointer to lkm drivers's structure
 *	cmd	lkm command code
 *	ver	lkm version
 *
 * Returns:
 *	0 	command was successful 
 *	errno	command failed - reason indicated
 *
 */
int
spans_mod(lkmtp, cmd, ver)
	struct lkm_table	*lkmtp;
	int		cmd;
	int		ver;
{
	MOD_DISPATCH(spans, lkmtp, cmd, ver,
		spans_load, spans_unload, lkm_nullcmd);
}
#endif	/* __FreeBSD__ */

#else	/* !ATM_SPANS_MODULE */

/*
 *******************************************************************
 *
 * Kernel Compiled Module Support
 *
 *******************************************************************
 */
static void	spans_doload __P((void *));

SYSINIT(atmspans, SI_SUB_PROTO_END, SI_ORDER_ANY, spans_doload, NULL)

/*
 * Kernel initialization
 * 
 * Arguments:
 *	arg	Not used
 *
 * Returns:
 *	none
 *
 */
static void
spans_doload(void *arg)
{
	int	err = 0;

	/*
	 * Start us up
	 */
	err = spans_start();
	if (err) {
		/* Problems, clean up */
		(void)spans_stop();

		log(LOG_ERR, "ATM SPANS unable to initialize (%d)!!\n", err);
	}
	return;
}
#endif	/* ATM_SPANS_MODULE */

