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

#include <stdio.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/prctl.h>
#include <sys/schedctl.h>
#include <sys/time.h>
#include <sys/lock.h>
#include <sys/sysmp.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <signal.h>
#include <errno.h>

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

#include "../sys/rciext/rciioctl.h"

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

#include "sgiCtrlTask.h"

#define PLAN_STACK_SIZE	0x40000

/* #define VME_A16NPBASE	0xbd000000	/* XXX XXX XXX */
/* #define VME_A16NPBASE	0xb7c10000	/* XXX XXX XXX */
#define VME_A16NPBASE   HOST_IOPAGE_BASE
#define VME_A16NPSIZE	0x10000

/* #define VME_A24NPBASE	0xb2000000 */
#define VME_A24NPBASE   HOST_BUS_BASE
#define VME_A24NPSIZE	0x1000000

#define VME_A24RCIBASE	(VME_A24NPBASE+0x800000)
#define VME_A24RCISIZE	0x800000

#define NUM_GENRCI_DEVS	4

extern int rciIsOpen;
extern int rciCtrlTaskPid;
extern int rciMainTaskPid;
extern int rciClockFd;
extern int rciPlanCtrlFds[2];
extern int rciCtrlPlanFds[2];

extern FILE *rciStdout;

extern fd_set intrDevFds;
extern IntrDevDesc *intrDevList;

static char *memdev = "/dev/mmem";
static int memFd;

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

extern void rciCtrlTask();

/* ----- 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 *nullCallMsg = "probable call through NULL at control level\n";
static char *noResponseMsg	= "%s -- no response from control task\n";
static char *cantMapMemMsg	= "%s -- can't map %s, errno %d\n";
static char *notSupportedMsg    = "%s -- not supported on this system\n";

extern int errno;

static onSigXcpu();
static doHostClose();
static IntrDevDesc *devLocateById();
static touchMemory();

static initVariables()
{
	rciIsOpen = 0;
	rciCtrlTaskPid = 0;
	rciMainTaskPid = 0;
	rciClockFd = -1;
	rciPlanCtrlFds[0] = -1;
	rciPlanCtrlFds[1] = -1;
	rciCtrlPlanFds[0] = -1;
	rciCtrlPlanFds[1] = -1;
	memFd = -1;
	rciStdout = 0;
	rciBus = (unsigned char*)0;
	rciIOpage = (unsigned char*)0;
	FD_ZERO(&intrDevFds);
}

#define RET_ERROR	doHostClose(); return (-1);

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

	initVariables();

	/* 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 ((rciClockFd = open (devName, O_RDWR)) < 0)
	    { if (errno != EBUSY && errno != ENXIO && errno != ENOENT)
	       { errorMsg (EBadAccess, cantOpenRciDev, fxnName, 
						       devName, errno);
		 RET_ERROR;
	       }
	      if (errno == EBUSY)
	       { numBusy++;
	       }
	    }
	   else
	    { break;
	    }
	 }
	if (k == NUM_GENRCI_DEVS)
	 { if (numBusy)
	    { errorMsg (ENotAvail, noAvailRciDevs, fxnName);
	      RET_ERROR;
	    }
	   else
	    { /* If we can't open any generic RCI devices, then
		 force us into SIMONLY mode. */

	      rciBus = (unsigned char*)0;
	      rciIOpage = (unsigned char*)0;
	      rciSimOnly = 1;
	      rciIsOpen = 1;
	      /* create a duplicate of stdout for the control task ... */
	      if ((fd = dup(1)) < 0)
	       { sysErrorMsg (EInternal, fxnName, "dup");
		 RET_ERROR;
	       }	
	      rciStdout = fdopen (fd, "w");
	      rciMainTaskPid = getpid();
	      return (1);
	    }
	 }
	if (ioctl (rciClockFd, RCIIOC_CONFIRM, 0) < 0)
	 { sysErrorMsg (EBadObj, fxnName, "ioctl CONFIRM");
	   RET_ERROR;
	 }
	if (ioctl (rciClockFd, RCIIOC_TIMER_ENABLE, 1) < 0)
	 { sysErrorMsg (EBadObj, fxnName, "ioctl TIMER ENABLE");
	   RET_ERROR;
	 }

	FD_ZERO(&intrDevFds);

