/*-=-=-=-=-=-=-=-=-=-=-=- 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:	rciGlobalMem
 *
 * DESCRIPTION:	Routines to allocate permanent sections of shared memory
 * 		between RCI tasks.       
 *
 * AUTHOR/DATE:	John Lloyd, Fall 1987
 *
 * MODIFICATION HISTORY:
 *
 *		John Lloyd, Fall 1989: Removed the per task 'memory labels',
 *		previously used to reference sharde sections, in favour of
 *		of the global hash table.
 *
 *--------------------------------------------------------------------------*/

#include <errno.h>

#include "../h/rciTask.h"
#include "../h/rciMacros.h"
#include "../h/errorCodes.h"

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

extern int rciNumTasks;

extern RCI_DESC rciTaskTable[];

extern int errno;
static int pageSize = 0;

#ifndef vax
#define ETABLEFULL	0	/* this error does not exist on suns */
#endif

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

static char *nullMsg		= "%s --\n";
static char *addrPageAlignMsg	= "%s -- address 0x%x not page aligned\n";
static char *sizePageAlignMsg	= "%s -- size 0x%x not page aligned\n";
static char *addrRangeMsg	= "%s -- address 0x%x out of CTRL segment\n";
static char *badTaskDescMsg	= "%s -- bad task descriptor\n";
static char *cantExportMsg	= "%s -- can't export 0x%x+%d from cpu %d\n";
static char *cantImportMsg	= "%s -- can't import 0x%x+%d to cpu %d\n";
static char *cantLockMsg	= "%s -- can't lock memory 0x%x+%d\n";
static char *cantAllocMemMsg	= "%s -- can't alloc memory on cpu %d\n";
static char *nameInUseMsg	= "%s -- name %s already in use\n";

extern void *allocSharedMemUpper();
	
void *rciGlobalMemory (label, size, accessMask, hostCpu)
const char *label;
int size;
unsigned long accessMask;
int hostCpu;
{
	char *fxnName = "rciGlobalMemory()";

	void *addr;
	int status;
	int dummy;
	if (!planningLevelCheck(fxnName))
	 { return (NULL);
	 }
	if (label)
	 { if (hashTableCreate() < 0)
	    { errorMsg (getErrorCode(0), nullMsg, fxnName);
	    }
	   if (hashTableLookup (label, GLOBAL_HASH_ID, &dummy, &dummy))
	    { errorMsg (EDuplicate, nameInUseMsg, fxnName, label);
	      RETURN_ERROR (NULL);
	    }
	   if (!hashTableHasRoom(label))
	    { errorMsg (getErrorCode(0), nullMsg, fxnName);
	      RETURN_ERROR (NULL);
	    }
	 }
	if ((addr = allocSharedMemUpper (hostCpu, size)) == NULL)
	 { errorMsg (getErrorCode(0), nullMsg, fxnName);
	   RETURN_ERROR (NULL);
	 }
	if (label)
	 { hashTableInsert (label, GLOBAL_HASH_ID, addr, hostCpu);
	 }
	return (addr);
}

void *rciSharedMemory (label, size, hostTd)
const char *label;
int size;
RCI_DESC *hostTd;
{
	char *fxnName = "rciSharedMemory()";

	int hostCpu;
	int hashId;
	int dummy;
	void *addr;

	if (!planningLevelCheck(fxnName))
	 { return (NULL);
	 }
	if (hostTd->taskCode != (1<<(hostTd->taskID)) )
	 { errorMsg (EDescriptor, badTaskDescMsg, fxnName);
	   return (NULL);
	 }
	hostCpu = TASKCPU(hostTd);
	hashId = SHARED_HASH_ID + TASKNUM(hostTd);

	if (label)
	 { if (hashTableCreate() < 0)
	    { errorMsg (getErrorCode(0), nullMsg, fxnName);
	    }
	   if (hashTableLookup (label, hashId, &dummy, &dummy))
	    { errorMsg (EDuplicate, nameInUseMsg, fxnName, label);
	      RETURN_ERROR (NULL);
	    }
	   if (!hashTableHasRoom(label))
	    { errorMsg (getErrorCode(0), nullMsg, fxnName);
	      RETURN_ERROR (NULL);
	    }
	 }
	if ((addr = allocSharedMemUpper (hostCpu, size)) == NULL)
	 { errorMsg (getErrorCode(0), nullMsg, fxnName);
	   RETURN_ERROR (NULL);
	 }
	if (label)
	 { hashTableInsert (label, hashId, addr, hostCpu);
	 }
	return (addr);
}

