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

#if !defined(SGI_CFG) && !defined(VXWORKS_CFG) && !defined(LINUX_CFG)

#include <sys/file.h>
#include <errno.h>

#include "../h/cdefs.h"	
#include "../h/rciConfig.h"
#include "../h/rciTask.h"
#include "../h/errorCodes.h"
#if !defined(SIMONLY_CFG) 
#include "../sys/rciext/rciioctl.h"
#endif
#include "../rci/link.h"
#include "../rci/rciAutoAbort.h"

#define NUM_GENRCI_DEVS	4

extern int _rciFd;

struct intrDevDesc {
	struct intrDevDesc *next;
	struct intrDevDesc *prev;
	int fd;
};

#if !defined(SIMONLY_CFG)
static struct RCI_interface interface;
#endif
static int crashSig;
static struct intrDevDesc *intrDevList = NULL;
static int clockAddrSpace;

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

static char *nullMsg		= "%s --\n";
static char *cantOpenRciDev	= "%s -- can't open %s, errno %d\n";
static char *noAvailRciDevs	= "%s -- no generic RCI devices available\n";
static char *badIoctlMsg	= "%s -- %s ioctl failed, errno %d\n";
static char *outOfMemoryMsg	= "%s -- out of memory\n";
static char *alreadyAttachedMsg	= "%s -- device already attached\n";
static char *notOnSimonlyMsg    = "%s -- not supported on simonly system\n";
static char *nullCallMsg = "probable call through NULL at control level\n";
static char *notSupportedMsg    = "%s -- not supported on this system\n";

extern int errno;

/* used by supportFxns.c ... */

extern int (*_rciSignalFxn)();
extern int (*_rciUprintfFxn)();
extern int (*_rciPrintfFxn)();

extern int *_rciClockAddrs[];
extern int *_rciClockHosts[];

extern int (*_rciBadAddrFxn)();
struct timeval (*_rciWallClockFxn)();
extern int (*_rciSetTraceFxn)();
extern int (*_rciClearTraceFxn)();

extern int (*_rciSignalAuxFxn)();
extern int (*_rciSchedAstFxn)();

/* used by atomicOps.c ... */

extern int (*_rciLockReadBuf)();
extern int (*_rciLockWriteBuf)();
extern int (*_rciUnlockBuf)();
extern int (*_rciKutCallFxn)();

extern unsigned char *rciBus;
extern unsigned char *rciIOpage;
extern int rciCpu;
extern int rciSimOnly;

static struct intrDevDesc *devLocateById();
static clockCtrl();

