/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by John E. Lloyd.			    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*---------------------------------------------------------------------------
 *		
 * MODULE NAME:	cpuAlloc
 *
 * DESCRIPTION:	Routines which allocate an auxiliary CPU.
 *
 *		Each RCI auxiliary CPU is mapped onto a UNIX device.
 *		The device driver provides a set of ioctls() that do things
 *		such as boot the CPU, download code into it, and set it
 *		executing. By making the driver for that device exclusive 
 *		open, we can restrict use of any CPU to one RCI program at
 *		at time.
 *
 *		What these routines here do is try to open the device for
 *		for a particular CPU.
 * 
 * CAVEATS:	If MAX_CPUS has been defined as 1, then we assume that 
 *		the system contains only the arbiter CPU, and there are
 *		no CPU 'devices' per-se. These routines then effectively
 *		turn into noops.
 *
 * AUTHOR/DATE:	John Lloyd, Fall 1987
 *
 * MODIFICATION HISTORY:
 *
 *--------------------------------------------------------------------------*/

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

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

#define KAFD(cpu)	cpuFileDescriptors[cpu]

int cpuFileDescriptors[MAX_CPUS];
unsigned long loadedCpus = 0;
extern int errno;
extern int rciSimOnly;

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

static char *badCpuMsg		= "%s -- cpu number (%d) specified\n";
static char *noExistCpuMsg	= "%s -- cpu %d nonexistant\n";
static char *cantOpenCpuMsg	= "%s -- can't open cpu %d, errno %d\n";

static int initDone = 0;
#define INIT_CHECK()	if (!initDone) doInit();
static doInit()
{
	int i;

	initDone = 1;
	for (i=0; i<MAX_CPUS; i++)
	 { cpuFileDescriptors[i] = UNDEF;
	 }
	loadedCpus = 0;
	requestFlagInit(&initDone);
}

/* -------------------------------------------------------------------------

	Assumption about this package:

	When issued a cpu, we do nothing to cause it to
	crash. If it does crash (and we will detect this), we
	consider this a fatal condition and abort the program.

  ------------------------------------------------------------------------ */

openCpu (cpu)
int cpu;
{
	/* Returns 1 if we can open the CPU, 0 if it is busy,
	   and -1 if an erro occurred during the open. If the CPU
	   is already open, then the function just returns 1 immediately

	   The file desciptor (or error obatined when trying to obtain the
	   file descriptor) is stored by the function for later use.
	*/
	   
	char *fxnName = "openCpu()";
	char devName[32];
	int kafd;

	INIT_CHECK();

	if (cpu < 0 || cpu >= MAX_CPUS)
	 { errorMsg (EInternal, badCpuMsg, fxnName, cpu);
	   return (-1);
	 }
#if defined(SIMONLY_CFG) || defined(VXWORKS_CFG)
	if (cpu != 0)
	 { errorMsg (EInternal, badCpuMsg, fxnName, cpu);
	   return (-1);
	 }
	else
	 { return (1);
	 }
#else
	if (rciSimOnly)
	 { if (cpu != 0)
	    { errorMsg (EInternal, badCpuMsg, fxnName, cpu);
	      return (-1);
	    }
	   else
	    { return (1);
	    }
	 }
	if (KAFD(cpu) != UNDEF)		/* If already open, just return  1 */
	 { return (1);
	 }

	if (MAX_CPUS == 1)		/* ARBITER CPU only case */
	 { KAFD(cpu) = 1;
	   return (1);
	 }
	else
	 { 
	   if (cpu == ARBITER_CPU)
	    { strcpy (devName, "/dev/kaD0");
	    }
	   else
	    { sprintf (devName, "/dev/ka%d", cpu);
	    }
	   if ((kafd = open (devName, O_RDWR)) < 0)
	    { switch (errno)
	       { case EBUSY:
		  { return (0);
		    break;
		  }
		 case ENXIO:
		  { errorMsg (ENoDev, noExistCpuMsg, fxnName, cpu);
		    return (-1);
		    break;
		  }
		 default:
		  { errorMsg (EBadAccess, cantOpenCpuMsg, fxnName, cpu,errno);
		    return (-1);
		    break;
		  }
	       }
	    }
	   else
	    { KAFD (cpu) = kafd;
	      return (1);
	    }
	 }
	return (1);
#endif
}

closeCpu (cpu)
{
	/* Close the file descriptor for a CPU device */

	if (rciSimOnly)
	 { return;
	 }
#if !defined(SIMONLY_CFG) && !defined(VXWORKS_CFG)
	if (KAFD(cpu) != UNDEF)
	 { 
	   if (MAX_CPUS > 1)
	    { close (KAFD(cpu));
	    }
	   KAFD(cpu) == UNDEF;
	   loadedCpus &= ~(1<<cpu);
	 }
#endif
}

clearCpus ()
{
	/* Close all file descriptors corresponding to all open
	   CPUs.
	*/
	int i;
	for (i=0; i<MAX_CPUS; i++)
	 { closeCpu (i);
	 }
}

loadCpu (cpu)
int cpu;
{
	/* Opens a CPU and tries to load it. Returns 0 if the CPU is
	   busy, -1 if an error occurred or the CPU is not there,
	   and 1 if the CPU is ready and loaded.

	   The arbiter CPU is not loaded.
	 */
	int status;

	if ((status = openCpu (cpu)) < 1)
	 { return (status);
	 }
	if (cpu == ARBITER_CPU || loadedCpus & (1<<cpu))
	 { return (1);
	 }
	if (loadAuxCpu (cpu) < 0)
	 { return (-1);
	 }
	else
	 { loadedCpus |= (1<<cpu);
	   return (1);
	 }
}
