/*-=-=-=-=-=-=-=-=-=-=-=- 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:	loadRobot
 *
 * DESCRIPTION:	These routines do the actual work of attaching a robot
 *		to a specific RCI task.
 *
 *		When a robot is attached to a task for the first time,
 *		parameter information about that robot (such as the
 *		contents of the read-only JLS and KYN structures) is
 *		loaded into private storage on each of the RCI task
 *		CPUs. This is done by broadcasting the necessary information
 *		to each CPU's task monitor, using the routine 'setupRobot()'.
 *
 *		When a robot is attached to a particular task, then all
 *		the task monitors are informed, so that they can set up
 *		pointers to the necessary shared memory areas, and,
 *		in the case of the host task, initialize those memory areas.
 *		This is done by the routine 'attachRobot()'.
 *
 *		Dereferencing a robot also requires notifying all the task
 *		monitors so that they can bring down the shared memory areas.
 *		This is done by the routine 'detachRobot()'.
 *
 * AUTHOR/DATE:	John Lloyd, Fall 1987
 *
 * MODIFICATION HISTORY:
 *
 *--------------------------------------------------------------------------*/

#include "../h/cdefs.h"
#include "../h/rciTask.h"
#include "../h/errorCodes.h"
#include "../h/rciMacros.h"
#include "../h/IOdevice.h"
#include "../h/robot_kynvar.h"
#include "../h/rciConfig.h"
#include "../h/assignMem.h"

#include "../robots/robotData.h"

#include "../rci/loadRobot.h"
#include "../rci/monitorCom.h"

extern char moncomMsgBuf[MAX_CPUS][MON_MSGBUF_SIZE];
extern char moncomReadBuf[MON_READBUF_SIZE];

#define SOFT	1	/* can do monitor command in user mode on arbiter */

#define PUT_MSG(msg,type,cpu)	*(struct type*)&moncomMsgBuf[cpu][0] = (msg);

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

static char *nullMsg		= "%s --\n";
static char *comRbufOflowMsg	= "%s -- com read buf overflow (size %d)\n";
static char *cantAllocMemMsg	= "%s -- can't allocate memory on cpu %d\n";
static char *cantExportMemMsg	= "%s -- can't export memory on cpu %d\n";
static char *cantImportMemMsg	= "%s -- can't import memory on cpu %d\n";
static char *cantExecMonCmdMsg	= "%s -- can't execute monitor cmd\n";

extern void *allocSharedMemUpper();
	
setupRobot (rdesc, cpuMask)
ROBOT_DESC *rdesc;
unsigned long cpuMask;
{
	/* loads robot information into the monitor on the indicated CPUs */

	char *fxnName = "setupRobot()";
	struct setupRobotMsg msg;

	unsigned long mask;
	int cpu;
	int export;
	int size;
	char *databuf;
	
	/* set up the command message */

	size = ALIGN_SIZE(sizeof(JLS)) +
	       ALIGN_SIZE(sizeof(GEN_KYN)) +
	       ALIGN_SIZE(rdesc->kynSize) +
	       ALIGN_SIZE(rdesc->nameLength);

	if (size > MON_READBUF_SIZE)
	 { errorMsg (EInternal, comRbufOflowMsg, fxnName, MON_READBUF_SIZE);
	   return (-1);
	 }
	databuf = &moncomReadBuf[0];

	msg.id = rdesc->id;
	msg.class = rdesc->class;

	ASSIGN_MEM (msg.jls, JLS*, sizeof(JLS), databuf);
	bcopy (rdesc->jls, msg.jls, sizeof(JLS));

	ASSIGN_MEM (msg.kynGen, GEN_KYN*, sizeof(GEN_KYN), databuf);
	bcopy (rdesc->kyn->gen, msg.kynGen, sizeof(GEN_KYN));

	ASSIGN_MEM (msg.kynPar, void*, rdesc->kynSize, databuf);
	bcopy (rdesc->kyn->ext, msg.kynPar, rdesc->kynSize);

	msg.name = databuf;
	strcpy (databuf, rdesc->name);

	msg.kynSize = rdesc->kynSize;
	msg.varSize = rdesc->varSize;
	msg.comfxns = rdesc->comfxns;

	cpu = 0;
	for (mask = cpuMask; mask; mask >>= 1)
	 { if (mask & 1)
	    { void *addr;
	      if (localMemoryAlloc (cpu, &addr, rdesc->privSize) < 0)
	       { errorMsg (getErrorCode(0), cantAllocMemMsg, fxnName, cpu);
		 return (-1);
	       }
	      rdesc->localMem[cpu] = addr;
	      msg.localMem = rdesc->localMem[cpu];
	      PUT_MSG (msg, setupRobotMsg, cpu);
	    }
	   cpu++;
	 }
	
	if (issueMonitorCommand (MON_SETUP_ROBOT, cpuMask, SOFT) < 0)
	 { errorMsg (EInternal, cantExecMonCmdMsg, fxnName);
	   return (-1);
	 }
	return (1);
}

attachRobot (rdesc, td, dev, cpuMask)
ROBOT_DESC *rdesc;
RCI_DESC *td;
IO_DEVICE *dev;
unsigned long cpuMask;
{
	/* causes the monitors in the indicated mask to attach the 
	  specified robot */

	char *fxnName = "attachRobot()";
	
	unsigned long mask;
	int hostCpu = td->cpu;
	int cpu;

	struct attachRobotMsg msg;

	/* set up some of the command message */

	msg.taskId = TASKNUM(td);
	msg.num = rdesc->num;
	msg.dev = *dev;
	msg.idev = rdesc->idev;
	
	cpu = 0;
	for (mask = cpuMask; mask; mask >>= 1)
	 { if (mask & 1)
	    { if (!rdesc->shareMemEx[hostCpu])
	       { void *addr;
		 addr = allocSharedMemUpper (hostCpu,rdesc->shareSize);
		 if (addr == NULL)
		  { errorMsg(getErrorCode(0),cantAllocMemMsg,fxnName,hostCpu);
		    return (-1);
		  }
		 rdesc->shareMemEx[hostCpu] = addr;
	       }
	      msg.flags = (cpu == hostCpu) ? ROBOT_HOST : 0;
	      msg.shareMem = rdesc->shareMemEx[hostCpu];
	      msg.localMem = rdesc->localMem[cpu];
	      PUT_MSG (msg, attachRobotMsg, cpu);
	    }
	   cpu++;
	 }

	if (issueMonitorCommand (MON_ATTACH_ROBOT, cpuMask, SOFT) < 0)
	 { errorMsg (EInternal, cantExecMonCmdMsg, fxnName);
	   return (-1);
	 }
	return (1);
}

detachRobot (rdesc, td, cpuMask)
ROBOT_DESC *rdesc;
RCI_DESC *td;
unsigned long cpuMask;
{
	/* causes the monitors in the indicated mask to attach the 
	  specficied robot */

	char *fxnName = "detachRobot()";

	unsigned long mask;
	int cpu;
	struct detachRobotMsg msg;
	
	msg.num = rdesc->num;
	msg.taskId = TASKNUM(td);

	cpu = 0;
	for (mask = cpuMask; mask; mask >>= 1)
	 { if (mask & 1)
	    { PUT_MSG (msg, detachRobotMsg, cpu);
	    }
	   cpu++;
	 }

	if (issueMonitorCommand (MON_DETACH_ROBOT, cpuMask, SOFT) < 0)
	 { errorMsg (EInternal, cantExecMonCmdMsg, fxnName);
	   return (-1);
	 }
	return (1);
}
