/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1991 by John E. Lloyd.				    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
#ifdef VXWORKS_CFG

#include <vxWorks.h>
#include <taskLib.h>
#include <semLib.h>
#ifdef VXWORKS_V5
#include <68k/iv.h>
#else
#include <iv68k.h>
#endif

#include "../h/cdefs.h"	
#include "../h/rciConfig.h"
#include "../h/errorCodes.h"
#include "../h/IOdevice.h"

#include "../rci/link.h"
#include "../rci/vxwCtrlTask.h"
#include "../rci/rciAutoAbort.h"

#define DEFAULT_ARBCLK_PERIOD	10	/* (milliseconds) */

static int arbClkPeriod = UNDEF;

/* ----- Error Messages. Defined externally to save space ----- */

static char *nullMsg		= "%s --\n";
static char *outOfMemoryMsg	= "%s -- out of memory\n";
static char *vectorAttachedMsg	= "%s -- vector %d already attached\n";
static char *vectorOpenMsg	= "%s -- vector %d already openned\n";
static char *cantSetupMsg	= "%s -- can't set up %s, errno %d\n";
static char *alreadyOpenMsg	= "%s -- RCI already openned\n";
static char *badVectorMsg	= "%s -- illegal vector number %d\n";
static char *badIplMsg		= "%s -- illegal ipl number %d\n";
static char *intrNotDefinedMsg	= "%s -- undefined interrupt number %d\n";

IntrDevDesc *intrDevs = NULL;		/* list of allocated interrupts */
IntrDevDesc *attachedIntrDevs = NULL;	/* list of connected interrupts */
extern EXEC_REQ *intrExrList;		/* interrupt polling list */

extern unsigned char *rciBus;		/* VME a24d16 space */
extern unsigned char *rciIOpage;	/* VME a16d16 space */

extern rciInterruptHandler();

int rciCtrlTaskId = 0;			/* task ID for planning task */
int rciPlanTaskId = 0;			/* ID of task running control level */
int rciIsOpen = NO;			/* RCI services are set up */

SEM_ID rciCtrlSem = NULL;		/* used to active control level */

extern int rciCtrlTask();		/* control task function */
extern int rciClockCount;		/* internal RCI clock counter */
extern int rciClockTick();		/* fxn called every RCI clock tick */
extern FUNCPTR intVecGet();		/* sys routine to get vector handler */

#define PLAN_TASK_PRIORITY	10	/*   priority than planning task */
#define CTRL_TASK_PRIORITY	1	/* make sure control task has higher */
#define CTRL_TASK_STACKSIZE	0x20000 /* RCI control task needs big stack */

static planTaskPri = PLAN_TASK_PRIORITY; /* this isn't used right now */
static ctrlTaskPri = CTRL_TASK_PRIORITY; 
static ctrlTaskStk = CTRL_TASK_STACKSIZE;

static fullCleanup();
static rciCleanup();
static IntrDevDesc *locateVectorByNum();

static fullCleanup()
{
	/* Called when the control task exits. */

	IntrDevDesc *d, *d_flink;

	rciCleanup();
	for (d=intrDevs; d; d=d_flink)
	 { d_flink = d->flink;
	   FREE (d);
	 }
	intrDevs = NULL;
}

static rciCleanup()
{
	/* Internal routine to shutdown or deallocate the services 
	   associated with RCI. 
	 */
	IntrDevDesc *d, *d_next;

	/* XXX WARNING: this routine suffers from re-entrancy problems XXX */

	if (rciIsOpen)
	 { if (rciCtrlTaskId)
	    { if (!taskIsSuspended(rciCtrlTaskId))
	       { taskDelete (rciCtrlTaskId);
		 rciCtrlTaskId = 0;
	       }
	    }
	   for (d=attachedIntrDevs; d; d=d_next)
	    { d_next = d->next;
	      rciIntrDetach(d->vector);
	    }
	   rciArbClkClose();
	   if (rciCtrlSem)
	    { semDelete (rciCtrlSem);
	      rciCtrlSem = NULL;
	    }
	   rciPlanTaskId = 0;
	   rciIsOpen = 0;
	 }
}

setCtrlTaskPri(pri)
int pri;
{
	int old = ctrlTaskPri;

	ctrlTaskPri = (pri >= 0 ? pri : CTRL_TASK_PRIORITY);
	return (old);
}