rciHostOpen(sig, period)
int sig;
int *period;
{
	char *fxnName = "rciHostOpen()";
	void *mem;
	int numBusy;
	int k;

#if vax
	/* check against calls through NULL pointers, on VAXen */
	if (rciCpu != ARBITER_CPU || rciInKernelMode())
	 { rciAbort (EInternal, nullCallMsg);
	   return (-1);
	 }
#endif

#if !defined(SIMONLY_CFG)

	/* try and open a generic RCI device */

	numBusy = 0;
	for (k=0; k<NUM_GENRCI_DEVS; k++)
	 { char devName[32];

	   sprintf (devName, "/dev/genrci%d", k);
	   if ((_rciFd = open (devName, O_RDWR)) < 0)
	    { if (errno != EBUSY && errno != ENXIO && errno != ENOENT)
	       { errorMsg (EBadAccess, cantOpenRciDev, fxnName, 
						       devName, errno);
		 return (-1);
	       }
	      if (errno == EBUSY)
	       { numBusy++;
	       }
	    }
	   else
	    { break;
	    }
	 }
	if (k == NUM_GENRCI_DEVS)
	 { if (numBusy)
	    { errorMsg (ENotAvail, noAvailRciDevs, fxnName);
	      return (-1);
	    }
	   else
	    { /* If we can't open any generic RCI devices, then
		 force us into SIMONLY mode. */

	      rciSimOnly = 1;
	      rciBus = (unsigned char*)0;
	      rciIOpage = (unsigned char*)0;
	      _rciClockAddrs[0] = &clockAddrSpace;
	      _rciClockHosts[0] = 0;
	      goto clockOpen;
	    }
	 }
	if (ioctl (_rciFd, RCIIOC_CONFIRM, 0) < 0)
	 { errorMsg (EBadObj, badIoctlMsg, fxnName, "CONFIRM", errno);
	   return (-1);
	 }

	/* tell RCI kernel to deliver 'sig' for kernel level exception */

/* RTUNIX -- we can omit this */
	crashSig = sig;
	if (ioctl (_rciFd, RCIIOC_ONCRASH, &crashSig) < 0)
	 { errorMsg (EInternal, badIoctlMsg, fxnName, "ONCRASH", errno);
	   return (-1);
	 }
/* */
	/* get the kernel level interface addresses */

	if (ioctl (_rciFd, RCIIOC_INTERFACE, &interface) < 0)
	 { errorMsg (EInternal, badIoctlMsg, fxnName, "INTERFACE", errno);
	   return (-1);
	 }

/* IRIX -- we can omit ... */
	rciBus = interface.bus;
	rciIOpage = interface.IOpage;

	_rciSignalFxn = interface.signal;
	_rciUprintfFxn = interface.uprintf;
	_rciPrintfFxn = interface.printf;

	_rciBadAddrFxn = interface.badaddr;
	_rciWallClockFxn = interface.wallclock;
	_rciSetTraceFxn = interface.setTrace;
	_rciClearTraceFxn = interface.clearTrace;

	_rciSignalAuxFxn = interface.sigslave;
	_rciSchedAstFxn = interface.timeout;

	_rciLockReadBuf = interface.lockReadBuf;
	_rciLockWriteBuf = interface.lockWriteBuf;
	_rciUnlockBuf = interface.vsunlock;
	_rciKutCallFxn = interface.kutcall;
/*  ... to here */

#if defined(VXWORKS_CFG) || defined(uVAX_CFG)
#define ARB_MALLOC_SIZE 0x20000 /* 128K */
#else
#define ARB_MALLOC_SIZE	0x40000	/* 256K */
#endif
	if (localMemoryAlloc (ARBITER_CPU, &mem, ARB_MALLOC_SIZE) < 0)
	 {  errorMsg (EMemory, nullMsg, fxnName);
	   return (-1);
	 }
	rciMallocInit (NULLF, 0, mem, ARB_MALLOC_SIZE);

/* IRIX: clock bboards are not needed */
	for (k=0; k<MAX_CPUS; k++)
	 { _rciClockAddrs[k] = &(interface.bboards[ARBITER_CPU]->time_rt[k]);
	   _rciClockHosts[k] = &(interface.bboards[k]->time_rt[k]);
	 }
#else
	rciBus = (unsigned char*)0;
	rciIOpage = (unsigned char*)0;

	_rciClockAddrs[0] = &clockAddrSpace;
	_rciClockHosts[0] = 0;
#endif /* !defined(SIMONLY_CFG) */
clockOpen:
/* IRIX: don't need to open RCI Arb Clk */
	if ((*period = rciArbClkOpen()) <= 0)
	 { 
	   if (*period < 0)
	    { errorMsg (getErrorMsg(0), nullMsg, fxnName);
	      return (-1);
	    }
#if !defined(vax) && !defined(sun)
	   printf ("WARNING: assuming system clock period is 10 msec.\n");
#endif
	   *period = 10;	/* 10 milliseconds */	
	 }
	return (1);
}

rciHostClose()
{
/* IRIX: don't need rciArbClkClose, and *may* not need generic fd either */
	rciArbClkClose();
	close (_rciFd);
	_rciFd = 0;
	return (1);
}

static struct intrDevDesc *devLocateById(fd)
int fd;
{
	struct intrDevDesc *d;

	for (d=intrDevList; d; d=d->next)
	 { if (d->fd == fd)
	    { return (d);
	    }
	 }
	return ((struct intrDevDesc*)NULL);
}

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

	errorMsg (ENotSupported, notSupportedMsg, fxnName);
	RETURN_ERROR (-1);	
}

