/*
 *
 * ===================================
 * 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.
 *
 *	@(#) $Id: fore_vcm.c,v 1.3 1998/10/31 20:06:53 phk Exp $
 *
 */

/*
 * FORE Systems 200-Series Adapter Support
 * ---------------------------------------
 *
 * Virtual Channel Management
 *
 */

#include <dev/hfa/fore_include.h>

#ifndef lint
__RCSID("@(#) $Id: fore_vcm.c,v 1.3 1998/10/31 20:06:53 phk Exp $");
#endif


/*
 * VCC Stack Instantiation
 * 
 * This function is called via the common driver code during a device VCC
 * stack instantiation.  The common code has already validated some of
 * the request so we just need to check a few more Fore-specific details.
 *
 * Called at splnet.
 *
 * Arguments:
 *	cup	pointer to device common unit
 *	cvp	pointer to common VCC entry
 *
 * Returns:
 *	0	instantiation successful
 *	err 	instantiation failed - reason indicated
 *
 */
int
fore_instvcc(cup, cvp)
	Cmn_unit	*cup;
	Cmn_vcc		*cvp;
{
	Fore_vcc	*fvp = (Fore_vcc *)cvp;
	Atm_attributes	*ap = &fvp->fv_connvc->cvc_attr;

	/*
	 * Validate requested AAL
	 */
	switch (ap->aal.type) {

	case ATM_AAL0:
		fvp->fv_aal = FORE_AAL_0;
		break;

	case ATM_AAL3_4:
		fvp->fv_aal = FORE_AAL_4;
		if ((ap->aal.v.aal4.forward_max_SDU_size > FORE_IFF_MTU) ||
		    (ap->aal.v.aal4.backward_max_SDU_size > FORE_IFF_MTU))
			return (EINVAL);
		break;

	case ATM_AAL5:
		fvp->fv_aal = FORE_AAL_5;
		if ((ap->aal.v.aal5.forward_max_SDU_size > FORE_IFF_MTU) ||
		    (ap->aal.v.aal5.backward_max_SDU_size > FORE_IFF_MTU))
			return (EINVAL);
		break;

	default:
		return (EINVAL);
	}

	return (0);
}


/*
 * Open a VCC
 * 
 * This function is called via the common driver code after receiving a
 * stack *_INIT command.  The common code has already validated most of
 * the request so we just need to check a few more Fore-specific details.
 * Then we just issue the command to the CP.  Note that we can't wait around
 * for the CP to process the command, so we return success for now and abort
 * the connection if the command later fails.
 *
 * Called at splimp.
 *
 * Arguments:
 *	cup	pointer to device common unit
 *	cvp	pointer to common VCC entry
 *
 * Returns:
 *	0	open successful
 *	else 	open failed
 *
 */
int
fore_openvcc(cup, cvp)
	Cmn_unit	*cup;
	Cmn_vcc		*cvp;
{
	Fore_unit	*fup = (Fore_unit *)cup;
	Fore_vcc	*fvp = (Fore_vcc *)cvp;
	H_cmd_queue	*hcp;
	Cmd_queue	*cqp;
	struct vccb	*vcp;

	vcp = fvp->fv_connvc->cvc_vcc;

	ATM_DEBUG4("fore_openvcc: fup=%p, fvp=%p, vcc=(%d,%d)\n", 
		fup, fvp, vcp->vc_vpi, vcp->vc_vci);

	/*
	 * Validate the VPI and VCI values
	 */
	if ((vcp->vc_vpi > fup->fu_pif.pif_maxvpi) ||
	    (vcp->vc_vci > fup->fu_pif.pif_maxvci)) {
		return (1);
	}

	/*
	 * Only need to tell the CP about incoming VCCs
	 */
	if ((vcp->vc_type & VCC_IN) == 0) {
		DEVICE_LOCK((Cmn_unit *)fup);
		fup->fu_open_vcc++;
		fvp->fv_state = CVS_ACTIVE;
		DEVICE_UNLOCK((Cmn_unit *)fup);
		return (0);
	}

	/*
	 * Queue command at end of command queue
	 */
	hcp = fup->fu_cmd_tail;
	if ((*hcp->hcq_status) & QSTAT_FREE) {

		/*
		 * Queue entry available, so set our view of things up
		 */
		hcp->hcq_code = CMD_ACT_VCCIN;
		hcp->hcq_arg = fvp;
		fup->fu_cmd_tail = hcp->hcq_next;
		fvp->fv_flags |= FVF_ACTCMD;

		/*
		 * Now set the CP-resident queue entry - the CP will grab
		 * the command when the op-code is set.
		 */
		cqp = hcp->hcq_cpelem;
		(*hcp->hcq_status) = QSTAT_PENDING;
		cqp->cmdq_act.act_vccid = CP_WRITE(vcp->vc_vci);
		if (fvp->fv_aal == FORE_AAL_0)
			cqp->cmdq_act.act_batch = CP_WRITE(1);
		cqp->cmdq_act.act_spec = CP_WRITE(
			ACT_SET_SPEC(BUF_STRAT_1, fvp->fv_aal,
				CMD_ACT_VCCIN | CMD_INTR_REQ));
	} else {
		/*
		 * Command queue full
		 */
		fup->fu_stats->st_drv.drv_cm_full++;
		return (1);
	}

	return (0);
}