void *rciLocalMemory (label, size, hostTd)
const char *label;
int size;
RCI_DESC *hostTd;
{
	char *fxnName = "rciLocalMemory() ";

	int hostCpu;
	int hashId;
	int dummy;
	void *addr;
	int alignedSize;
	int padding;

	if (!planningLevelCheck(fxnName))
	 { return (NULL);
	 }
	if (hostTd->taskCode != (1<<(hostTd->taskID)) )
	 { errorMsg (EDescriptor, badTaskDescMsg, fxnName);
	   return (NULL);
	 }
	hostCpu = TASKCPU(hostTd);
	hashId = LOCAL_HASH_ID + TASKNUM(hostTd);

	/* Keep the page alignment stuff in for now ... cause we have
	   some internal software that relies on it */

	if (!pageSize)
  	 { pageSize = getpagesize();
	 }
	alignedSize = size;
	if ((padding = alignedSize % pageSize) != 0)
	 { alignedSize += pageSize - padding;
	 }
	if (label)
	 { if (hashTableCreate() < 0)
	    { errorMsg (getErrorCode(0), nullMsg, fxnName);
	    }
	   if (hashTableLookup (label, hashId, &dummy, &dummy))
	    { errorMsg (EDuplicate, nameInUseMsg, fxnName, label);
	      RETURN_ERROR (NULL);
	    }
	   if (!hashTableHasRoom(label))
	    { errorMsg (getErrorCode(0), nullMsg, fxnName);
	      RETURN_ERROR (NULL);
	    }
	 }
	if (localMemoryAlloc (hostCpu, &addr, alignedSize) < 0)
	 { errorMsg (EMemory, cantAllocMemMsg, fxnName, TASKCPU(hostTd));
	   return (NULL);
	 }
	if (label)
	 { hashTableInsert (label, hashId, addr, hostCpu);
	 }
	return (addr);
}

rciSharedData (addr, size, hostTd, accessMask)
void *addr;
int size;
RCI_DESC *hostTd;
unsigned long accessMask;
{
	int hostCpu;
	int targetCpu1;
	int targetCpus;
	int cpu;
	int id;
	int export;
	unsigned long access;

#if !defined(VXWORKS_CFG) && !defined(SGI_CFG)
	extern char _RCId_begin;
	extern char _RCId_end;
#endif

	char *fxnName = "rciSharedData()";

	if (!planningLevelCheck(fxnName))
	 { return (0);
	 }
	if (!pageSize)
  	 { pageSize = getpagesize();
	 }
	if (((int)addr) % pageSize)
	 { errorMsg (ENoPageAlign, addrPageAlignMsg, fxnName, addr);
	   RETURN_ERROR (-1);
	 }
	if (size % pageSize)
	 { errorMsg (ENoPageAlign, sizePageAlignMsg, fxnName, addr);
	   RETURN_ERROR (-1);
	 }
#if !defined(VXWORKS_CFG) && !defined(SGI_CFG)
	if ((char*)addr < &_RCId_begin || (char*)addr + size > &_RCId_end)
  	 { errorMsg (ERange, addrRangeMsg, fxnName, addr);
	   RETURN_ERROR (-1);
	 }
#endif
	if (hostTd->taskCode != (1<<(hostTd->taskID)) )
	 { errorMsg (EDescriptor, badTaskDescMsg, fxnName);
	   RETURN_ERROR (-1);
	 }
	accessMask &= ((1<<rciNumTasks)-1);

	hostCpu = hostTd->cpu;
	targetCpus = 0;
	targetCpu1 = 0;
	id = 0;
	for (access = accessMask; access; access >>= 1)
	 { if (access & 1)
	    { targetCpus |= (1<<rciTaskTable[id].cpu);
	      if (!targetCpu1)
	       { targetCpu1 = rciTaskTable[id].cpu;
	       }
	    }
	   id++;
	 }
	targetCpus &= ~(1<<hostCpu);

	if (hostCpu == ARBITER_CPU)
	 { if (targetCpus)
	    { if ((export = exportMemory (ARBITER_CPU, addr,
	      				  size, targetCpu1)) < 0)
	       { errorMsg (getErrorCode(0), cantExportMsg, 
			   fxnName, addr, size, ARBITER_CPU);
		 RETURN_ERROR (-1);
	       }
	    }
	   else
	    { if (lockMemory (addr, size, /*text=*/0) < 0)
	       { errorMsg (errno == ETABLEFULL ? EMemory : EInternal, 
			   cantLockMsg, fxnName, addr, size);
		 RETURN_ERROR (-1);	/* ELockMem */
	       }
	    }
	 }
 	else
  	 { if ((export = exportMemory (hostCpu, addr, size, 0)) < 0)
	    { errorMsg (getErrorCode(0), cantExportMsg, fxnName, 
					 addr, size, hostCpu);
	      RETURN_ERROR (-1);
	    }
	 }

	cpu = 0;
	for ( ; targetCpus; targetCpus >>= 1)
	 { if (targetCpus & 1)
	    { if (importMemory (cpu, export, size, addr) < 0)
	       { errorMsg (getErrorCode(0), cantImportMsg, fxnName,
					    addr, size, cpu);
		 RETURN_ERROR (-1);
	       }
	    }
	   cpu++;
	 }
	return (1);
}