rciInterruptClose(vector)
int vector;
{
	return (0);
}

rciIntrAttach(fd, fxn, arg)
int fd;
int (*fxn)();
int arg;
{
	char *fxnName = "rciIntrAttach()";

#if defined(SIMONLY_CFG)
	errorMsg (EInternal, notOnSimonlyMsg, fxnName);
	return (-1);
#else
	struct intrDevDesc *d;
	struct RCI_fxn_arg exec;

	if (devLocateById(fd))
	 { errorMsg (EBusy, alreadyAttachedMsg, fxnName);
	   return (-1);
	 }
	if (ioctl (fd, RCIIOC_CONFIRM, 0) < 0)
	 { errorMsg (EBadDev, badIoctlMsg, fxnName, "CONFIRM", errno);
	   return (-1);
	 }
/* IRIX: don't need: */	
	if (ioctl (fd, RCIIOC_ONCRASH, &crashSig) < 0)
	 { errorMsg (EInternal, badIoctlMsg, fxnName, "ONCRASH", errno);
	   return (-1);
	 }
	if ((d = ALLOC(struct intrDevDesc)) == NULL)
	 { errorMsg (EMemory, outOfMemoryMsg, fxnName, errno);
	   return (-1);
	 }
/* IRIX: need to give device id for the RCI control task: */
	d->fd = fd;
	exec.function = fxn;
	exec.argument = arg;
	if (ioctl (fd, RCIIOC_ATTACH, &exec) < 0)
	 { errorMsg (EInternal, badIoctlMsg, fxnName, "ATTACH", errno);
	   FREE (d);
	   return (-1);
	 }
	LINK_H (intrDevList, d);
	return (1);
#endif
}

rciIntrDetach(fd)
int fd;
{
	char *fxnName = "rciIntrDetach()";

#if !defined(SIMONLY_CFG)
	struct intrDevDesc *d;
	int rv;

	if ((d = devLocateById(fd)) == NULL)
	 { return (0);
	 }
	rv = 1;
/* IRIX: tell device to leave RCI control task alone? */
	if (ioctl (fd, RCIIOC_DETACH, 0) < 0)
	 { errorMsg (EInternal, badIoctlMsg, fxnName, "DETACH", errno);
	   rv = -1;
	 }
	UNLINK_H (intrDevList, d);
	FREE ((char*)d);
	return (rv);
#else
	return (0);
#endif
}

rciClockExec (ticks, fxn, arg)
int ticks;
int (*fxn)();
int arg;
{
	char *fxnName = "rciClockExec()";

#if !defined(SIMONLY_CFG)
	struct RCI_heartbeat heartbeat;

	if (rciSimOnly)
	 { return (1);
	 }
/* IRIX: arrange for a SIGALRM on the task? */
	heartbeat.howoften = ticks;
	heartbeat.function = fxn;
	heartbeat.argument = arg;
	if (ioctl (_rciFd, RCIIOC_HEARTBEAT, &heartbeat) < 0)
	 { errorMsg (EInternal, badIoctlMsg, fxnName, "HEARTBEAT", errno);
	   return (-1);
	 }
#endif
	return(1);
}

#if !defined(SIMONLY_CFG)

rciCrashInfo (ci, fdp)
CRASHINFO *ci;
int *fdp;
{
	char *fxnName = "rciCrashInfo()";

	struct intrDevDesc *d;

	if (rciSimOnly)
	 { return (0);
	 }
	if (ioctl (_rciFd, RCIIOC_GETCRASH, &ci) < 0)
	 { errorMsg (EInternal, badIoctlMsg, fxnName, "GETCRASH", errno);
	   return (-1);
	 }
	if (ci->type != CI_NULL)
	 { if (fdp)
	    { *fdp = 0;
	    }
	   return (1);
	 }
	for (d=intrDevList; d; d=d->next)
	 { if (ioctl (d->fd, RCIIOC_GETCRASH, &ci) < 0)
	    { errorMsg (EInternal, badIoctlMsg, fxnName, "GETCRASH", errno);
	      return (-1);
	    }
	   if (ci->type != CI_NULL)
	    { if (fdp)
	       { *fdp = d->fd;
	       }
	      return (1);
	    }
	 }
	return (0);		
}

