/*-=-=-=-=-=-=-=-=-=-=-=- 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:	allocMem
 *
 * DESCRIPTION:	Routines for allocating the RCI 'mem' objects.
 *
 * AUTHOR/DATE:	John Lloyd, Fall 1988
 *
 * MODIFICATION HISTORY:
 *
 *		John Lloyd, October 1989: Converted old 'object' package into
 *		the 'allocMem' package.
 *
 *--------------------------------------------------------------------------*/

#include "../h/allocMem.h"
#include "../h/cdefs.h"
#include "../h/mutex.h"
#include "../h/rciMacros.h"
#include "../h/rciTask.h"
#include "../h/errorCodes.h"

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

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

static char *nullMsg		= "%s --\n";
static char *cantSetupCpuPool	= "%s -- can't set up pool for CPU %d\n";
static char *memExists		= "%s -- mem %s already defined\n";
static char *illegalMemPtr	= "%s -- invalid mem pointer (0x%x)\n";
static char *badSizeMsg		= "%s -- illegal size (%d) specified\n";
static char *outOfMemMsg	= "%s -- out of memory\n";

void *allocSharedMemUpper();
void *allocSharedMemLower();

static FIRST_ALLOC_DESC allocPool[MAX_CPUS];	/* object memory pool */
static char allocPoolSet[MAX_CPUS];

static int initDone = 0;
#define INIT_CHECK()	if (!initDone) doInit();
static doInit()
{
	initDone = 1;
	/* assumes memory allocation pools will be returned on task exit */
	bzero ((char*)allocPoolSet, sizeof(allocPoolSet));
	requestFlagInit (&initDone);
}

static setupMemPool (cpu)
int cpu;
{
	void *pool;

	if ((pool = allocSharedMemLower (cpu, MEM_POOL_SIZE)) == NULL)
	 { return (-1);
	 }
	firstAllocInit (&allocPool[cpu], NULLF, 0, pool, MEM_POOL_SIZE);
	allocPoolSet[cpu] = 1;
	return (1);
}

freeMemId (mem, id)
void *mem;
int id;
{
	char *fxnName = "freeMem()";
	MEM_HEADER *ohead;

	if (!planningLevelCheck (fxnName))
	 { return (-1);
	 }
	ohead = (MEM_HEADER*)((char*)mem-sizeof(MEM_HEADER));
	if (!MUTEX_VALID(&(ohead->mutex)))
	 { errorMsg (EBadArg, illegalMemPtr, fxnName, mem);
	   return (-1);
	 }
	if (ohead->name)
	 { hashTableDelete (ohead->name, id, (void*)ohead);
	 }
	ohead->mutex.check = 0;
	firstFree (&allocPool[ohead->cpu], (void*)ohead);
	return (1);
}

freeMem (mem)
void *mem;
{
	char *fxnName = "freeMem()";

	if (freeMemId (mem, MEM_HASH_ID) < 0)
	 { errorMsg (getErrorCode(0), nullMsg, fxnName);
	   RETURN_ERROR (-1);
	 }
	return (1);
}

extern char *hashTableInsert();

void *allocMemId (name, id, memSize, pool)
const char *name;
int id;
int memSize;
int pool;
{
	char *fxnName = "allocMemId()";

	int allocSize;
	char *mem;
	char *raw;

	MEM_HEADER *ohead;

	INIT_CHECK();

	if (!planningLevelCheck())
	 { return (NULL);
	 }

	/* eliminate name in the case of a NULL string */

	if (name && (strlen(name) == 0))
	 { name = NULL;
	 }
	if (pool == UNDEF)
	 { pool = 0;
	 }
	if (memSize < 0)
	 { errorMsg (ERange, badSizeMsg, fxnName, memSize);
	   return (NULL);
	 }
	if (hashTableCreate() < 0)
	 { errorMsg (getErrorCode(0), nullMsg, fxnName);
	   return(NULL);
	 }
	if (!allocPoolSet[pool])
	 { if (setupMemPool(pool) < 0)
	    { errorMsg (getErrorCode(0), cantSetupCpuPool, fxnName, pool);
	      return (NULL);
	    }
	 }
	if (name)
	 { if (getMemByNameId(name, id))
	    { errorMsg (EDuplicate, memExists, fxnName, name);
	      return (NULL);
	    }
	 }

	allocSize = sizeof(MEM_HEADER) + memSize;
	if ((raw = (char*)firstAlloc(&allocPool[pool], allocSize)) == NULL)
	 { errorMsg (EMemory, outOfMemMsg, fxnName);
	   return (NULL);
	 }
	ohead = (MEM_HEADER*)raw;
	ohead->size = memSize;
	MUTEX_CLEAR (&(ohead->mutex));
	ohead->cpu = pool;

	if (name)
	 { char *namep;
	   if ((namep = hashTableInsert (name,id,(void*)ohead, pool)) == NULL)
	    { firstFree (&allocPool[pool], (void*)raw);
	      return (NULL);
	    }
	   else
	    { ohead->name = namep;
	    }	   
	 }
	else
	 { ohead->name = NULL;
	 }
	mem = raw + sizeof(MEM_HEADER);
	bzero ((void*)mem, memSize);
	return ((void*)mem);
}