#if defined(uVAX_CFG) || defined(VXWORKS_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);
	   RET_ERROR;
	 }
	rciMallocInit (NULLF, 0, mem, ARB_MALLOC_SIZE);

	if ((memFd = open(memdev,O_RDWR,0)) < 0)
	 { errorMsg (EBadAccess, cantOpenRciDev, fxnName, memdev, errno);
	   RET_ERROR;
	 }
	rciIOpage = (unsigned char*)mmap 
			(0, VME_A16NPSIZE, PROT_READ|PROT_WRITE,
				    MAP_PRIVATE, memFd, VME_A16NPBASE);
	if (rciIOpage == (unsigned char*)-1)
	 { sysErrorMsg (EBadAccess, fxnName, "mmap for rciIOpage:");
	   RET_ERROR;
	 }
#if 1
	rciBus = (unsigned char*)mmap 
			(0, VME_A24RCISIZE, PROT_READ|PROT_WRITE,
				    MAP_PRIVATE, memFd, VME_A24RCIBASE);
	if (rciBus == (unsigned char*)-1)
	 { sysErrorMsg (EBadAccess, fxnName, "mmap for rciBus:");
	   RET_ERROR;
	 }
#endif
	*period = 10;	/* XXX XXX XXX */

	/* now start up the control task ... */

	/* lock down memory and page it in */

	if (plock(PROCLOCK) < 0)
	 { perror("plock:");
	   exit (-1);
	 }
	/* UGHH ... page it all in 'cause plock won't. */
	 { extern etext, edata, _ftext, _fdata;

	   touchMemory ((char*)&_ftext, (char*)&etext);
	   touchMemory ((char*)&_fdata, (char*)&edata);
	 }
	/* shouldn't have to lock stack either, but ... */
	sgiLockStack(PLAN_STACK_SIZE);

	/* open two pipes so that the main task and the control
	   task can communicate ... */

	if ((pipe (rciCtrlPlanFds) < 0) ||
	    (pipe (rciPlanCtrlFds) < 0))
	 { sysErrorMsg (EInternal, fxnName, "pipe");
	   RET_ERROR;
	 }

	/* create a duplicate of stdout for the control task to use ... */

	if ((fd = dup(1)) < 0)
	 { sysErrorMsg (EInternal, fxnName, "dup");
	   RET_ERROR;
	 }	
	rciStdout = fdopen (fd, "w");

	/* Arrange for the planning and control tasks to send
	   themselves a SIGXCPU when they exit. */

	prctl (PR_SETEXITSIG, SIGXCPU);

	/* find out how many CPUs the system has, and the constrain
	   us to run on only one of them. We *should* check to see
	   if the processor is actually available for operation,
	   but we don't worry about that now */
#if 1
	k = sysmp (MP_NPROCS);
	if (k > 1)
	 { if (sysmp (MP_MUSTRUN, 0) < 0)
	    { sysErrorMsg (EInternal, fxnName, "sproc");
	      errorMsg (EInternal, 
"%s -- can't make RCI application run on cpu number 1\n", fxnName);
	      RET_ERROR;
	    }
	 }
#endif
	/* Now create the control task itself. */

	rciMainTaskPid = getpid();
	rciCtrlTaskPid = sproc (rciCtrlTask, PR_SADDR|PR_SFDS);
	if (rciCtrlTaskPid < 0)
	 { sysErrorMsg (EInternal, fxnName, "sproc");
	   RET_ERROR;
	 }
	if ((read_restart (rciCtrlPlanFds[0], &com, 1) != 1) ||
	    (com != RCI_CTRL_ACK))
	 { errorMsg (EInternal, noResponseMsg, fxnName);
	   RET_ERROR;
	 }
	rciIsOpen = 1;
	return (1);
}

static onSigXcpu()
{
	rciCtrlTaskPid = 0;
}

