/*-=-=-=-=-=-=-=-=-=-=-=- 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:	signalRef
 *
 * DESCRIPTION:	These routines connect and disconnect RCI tasks from
 *		UNIX signals. The code is analogous to that used
 *		in 'interruptRef.c'.
 *
 * AUTHOR/DATE:	John Lloyd, Fall 1987
 *
 * MODIFICATION HISTORY:
 *
 *--------------------------------------------------------------------------*/

#include <signal.h>

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

#include "../rci/monitorDefs.h"
#include "../rci/interruptDev.h"
#include "../rci/link.h"

static struct sigvec svec;
static struct sigvec oldvecs[NSIG];
static INTERDEV *signalList[NSIG];

int rciSignalRefCount = 0;

extern int errno;

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

static char *badSignalMsg	= "%s -- %d is not a valid signal\n";
static char *reservedSignalMsg	= "%s -- signal %d reserved is to RCI\n";
static char *outOfMemoryMsg	= "%s -- out of memory\n";
static char *outOfTaskEntriesMsg = "%s -- out of task entries\n";
static char *badSigvecMsg	= "%s -- sigvec() failed, errno %d\n";

#if defined(vax) || defined(sgi) || defined(VXWORKS_CFG) || defined(_AIX)
#ifdef VXWORKS_V5
static void signalHandler(signo)
#else
static int signalHandler(signo)
#endif
#else
static void signalHandler(signo)
#endif
int signo;
{
	monitorInterruptRun (signalList[signo]->queue);
}

static doCleanup ()
{
	INTERDEV *sig;
	int k;

	for (k=0; k<NSIG; k++)
	 { if (svec.sv_mask & sigmask(k))
	    { sig = signalList[k];
	      sigvec (k, &oldvecs[k], NULL);
	      signalList[k] = NULL;
	      if (sig)
	       { FREE (sig);
	       }
	    }
	 }
	svec.sv_mask = 0;
	rciSignalRefCount = 0;
}

static initDone = 0;
#define INIT_CHECK()	if (!initDone) doInit();
static doInit()
{
	/* Called once per program to initialize data structures */

	initDone = 1;
	svec.sv_handler = signalHandler;
	svec.sv_mask = 0;
	svec.sv_flags = 0;
	requestExitFxn(doCleanup,0);
	requestFlagInit(&initDone);
}

referenceSignal (td, signo)
RCI_DESC *td;
int signo;
{	
	/* Connect the task 'td' to the UNIX signal 'signo'.
	   Place the task on the list of tasks for the RCI monitor 
	   to poll when this signal is delivered.

	   RCI takes over the named signal the first time
	   a task is attached to it. RCI restores the signal when
	   the last task drops the reference.
	 */

	char *fxnName = "referenceSignal()";
	INTERDEV *sig;
	
	INIT_CHECK();

	/* check that signo is a valid signal */

	if (signo <= 0 || signo > (NSIG-1))
  	 { errorMsg (EBadDev, badSignalMsg, fxnName, signo);
	   return (-1);
	 }
	if (signo == SIGXCPU || signo == SIGHUP)
	 { errorMsg (EBadDev, reservedSignalMsg, fxnName, signo);
	   return (-1);
	 }

	/* look to see if we are maintaining a descriptor for the signal */

	if (!(svec.sv_mask & sigmask(signo)))
	 { 
	   /* If we aren't maintaining a descriptor, set one up */
	   int k;

	   if ((sig = ALLOC (INTERDEV)) == NULL)
	    { errorMsg (EMemory, outOfMemoryMsg, fxnName);
	      return (-1);
	    }
	   if ((sig->queue = getTaskEntry()) == NULL)
	    { errorMsg (EInternal, outOfTaskEntriesMsg, fxnName);
	      return (-1);
	    }
	   sig->queue->next = sig->queue->prev = sig->queue;
	   sig->devnum = signo;

	   signalList[signo] = sig;
	   svec.sv_mask |= sigmask(signo);

	   if (sigvec (signo, &svec, &oldvecs[signo]) < 0)
	    { errorMsg (EInternal, badSigvecMsg, fxnName, errno);
	      return (-1); 
	    }
	   for (k=0; k<NSIG; k++)
	    { if ((svec.sv_mask & sigmask(k)) && k != signo)
	       { if (sigvec (k, &svec, NULL) < 0)
		  { errorMsg (EInternal, badSigvecMsg, fxnName, errno);
		    return (-1); 
		  }
	       }
	    }
	 }
	else
	 { sig = signalList[signo];
	 }
	
	/* Make sure the task is referenced in the queue for this signal */

	if (insertTaskEntry(td, sig->queue) < 0)
	 { errorMsg (EInternal, outOfTaskEntriesMsg, fxnName);
	   return (-1);
	 }

	rciSignalRefCount++;
	return (1);
}

dereferenceSignal (td, signo)
RCI_DESC *td;
int signo;
{
	/* Remove 'td' from the list of tasks attached to signal 'signo'. */

	char *fxnName = "dereferenceSignal()";
	INTERDEV *sig;
	
	INIT_CHECK();

	/* find the device signo */

	if (!(svec.sv_mask & sigmask(signo)))
	 { return (0);
	 }
	sig = signalList[signo];

	deleteTaskEntry (td, sig->queue);

	if (sig->queue->next == sig->queue)
	 { int k;
	   if (sigvec (signo, &oldvecs[signo], NULL) < 0)
	    { errorMsg (EInternal, badSigvecMsg, fxnName, errno);
	      return (-1);
	    }
	   freeTaskEntry (sig->queue);
	   
	   signalList[signo] = NULL;
	   svec.sv_mask &= ~sigmask(signo);
	   FREE (sig);
	
	   for (k=0; k<NSIG; k++)
	    { if (svec.sv_mask & sigmask(signo))
	       { if (sigvec (k, &svec, NULL) < 0)
		  { errorMsg (EInternal, badSigvecMsg, fxnName, errno);
		    return (-1);
		  }
	       }
	    }
	 }
	rciSignalRefCount--;
	return (1);
}

#define MASK_STK_SIZE	32

static int maskStk[MASK_STK_SIZE];
static int maskPtr = 0;


blockRciSignals()
{
	/* Prevent delivery of signals to which RCI tasks are connected */

	int oldmask;

	INIT_CHECK();

	oldmask = sigblock(0);
	sigsetmask (oldmask | svec.sv_mask);
	maskStk[maskPtr++] = oldmask;
}

unblockRciSignals()
{
	/* Re-enable delivery of task signals */

	INIT_CHECK();

	sigsetmask (maskStk[--maskPtr]);
}

dereferenceAllSignals ()
{
	/* called by rciClose() */

	INTERDEV *sig;
	int k;

	INIT_CHECK();

	for (k=0; k<NSIG; k++)
	 { if (svec.sv_mask & sigmask(k))
	    { TASK_ENTRY *entry, *entry_next;

	      sig = signalList[k];
	      for (entry=sig->queue->next; entry!=sig->queue; entry=entry_next)
	       { entry_next = entry->next;
		 freeTaskEntry (entry);
	       }
	      sigvec (k, &oldvecs[k], NULL);
	      freeTaskEntry (sig->queue);
	      signalList[k] = NULL;
	      FREE (sig);
	    }
	 }
	svec.sv_mask = 0;
	rciSignalRefCount = 0;
}