/*
 * Close a VCC
 * 
 * This function is called via the common driver code after receiving a
 * stack *_TERM command.  The common code has already validated most of
 * the request so we just need to check a few more Fore-specific details.
 * Then we just issue the command to the CP.  Note that we can't wait around
 * for the CP to process the command, so we return success for now and whine
 * if the command later fails.
 *
 * Called at splimp.
 *
 * Arguments:
 *	cup	pointer to device common unit
 *	cvp	pointer to common VCC entry
 *
 * Returns:
 *	0	close successful
 *	else 	close failed
 *
 */
int
fore_closevcc(cup, cvp)
	Cmn_unit	*cup;
	Cmn_vcc		*cvp;
{
	Fore_unit	*fup = (Fore_unit *)cup;
	Fore_vcc	*fvp = (Fore_vcc *)cvp;
	H_xmit_queue	*hxp;
	H_cmd_queue	*hcp;
	Cmd_queue	*cqp;
	struct vccb	*vcp;
	int		i, err = 0;

	vcp = fvp->fv_connvc->cvc_vcc;

	ATM_DEBUG4("fore_closevcc: fup=%p, fvp=%p, vcc=(%d,%d)\n", 
		fup, fvp, vcp->vc_vpi, vcp->vc_vci);

	DEVICE_LOCK((Cmn_unit *)fup);

	/*
	 * Clear any references to this VCC in our transmit queue
	 */
	for (hxp = fup->fu_xmit_head, i = 0;
	     (*hxp->hxq_status != QSTAT_FREE) && (i < XMIT_QUELEN);
	     hxp = hxp->hxq_next, i++) {
		if (hxp->hxq_vcc == fvp) {
			hxp->hxq_vcc = NULL;
		}
	}

	/*
	 * Clear any references to this VCC in our command queue
	 */
	for (hcp = fup->fu_cmd_head, i = 0;
	     (*hcp->hcq_status != QSTAT_FREE) && (i < CMD_QUELEN);
	     hcp = hcp->hcq_next, i++) {
		switch (hcp->hcq_code) {

		case CMD_ACT_VCCIN:
		case CMD_ACT_VCCOUT:
			if (hcp->hcq_arg == fvp) {
				hcp->hcq_arg = NULL;
			}
			break;
		}
	}

	/*
	 * If this VCC has been previously activated, then we need to tell
	 * the CP to deactivate it.
	 */
	if (fvp->fv_flags & FVF_ACTCMD) {

		/*
		 * Queue command at end of command queue
		 */
		hcp = fup->fu_cmd_tail;
		if ((*hcp->hcq_status) & QSTAT_FREE) {

			/*
			 * Queue entry available, so set our view of things up
			 */
			hcp->hcq_code = CMD_DACT_VCCIN;
			hcp->hcq_arg = fvp;
			fup->fu_cmd_tail = hcp->hcq_next;

			/*
			 * Now set the CP-resident queue entry - the CP will 
			 * grab the command when the op-code is set.
			 */
			cqp = hcp->hcq_cpelem;
			(*hcp->hcq_status) = QSTAT_PENDING;
			cqp->cmdq_dact.dact_vccid = CP_WRITE(vcp->vc_vci);
			cqp->cmdq_dact.dact_cmd =
				CP_WRITE(CMD_DACT_VCCIN|CMD_INTR_REQ);
		} else {
			/*
			 * Command queue full
			 *
			 * If we get here, we'll be getting out-of-sync with
			 * the CP because we can't (for now at least) do
			 * anything about close errors in the common code.
			 * This won't be too bad, since we'll just toss any
			 * PDUs received from the VCC and the sigmgr's will
			 * always get open failures when trying to use this
			 * (vpi,vci)...oh, well...always gotta have that one
			 * last bug to fix! XXX
			 */
			fup->fu_stats->st_drv.drv_cm_full++;
			err = 1;
		}
	}

	/*
	 * Finish up...
	 */
	if (fvp->fv_state == CVS_ACTIVE)
		fup->fu_open_vcc--;

	DEVICE_UNLOCK((Cmn_unit *)fup);

	return (err);
}

