/*-=-=-=-=-=-=-=-=-=-=-=- 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:	atomicOps
 *
 * DESCRIPTION:	Routines to do atomic operations on data structures.
 *		At the moment, these include 'testAndSet' and
 *		copying of a structure.
 *
 * CAVEATS:	When executed from the planning level, the
 *		routines must presently be called
 *		in kernel mode to prevent interruption by the ARBITER
 *		control level. This requires executing through a 'cmkrnl()'
 *		("Change Mode to KeRNeL") syscall, which slows things down
 *		a lot. This should be cleaned up when we give the planning
 *		level the ability to mask out RCI control level interrupts.
 *
 * AUTHOR/DATE:	John Lloyd, Fall 1987
 *
 * MODIFICATION HISTORY:
 *
 *--------------------------------------------------------------------------*/

#ifdef LINUX_CFG

#include "../h/cdefs.h"
#include "../h/mutex.h"
#include "../h/rciConfig.h"
#include "../rci/maskInterrupts.h"

extern MUTEX mutexTestAndSet;

extern int rciSignalRefCount;
extern int rciTaskId;
extern int rciCpu;
extern int rciIsOpen;

extern int rciCtrlTaskPid; /* IRIX, INIT */

static int initDone = 0;
atomicOpsInit()
{
	/* this routine MUST be called be RCI setup code */
	initDone = 1;
	mutexInit(rciCpu);
}

/* Routines to implement testAndSet() ... */

testAndSet (flag, mutex)
int *flag;
MUTEX *mutex;
{
	char *fxnName = "testAndSet()";
	int code;

	if (!(MUTEX_VALID(mutex)))
	 { mutexAbort(fxnName, mutex);
	   return(-1);
	 }
	if (!rciIsOpen)
	 { /* then no other RCI tasks could have been set up yet */
	   code = (*flag ? 0 : (*flag = 1));
	 }
	else if (rciInKernelMode())
	 { mutexBegin (mutex);
	   if (*flag == 0)
	    { *flag = 1;
	      code = 1;
	    }
	   else
	    { code = 0;
	    }
	   mutexEnd (mutex);
	 }
	else
	 { if (rciTaskId == 0 && rciSignalRefCount > 0)
	    { blockRciSignals();
	    }
	 /*	   blockproc (rciCtrlTaskPid); SGI Call */
	   mutexBegin (mutex);
	   if (*flag == 0)
	    { *flag = 1;
	      code = 1;
	    }
	   else
	    { code = 0;
	    }
	   mutexEnd (mutex);
	   /*	   unblockproc (rciCtrlTaskPid); */
	   if (rciTaskId == 0 && rciSignalRefCount > 0)
	    { unblockRciSignals();
	    }
	 }
	return (code);
}

atomicCopy (mutex, src, dest, size)
MUTEX *mutex;
void *src;
void *dest;
int size;
{
	char *fxnName = "atomicCopy()";

	if (!(MUTEX_VALID(mutex)))
	 { mutexAbort(fxnName, mutex);
	   return(-1);
	 }
	if (!rciIsOpen) /* then no other RCI tasks have been set up yet */
	 { bcopy (src, dest, size);	      
	 }
	else if (rciInKernelMode())
	 { mutexBegin (mutex);
	   bcopy (src, dest, size);
	   mutexEnd (mutex);
	 }
	else
	 { if (rciTaskId == 0 && rciSignalRefCount > 0)
	    { blockRciSignals();
	    }
	 /*	   blockproc (rciCtrlTaskPid); */
	   mutexBegin (mutex);
	   bcopy (src, dest, size);
	   mutexEnd (mutex);
	   /*	   unblockproc (rciCtrlTaskPid); */
	   if (rciTaskId == 0 && rciSignalRefCount > 0)
	    { unblockRciSignals();
	    }
	 }
	return (1);
}

/* Routines to implement atomicSet(), atomicClear(), and atomicPut() ... */

#define SET_CMD		0x1
#define CLEAR_CMD	0x2
#define PUT_CMD		0x3

unsigned long setBitMask (addr, mask, cmd)
unsigned long *addr;
unsigned long mask;
int cmd;
{
	/* sets the bits as described by cmd and returns the entire
	   setting of the previous bit mask */

	unsigned long oldValue;
	
	oldValue = *addr;
	if (cmd == SET_CMD)
	 { *addr |= mask;
	 }
	else if (cmd == CLEAR_CMD)
	 { *addr &= ~(mask);
	 }
	else if (cmd == PUT_CMD)
	 { *addr = mask;
	 }
	return (oldValue);
}

unsigned long atomicSetCmd (mutex, addr, mask, cmd)
MUTEX *mutex;
unsigned long *addr;
unsigned long mask;
int cmd;
{
	unsigned long oldValue;

	if (!rciIsOpen)	/* then no RCI tasks could have been set up yet */
	 { oldValue = setBitMask (addr, mask, cmd);
	 }
	else
	 { if (rciInKernelMode())
	    { mutexBegin (mutex);
	      oldValue = setBitMask (addr, mask, cmd);
	      mutexEnd (mutex);
	    }
	   else
	    { if (rciTaskId == 0 && rciSignalRefCount > 0)
	       { blockRciSignals();
	       }
	    /*	      blockproc (rciCtrlTaskPid); */
	      oldValue = setBitMask (addr, mask, cmd);
	      /*	      unblockproc (rciCtrlTaskPid); SGI call */
	      if (rciTaskId == 0 && rciSignalRefCount > 0)
	       { unblockRciSignals();
	       }
	    }
	 }
	return (oldValue);
}

unsigned long atomicSet (mutex, addr, mask)
MUTEX *mutex;
unsigned long *addr;
unsigned long mask;
{
	/* do (*addr |= mask) atomically */

	if (!(MUTEX_VALID(mutex)))
	 { mutexAbort("atomicSet()", mutex);
	   return(-1);
	 }
	return (atomicSetCmd (mutex, addr, mask, SET_CMD));
}

unsigned long atomicClear (mutex, addr, mask)
MUTEX *mutex;
unsigned long *addr;
unsigned long mask;
{
	/* do (*addr &= ~mask) atomically */

	if (!(MUTEX_VALID(mutex)))
	 { mutexAbort("atomicClear()", mutex);
	   return(-1);
	 }
	return (atomicSetCmd (mutex, addr, mask, CLEAR_CMD));
}

unsigned long atomicPut (mutex, addr, mask)
MUTEX *mutex;
unsigned long *addr;
unsigned long mask;
{
	/* do (*addr |= mask) atomically */

	if (!(MUTEX_VALID(mutex)))
	 { mutexAbort("atomicPut()", mutex);
	   return(-1);
	 }
	return (atomicSetCmd (mutex, addr, mask, PUT_CMD));
}

#else /* !defined(LINUX_CFG) */

static int keepRanlibHappy;

#endif

