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

#include <stdio.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <signal.h>
#include <errno.h>
/* MS For prctl call */
#include <linux/prctl.h>
#include <sched.h> /* For the clone call */

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

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

#include "linCtrlTask.h"

#define PLAN_STACK_SIZE	0x80000


#define NUM_GENRCI_DEVS	4

extern int rciIsOpen;
extern int rciCtrlTaskPid;
extern int rciMainTaskPid;
extern int rciClockFd;
extern int taskCtrlFd;

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 void 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,simflag)
int sig;
int *period;
int simflag;
{
	char *fxnName = "rciHostOpen()";
	void *mem;
	int numBusy;
	char com;
	int fd;
	char contDevName[32];
	char actDevName[32];

	jlsMessageType msg;
        void *stack;
	char junk;


	initVariables();

	if(simflag) {
	  /* MS If we are simulating in Linux, start a new RTLinux process to
	     give us RTI. Do not interfere with the JLS control if we are just
	     running the simulator.
	  */
	   sprintf (contDevName, "/dev/rtf%d", SIM_CONTROL_LEVEL_FIFO);
	   sprintf (actDevName, "/dev/rtf%d", SIM_ACTIVATION_FIFO);
	} else {
	   sprintf (contDevName, "/dev/rtf%d", CONTROL_LEVEL_FIFO);
	   sprintf (actDevName, "/dev/rtf%d", ACTIVATION_FIFO);
	} 
	/* No longer any need to open generic RCI devices.  Instead, here we
	   open up the real-time fifo to communicate with the jls process */
	 
	   if ((rciClockFd = open (contDevName, O_RDONLY)) < 0)
	    { if (errno != EBUSY && errno != ENXIO && errno != ENOENT)
	       { errorMsg (EBadAccess, cantOpenRciDev, fxnName, 
						       contDevName, errno);
		 RET_ERROR;
	       }
	      if (errno == EBUSY)
	       { numBusy++;
	       }
	    }

	/* Start on this fd we can send message to start alarm signals coming 
           from the JLS process.  Do not start these yet. */
	   if ((taskCtrlFd = open (actDevName, O_RDWR)) < 0)
	     { errorMsg (EBadAccess, cantOpenRciDev, fxnName, 
			 actDevName, errno);
	     RET_ERROR;
	     }

	   /* MS Makes sense to clean out the rtf.  There can be packets
	      left in the rtf if the jls had written before going idle
	      or if it is still running. To avoid deadlock, write another 
	      stop command to the jls (it can't hurt) just in case it was
	      left running by a previous process.
	   */
	   /* MS Not sure yet if this is where to put this	   
	      if (rciStopJLS() < 0)
	     rciAbort (ENoDev, "Failed to stop JLS\n");
	   */
	   /* read and disgard 1 character at a time until nothing 
	      left to read */
	   /* 
	      while(read_timeout(rciClockFd,&junk,1,5) == 1);
	   */

	   /* MS */

	   FD_ZERO(&intrDevFds);


#if defined(uVAX_CFG) || defined(VXWORKS_CFG)
#define ARB_MALLOC_SIZE 0x20000 /* 128K */
#else
#define ARB_MALLOC_SIZE	0x40000	/* 256K */
#endif

	*period = 9;	/* 9 milliseconds */

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

	/* 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_SET_PDEATHSIG, SIGXCPU);

	/* Now create the control task itself. */

	rciMainTaskPid = getpid();
	/* MS clone appears the closest to SGI's sproc */
	/* Start up the control task that will wait for timer event
	   messages to arrive on rciClockFd. Because timer events
	   are not started yet, the task will initiailize and then wait
	   We need to allocate space for a new stack before calling clone
	   (as per the clone man page).
	*/
	stack = (void *)malloc(PLAN_STACK_SIZE);
	rciCtrlTaskPid = __clone (rciCtrlTask, stack,  
				  CLONE_VM|CLONE_FS, 0);
	if (rciCtrlTaskPid < 0)
	 { sysErrorMsg (EInternal, fxnName, "__clone");
	   RET_ERROR;
	 }

	/* This is just a test to see if the control task has
	   been started.  Looks for an acknowledge from the task */

	if ((read_restart (rciCtrlPlanFds[0], &com, 1) != 1) ||
	    (com != RCI_CTRL_ACK))
	 { errorMsg (EInternal, noResponseMsg, fxnName);
	   RET_ERROR;
	 }
	rciIsOpen = 1;
	return (1);
}

static void onSigXcpu()
{
	rciCtrlTaskPid = 0;
}

static doHostClose()
{
	void (*oldHandler)();
	int tout_count=0;

	if (rciCtrlTaskPid)
	 { oldHandler = signal(SIGXCPU, onSigXcpu);
	   kill (rciCtrlTaskPid, SIGXCPU);
	   while (rciCtrlTaskPid && tout_count < 100)
	    { nap (0.01); tout_count++;
	    }
	   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 (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 ((d = ALLOC(IntrDevDesc)) == NULL)
	 { errorMsg (EMemory, outOfMemoryMsg, fxnName, errno);
	   return (-1);
	 }
	d->fd = fd;
	d->fxn = fxn;
	d->arg = arg;
	/* Start up the signals here */
	/*	blockproc (rciCtrlTaskPid); This is an SGI call */
	LINK_H (intrDevList, d);
	FD_SET (d->fd, &intrDevFds);
	/*	unblockproc (rciCtrlTaskPid); SGI Call*/
	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;
	/*	blockproc (rciCtrlTaskPid); SGI Call */
	FD_CLR (d->fd, &intrDevFds);
	UNLINK_H (intrDevList, d);
	/*	unblockproc (rciCtrlTaskPid); SGI Call */
	FREE ((char*)d);
	return (rv);
}

rciCrashInfo()
{
	return (0);		
}

int
rciArbClkGetPeriod(period)
int *period;
{
	*period = 9;  /* ON Linux hard wire system clock to have a 9
			 millisecond period.  This is an integral
			 number (10) of the jls clock period of .9
			 milliseconds */
	return(*period);
}

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