setCtrlTaskStk(stk)
int stk;
{
	int old = ctrlTaskStk;

	ctrlTaskStk = (stk >= 0 ? stk : CTRL_TASK_STACKSIZE);
	return (old);
}

rciHostOpen(sig, period)
int sig;
int *period;
{
	/* Sets up the RCI control task intializes other services used by
	   RCI. Usually called once at the start of the program.
	 */

	char *fxnName = "rciHostOpen()";
	int k;

	if (rciIsOpen)
	 { errorMsg (EBusy, alreadyOpenMsg, fxnName);
	   return (-1);
	 }

	/* create the RCI control task */

	requestExitFxn(fullCleanup,0);
	if ((rciCtrlSem = semCreate()) == NULL)
	 { errorMsg (EInternal,cantSetupMsg,fxnName,"semaphore",errnoGet());
	   rciCleanup();
	   return (-1);
	 }
	if (rciCtrlTaskId != 0)
	 { taskDelete (rciCtrlTaskId);
	   rciCtrlTaskId = 0;
	 }
	if ((k = taskSpawn ("rciCtrl", 
			     ctrlTaskPri,
			     (VX_SUPERVISOR_MODE |
			      VX_DEALLOC_STACK |
			      VX_STDIO |
			      VX_FP_TASK),
			     ctrlTaskStk,
			     rciCtrlTask,
			     sig,
			     0,0,0, 0,0,0, 0,0,0)) == ERROR)
	 { errorMsg (EInternal,cantSetupMsg,fxnName,"ctrl task",errnoGet());
	   rciCleanup();
	   return (-1);
	 }
	rciCtrlTaskId = k;
	if (arbClkPeriod == UNDEF)
	 { rciArbClkSetPeriod(DEFAULT_ARBCLK_PERIOD);
	 }
	rciBus = (unsigned char*)HOST_BUS_BASE;
	rciIOpage = (unsigned char*)HOST_IOPAGE_BASE;
	rciPlanTaskId = taskIdSelf();
	k = rciArbClkOpen();
	if (period)
	 { *period = k;
	 }
	rciIsOpen = 1;
/*	taskPrioritySet(taskIdSelf(), PLAN_TASK_PRIORITY); */
	
	return (1);
}

rciHostClose()
{
	/* Deletes the RCI control task. Called from rciClose(), or
	   upon the exit of the planning level task.
	 */
	rciCleanup();
	return (1);
}

static IntrDevDesc *locateVectorByNum(vector)
int vector;
{
	/* locate the descriptor block for an interrupt by number */

	IntrDevDesc *d;

	for (d=intrDevs; d; d=d->flink)
	 { if (d->vector == vector)
	    { return (d);
	    }
	 }
	return ((IntrDevDesc*)NULL);
}

rciInterruptOpen (intr, vector)
IO_INTERRUPT *intr;
int vector;
{
	char *fxnName = "rciInterruptOpen()";
	IntrDevDesc *d;

	if (vector < 0 || vector > 255)
	 { errorMsg (ERange, badVectorMsg, fxnName, vector);
	   RETURN_ERROR (-1);
	 }
	if (intr->ipl < 0 || intr->ipl > 7)
	 { errorMsg (ERange, badIplMsg, fxnName, intr->ipl);
	   RETURN_ERROR (-1);
	 }
	if (locateVectorByNum (vector))
	 { errorMsg (EBusy, vectorOpenMsg, fxnName, vector);
	   RETURN_ERROR (-1);
	 }

	if ((d = ALLOC(IntrDevDesc)) == NULL)
	 { errorMsg (EMemory, outOfMemoryMsg, fxnName);
	   RETURN_ERROR (-1);
	 }
	bzero (d, sizeof(IntrDevDesc));

	d->intr = *intr;
	d->vector = vector;
	d->intr.vector = vector;

	/* link new interrupt into the list of interrupts */

	d->flink = intrDevs;
	d->blink = NULL;
	if (intrDevs)
	 { intrDevs->blink = d;
	 }
	intrDevs = d;
	return (vector);
}

rciInterruptClose (vector)
{
	IntrDevDesc *d;

	if ((d = locateVectorByNum(vector)) == NULL)
	 { return (0);
	 }
	if (d->attached)
	 { rciIntrDetach (vector);
	 }
	if (d->flink)
	 { d->flink->blink = d->blink;
	 }
	if (d->blink)
	 { d->blink->flink = d->flink;
	 }
	else
	 { intrDevs = d->flink;
	 }
	FREE(d);
	return (1);
}