#else

rciCrashInfo()
{
	return (0);
}

#endif

static char *clkDevName = "/dev/rciclk";
static int clkFd = UNDEF;

/* IRIX: don't need any clock stuff */
rciArbClkOpen()
{
	char *fxnName = "rciArbClkOpen()";
	int period;

#if defined(SIMONLY_CFG)
	return (0);
#else
	if (clkFd == UNDEF)
	 { if ((clkFd = open (clkDevName, O_RDWR)) < 0)
	    { clkFd = UNDEF;
	      if (errno != ENXIO && errno != ENOENT)
	       { sysErrorMsg (EBadDev, fxnName, "open");
		 return (-1);
	       }
	      else
	       { return (0);
	       }
	    }
	 }
	if (ioctl (clkFd, RCIIOC_GETCLKRATE, &period) < 0)
	 { sysErrorMsg (EBadIoctl, fxnName, "ioctl GETCLKRATE");
	   rciArbClkClose();
	   return (-1);
	 }
	return(period);
#endif
}

rciArbClkClose()
{
	if (clkFd != UNDEF)
	 { close (clkFd);
	   clkFd = UNDEF;
	   return (1);
	 }
	else
	 { return (0);
	 }
}

static clockCtrl(fxnName, req, reqName, argp)
char *fxnName;
unsigned long req;
char *reqName;
char *argp;
{
	int rv;

#if defined(SIMONLY_CFG)
	return (0);
#else
	if (rciSimOnly)
	 { return (0);
	 }
	if (clkFd == UNDEF)
	 { if ((clkFd = open (clkDevName, O_RDWR)) < 0)
	    { if (errno != ENXIO && errno != ENOENT)
	       { sysErrorMsg (EBadDev, fxnName, "open");
		 return (-1);
	       }
	      else
	       { return (0);
	       }
	    }
	 }
	rv = 1;
	if (ioctl (clkFd, req, argp) < 0)
	 { sysErrorMsg (EBadIoctl, fxnName, "ioctl");
	   rv = -1;
	 }
	return (rv);
#endif
}

rciArbClkSetPeriod(period)
int period;
{
	char *fxnName = "rciArbClkSetPeriod()";

#if defined(SIMONLY_CFG)
	return (0);
#else
	return (clockCtrl (fxnName, RCIIOC_SETCLKRATE,"SETCLKRATE",&period));
#endif
}

rciArbClkGetPeriod(period)
int *period;
{
	char *fxnName = "rciArbClkGetPeriod()";

#if defined(SIMONLY_CFG)
	return (0);
#else
	return (clockCtrl (fxnName, RCIIOC_GETCLKRATE, "GETCLKRATE", period));
#endif
}

rciArbClkSetDebug(arg)
int arg;
{
	char *fxnName = "rciArbClkSetDebug()";

#if defined(SIMONLY_CFG)
	return (0);
#else
	return (clockCtrl (fxnName, RCIIOC_SETCLKDEBUG, "SETCLKDEBUG", &arg));
#endif
}

rciArbClkEnable()
{
	char *fxnName = "rciArbClkEnable()";

#if defined(SIMONLY_CFG)
	return (0);
#else
	return (clockCtrl (fxnName, RCIIOC_CLKENABLE, "CLKENABLE", NULL));
#endif
}

rciArbClkDisable()
{
	char *fxnName = "rciArbClkDisable()";

#if defined(SIMONLY_CFG)
	return (0);
#else
	return (clockCtrl (fxnName, RCIIOC_CLKDISABLE, "CLKDISABLE", NULL));
#endif
}

rciArbClkStatus(arg)
int *arg;
{
	char *fxnName = "rciArbClkStatus()";

#if defined(SIMONLY_CFG)
	return (0);
#else
	return (clockCtrl (fxnName, RCIIOC_CLKSTATUS, "CLKSTATUS", arg));
#endif
}

#endif /* !defined(SGI_CFG) && !defined(VXWORKS_CFG) */