void *allocMem (name, memSize, pool)
const char *name;
int memSize;
int pool;
{
	char *fxnName = "allocMem()";
	void *mem;

	if ((mem = allocMemId (name, MEM_HASH_ID, memSize, pool)) == NULL)
	 { errorMsg (getErrorCode(0), nullMsg, fxnName);
	   RETURN_ERROR (NULL);
	 }
	return (mem);
}

#if TEST

#ifdef VXWORKS_CFG
#define main amtest_
#endif

#include <ctype.h>
#include "../h/rciOptions.h"

#define	COMMAND(s)	(strcmp (key_buf, s) == 0)
#define	COMMANDS(s)	(strncmp (key_buf, s, strlen(s)) == 0)
char key_buf [256];

#define DO_FIND	1
#define DO_GET	2

main()
{
	int numTasks = 0;
	RCI_DESC *taskDesc[MAX_RCI_TASKS];
	char *memName;
	char *memAck;
	void **memAddr;

	char *keytree, *tree_match_parse(), *tree_match_err_at();
	char name[256];
	char stuff[256];
	int size;
	int cpuSelect;
	int cpu;
	void *addr;

	keytree = tree_match_parse("					\
(	quit								\
	alloc 								\
	 (%s", name, "							\
	   (%d", &size, "						\
	      (%d", &cpu, ")))						\
	[get free getName]						\
	 (%s", name, ")							\
	find								\
	 (%x", &addr, ",,,		\"address (hex)\")		\
	show (table)							\
	write 								\
	 (%s", name, "							\
	   (%s", stuff, "))						\
	read								\
	 (%s", name, ")							\
	task								\
	 (create							\
	   (%s", name, ", 		\"task name\"			\
	     (%x", &cpuSelect, ",,,	\"cpu selection for task (hex)\"))\
	  [get find]							\
	   (%s", name, ",		\"task name\"			\
	     (%s", stuff, ",		\"mem name\"))		\
	 )								\
	rciMalloc							\
	 (%d", &size, ",,,		\"size (bytes) \"		\
	   (%s", name, ",		\"task name\"))			\
	self-test							\
	confirm								\
)",0);
	if (keytree == 0)
	 { printf ("ERROR/Parse error at %.30s\n", tree_match_err_at());
	   exit (-1);
	 }

	rciSetOptions (EXIT_ON_ERROR);

	memName = allocMem ("OBJECT_NAME", 256, 0);
	memAck = allocMem ("OBJECT_ACK", 256, 0);
	memAddr = (void**)allocMem ("OBJECT_ADDR", sizeof(void*), 0);

	rciClearOptions (EXIT_ON_ERROR);

	do
 	 { tree_match (keytree, "AMTEST> ", key_buf);

	   if (COMMAND("alloc"))
	    { void *op;
	      if (strcmp (name, "NULL") == 0)
	       { name[0] = '\0';
	       }
	      if ((op = allocMem (name, size, cpu)) == NULL)
	       { printErrors();
		 clearErrors();
		 printf ("can't alloc mem\n");
	       }
	      else
	       { printf ("0x%x\n", op);
	       }
	    }
	   else if (COMMAND ("get"))
	    { void *op;
	      if ((op = getMemByName (name)) == NULL)
	       { printf ("can't find mem\n");
	       }
	      else
	       { printf ("0x%x\n", op);
	       }
	    }
	   else if (COMMAND ("getName"))
	    { void *op;
	      if ((op = getMemByName (name)) == NULL)
	       { printf ("can't find mem\n");
	       }
	      else
	       { printf ("%s\n", getMemName(op));
	       }
	    }
	   else if (COMMAND ("find"))
	    { if (getMemByAddr(addr) != addr)
	       { printf ("error: find mem returns 0x%x vs. 0x%x\n",
			  getMemByAddr(addr), addr);
	       }
	      else
	       { printf ("0x%x\n", addr);
	       }
	    }
	   else if (COMMAND ("free"))
	    { void *op;
	      int status;

	      if (op = getMemByName (name))
	       { if ((status = freeMem(op)) == 0)
		  { printf ("Mem not found; not freed\n");
		  }
		 else if (status < 0)
		  { printErrors();
		    clearErrors();
		  }
		 continue;
	       }
	      else if (isdigit(name[0]))
	       { char *cp = name;
		 int address = 0;

		 if (cp[0] == '0' && cp[1] == 'x')
		  { cp += 2;
		  }
		 while (*cp != '\0')
		  { if (isxdigit (*cp))
		     { address = address * 16 +
				 (isdigit(*cp) ? (*cp-'0') : (*cp+10-'a'));
		       cp++;
		     }
		    else
		     { break;
		     }
		  }
		 if (*cp == '\0')
		  { printf ("interpreted as hex address ...\n");
		    if ((status = freeMem((void*)address)) == 0)
		     { printf ("Mem not found; not freed\n");
		     }
		    else if (status < 0)
		     { printErrors();
		       clearErrors();
		     }
		    continue;
		  }
	       }
	      printf ("Can't find mem\n");
	    }
	   else if (COMMAND ("write"))
	    { void *mem;
	      if ((mem = getMemByName(name)) == NULL)
	       { printf ("can't find mem\n");
	       }
	      else
	       { writeMem (mem, stuff);
	       }
	    }
	   else if (COMMAND ("read"))
	    { void *mem;
	      if ((mem = getMemByName(name)) == NULL)
	       { printf ("can't find mem\n");
	       }
	      else
	       { readMem (mem, stuff);
		 printf ("%s\n", stuff);
	       }
	    }
	   else if (COMMAND ("task create"))
	    { RCI_DESC *td;

	      if ((td = rciCreate (name, cpuSelect)) == NULL)
	       { printErrors();
		 clearErrors();
		 continue;
	       }
	      if (strcmp (name, "aux1") != 0)
	       { continue;
	       }
	      taskDesc[numTasks++] = td;
	    }
	   else if (COMMAND ("task get") ||
		    COMMAND ("task find"))
	    { RCI_DESC *td;
	      extern printMem();
	      void *mem;
	      int noMem = 0;
	      char ack[256];
	      int arg;

	      if ((td = rciDescriptor (name)) == NULL)
	       { printf ("No task named %s\n", name);
		 continue;
	       }
	      if ((mem = getMemByName(stuff)) == NULL)
	       { printf ("warning: can't find mem %s\n", stuff);
		 noMem = 1;
	       }
	      if (COMMAND ("task get"))
	       { writeMem (memName, stuff);
		 arg = DO_GET;
	       }
	      else
	       { writeMem ((void*)memAddr, &mem);
		 arg = DO_FIND;
	       }
	      if (rciExecute (td, printMem, arg, 0) < 0)
	       { if (!noMem)
		  { printf ("***ERROR***\n");
		    printErrors();
		    clearErrors();
		    continue;
		  }
	       }
	      readMem (memAck, ack);
	      if (strcmp (ack, mem) != 0)
	       { printf ("***ERROR***\n");
		 printf ("task does not provide acknowledge\n");
	       }
	    }
	   else if (COMMAND ("confirm"))
	    { static int ignore = 0;
	      int c;

	      if (!ignore)
	       { printf ("<CR> to continue, 'q' to quit:");
		 switch (c = getchar())
		  { case 'q':
		     { return (0);
		       break;
		     }
		    case '!':
		     { ignore = 1;
		       break;
		     }
		  }
		 while (c != '\n')
		  { c = getchar();
		  }
	       }
	    }
	   else if (COMMAND ("rciMalloc"))
	    { RCI_DESC *td;
	      extern callMalloc();

	      int retval;
	      if ((td = rciDescriptor (name)) == NULL)
	       { printf ("No task named %s\n", name);
		 continue;
	       }
	      if (td->taskID == 0)
	       { printf ("0x%x\n", rciMalloc(size));
	       }
	      else
	       { if (rciExecute (td, callMalloc, size, &retval) < 0)
		  { printErrors();
		    clearErrors();
		  }
		 else
		  { printf ("0x%x\n", retval);
		  }
	       }
	    }
	   else if (COMMAND ("self-test"))
	    { int status;
	      status = automaticTest();
	      if (status)
	       { printErrors();
		 clearErrors();
		 printf ("selt-test failed at point %d\n", status);
	       }
	    }
	 }
	while (!(COMMAND("quit")));
}