static doHostClose()
{
	int (*oldHandler)();

	if (rciCtrlTaskPid)
	 { oldHandler = signal(SIGXCPU, onSigXcpu);
	   kill (rciCtrlTaskPid, SIGXCPU);
	   while (rciCtrlTaskPid)
	    { nap (0.01);
	    }
	   signal (SIGXCPU, oldHandler);
	 }
	if (rciStdout)
	 { fclose (rciStdout);
	 }
	if (rciCtrlPlanFds[0] != -1)
	 { close (rciCtrlPlanFds[0]);
	 }
	if (rciCtrlPlanFds[1] != -1)
	 { close (rciCtrlPlanFds[1]);
	 }
	if (rciPlanCtrlFds[0] != -1)
	 { close (rciPlanCtrlFds[0]);
	 }
	if (rciPlanCtrlFds[1] != -1)
	 { close (rciPlanCtrlFds[1]);
	 }
	if (rciBus != (unsigned char*)0 && rciBus != (unsigned char*)-1)
	 { munmap (rciBus, VME_A24RCISIZE);
	 }
	if (rciIOpage != (unsigned char*)0 && rciIOpage != (unsigned char*)-1)
	 { munmap (rciIOpage, VME_A16NPSIZE);
	 }
	if (memFd != -1)
	 { close (memFd);
	 }
	if (rciClockFd != -1)
	 { close (rciClockFd);
	 }
	initVariables();

	return (1);
}

rciHostClose()
{
	doHostClose();
}

static IntrDevDesc *devLocateById(fd)
int fd;
{
	IntrDevDesc *d;

	for (d=intrDevList; d; d=d->next)
	 { if (d->fd == fd)
	    { return (d);
	    }
	 }
	return ((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()";

	IntrDevDesc *d;

	if (devLocateById(fd))
	 { errorMsg (EBusy, alreadyAttachedMsg, fxnName);
	   return (-1);
	 }
	if (ioctl (fd, RCIIOC_CONFIRM, 0) < 0)
	 { errorMsg (EBadDev, badIoctlMsg, fxnName, "CONFIRM", errno);
	   return (-1);
	 }
	if ((d = ALLOC(IntrDevDesc)) == NULL)
	 { errorMsg (EMemory, outOfMemoryMsg, fxnName, errno);
	   return (-1);
	 }
	d->fd = fd;
	d->fxn = fxn;
	d->arg = arg;
	if (ioctl (fd, RCIIOC_INTR_ENABLE, 0xff) < 0)
	 { errorMsg (EInternal, badIoctlMsg, fxnName, "INTR_ENABLE 0xff", 
		     errno);
	   FREE (d);
	   return (-1);
	 }
	blockproc (rciCtrlTaskPid);
	LINK_H (intrDevList, d);
	FD_SET (d->fd, &intrDevFds);
	unblockproc (rciCtrlTaskPid);
	return (1);
}

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

	IntrDevDesc *d;
	int rv;

	if ((d = devLocateById(fd)) == NULL)
	 { return (0);
	 }
	d->fxn = 0;
	rv = 1;
	if (ioctl (fd, RCIIOC_INTR_ENABLE, 0) < 0)
	 { errorMsg (EInternal, badIoctlMsg, fxnName, "INTR_ENABLE 0", errno);
	   rv = -1;
	 }
	blockproc (rciCtrlTaskPid);
	FD_CLR (d->fd, &intrDevFds);
	UNLINK_H (intrDevList, d);
	unblockproc (rciCtrlTaskPid);
	FREE ((char*)d);
	return (rv);
}

rciCrashInfo()
{
	return (0);		
}

rciArbClkGetPeriod(period)
int *period;
{
	*period = 10;
}

static touchMemory(addr1, addr2)
char *addr1, *addr2;
{
	/* Touch all memory in the range [addr1, addr2) */
	int pagesize;
	char *cp;
	volatile char c;

	pagesize = getpagesize();
	for (cp=addr1; cp<addr2; cp+=pagesize)
	 { c = *cp;
	 }
}

#else

static int keepRanlibHappy;

#endif