rciIntrAttach(vector, fxn, arg)
int vector;
int (*fxn)();
int arg;
{
	/* Attach the RCI task to the interrupt for the
	   specified vector.
	 */	   
	char *fxnName = "rciIntrAttach()";

	IntrDevDesc *d;
	EXEC_REQ *exr;
	int s;

	if ((d = locateVectorByNum(vector)) == NULL)
	 { errorMsg (ENotDefined, intrNotDefinedMsg, fxnName, vector);
	   return (-1);
	 }
	if (d->attached)
	 { errorMsg (EBusy, vectorAttachedMsg, fxnName, vector);
	   return (-1);
	 }

	d->oldfxn = intVecGet(INUM_TO_IVEC(vector));
	exr = &d->exr;

	s = VXINT_LOCK();
	intConnect (INUM_TO_IVEC(vector), rciInterruptHandler, (int)d);
	exr->fxn = fxn;
	exr->arg = arg;
	exr->request = 0;
	LINK_H (intrExrList, exr);

	sysIntEnable(d->intr.ipl);
	(*d->intr.intrAttach)(&d->intr, /*enable=*/1);

	VXINT_UNLOCK(s);
	/* spurious interrupt may occur right here, but will be 
	   ignored if d->attached is not set */

	LINK_H (attachedIntrDevs, d);
	d->attached = 1;

	return (1);
}

rciIntrDetach(vector)
int vector;
{
	/* detach an interrupt from RCI */

       	char *fxnName = "rciIntrDetach()";

	IntrDevDesc *d;
	EXEC_REQ *exr;
	int s;

	if ((d = locateVectorByNum(vector)) == NULL)
	 { return (0);
	 }
	exr = &d->exr;

	s = VXINT_LOCK();
	intVecSet (INUM_TO_IVEC(vector), d->oldfxn);
	exr->fxn = 0;
	exr->arg = 0;
	exr->request = 0;
	UNLINK_H (intrExrList, exr);

	(*d->intr.intrAttach)(&d->intr, /*enable=*/0);

	VXINT_UNLOCK(s);

	UNLINK_H (attachedIntrDevs, d);
	d->attached = 0;

	return (1);
}

rciClockExec (period, fxn, arg)
int period;
int (*fxn)();
int arg;
{
	/* Tell the system to execute an RCI control fxn periodically */

	extern EXEC_REQ clockExr;
	extern int clockReqPeriod;
	extern int clockReqCount;
	int s;

	s = VXINT_LOCK();

	if (fxn)
	 { clockReqPeriod = period;
	   clockReqCount = rciClockCount + period;
	   clockExr.fxn = fxn;
	   clockExr.arg = arg;
	 }
	else
	 { clockReqPeriod = UNDEF;
	   clockReqCount = UNDEF;
	   clockExr.fxn = 0;
	 }
	clockExr.request = 0;

	VXINT_UNLOCK(s);
	return(1);
}

#define MSEC_PER_SEC	1000

rciArbClkOpen()
{
	/* start the RCI system clock */

	int s;

	sysAuxClkDisable();
	/* Argh! we have to lock interrupts because sysAuxClkConnect()
	   doesn't seem to! */	   
	s = intLock();	
	sysAuxClkConnect(rciClockTick, 0);
	sysAuxClkEnable();
	intUnlock(s);
	return (arbClkPeriod);
}

rciArbClkClose()
{
	/* stop the RCI system clock */

	sysAuxClkDisable();
	return (1);
}

rciArbClkSetPeriod(period)
int period; /* milliseconds */
{
	/* set the rate of the RCI system clock */

	arbClkPeriod = period;
	sysAuxClkRateSet (MSEC_PER_SEC/arbClkPeriod);
	return(1);
}

rciArbClkGetPeriod(period)
int *period;
{
	/* get the rate of the RCI system clock */

	*period = arbClkPeriod;
	return (1);
}

rciArbClkSetDebug(arg)
int arg;
{
	/* not used */
	return (1);
}

rciArbClkEnable()
{
	/* not used -- clock is always "enabled" */
	return (1);
}

rciArbClkDisable()
{
	/* not used -- clock is always "enabled" */
	return (0);
}

rciArbClkStatus(arg)
int *arg;
{
	/* clock is always enabled */
	return (1);
}

#endif