automaticTest()
{
	void *addr1;
	void *addr2;
	void *addr3;
	int overhead;
	int i;

	/* routine to automatically test the system */

	rciClearOptions (EXIT_ON_ERROR);

	for (i=0; i<=MEM_POOL_SIZE/2; i = (i==0?1:i*2))
	 { printf ("0x%x\n", i);
	   if ((addr1 = allocMem ("foo1", i, UNDEF)) == NULL)
	    { return (1);
	    }
	   if (freeMem (addr1) <= 0)
	    { return (1);
	    }
	   if ((addr2 = allocMem ("foo2", i, UNDEF)) == NULL)
	    { return (1);
	    }
	   if (addr1 != addr2)
	    { return (1);
	    }
	   if (freeMem (addr2) <= 0)
	    { return (1);
	    }
	   if ((addr1 = allocMem (NULL, i, UNDEF)) == NULL)
	    { return (1);
	    }
	   if (addr1 != addr2)
	    { return (1);
	    }
	   if (freeMem (addr1) <= 0)
	    { return (1);
	    }
	   if ((addr2 = allocMem (NULL, i, UNDEF)) == NULL)
	    { return (1);
	    }
	   if (addr1 != addr2)
	    { return (1);
	    }
	   if (freeMem (addr2) <= 0)
	    { return (1);
	    }
	 }
	return (0);
}

#endif
