/*-=-=-=-=-=-=-=-=-=-=-=- 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:	interruptRef
 *
 * DESCRIPTION:	These routines connect and disconnect RCI tasks from
 *		UNIX device interrupts. 
 *
 * AUTHOR/DATE:	John Lloyd, Fall 1987
 *
 * MODIFICATION HISTORY:
 *
 *--------------------------------------------------------------------------*/

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

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

static INTERDEV interruptDevList = { &interruptDevList, &interruptDevList };

static char *nullMsg		= "%s --\n";
static char *outOfMemoryMsg	= "%s -- out of memory\n";
static char *outOfTaskEntriesMsg = "%s -- out of task entries\n";

static doCleanup()
{
	/* called on exit (for vxWorks systems only) */

	INTERDEV *devList = &interruptDevList;
	INTERDEV *dev, *dev_next;

	for (dev = devList->next; dev != devList; dev = dev_next)
	 { /* task entries are reclaimed by the their own module */
	   dev_next = dev->next;
	   FREE (dev);
	 }
}

static int initDone = 0;
#define INIT_CHECK()	if (!initDone) doInit();
static doInit()
{
	initDone = 1;
	interruptDevList.next = interruptDevList.prev = &interruptDevList;
	requestExitFxn (doCleanup, 0);
	requestFlagInit (&initDone);
}

referenceInterrupt (td, fd)
RCI_DESC *td;
int fd;
{	
#ifndef LINUX_CFG
	/* Connect the task 'td' to an interrupt from the 
	   RCI device 'fd'. Place the task on the list of tasks
	   for the RCI monitor to call when an interrupt 
	   occurs on that device.
	 */

	char *fxnName = "referenceInterrupt()";
	int ioctlArg;

	INTERDEV *devList = &interruptDevList;
	INTERDEV *dev;
	
	INIT_CHECK();

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

	for (dev = devList->next; dev != devList; dev = devList->next)
	 { if (dev->devnum == fd)
	    { break;
	    }
	 }
	if (dev == devList)
	 { 
	   /* If we aren't maintaining a descriptor, set one up */

	   extern monitorInterruptRun();

	   if ((dev = ALLOC (INTERDEV)) == NULL)
	    { errorMsg (EMemory, outOfMemoryMsg, fxnName);
	      return (-1);
	    }
	   if ((dev->queue = getTaskEntry()) == NULL)
	    { errorMsg (EInternal, outOfTaskEntriesMsg, fxnName);
	      free ((char*)dev);
	      return (-1);
	    }
	   dev->queue->next = dev->queue->prev = dev->queue;
	   dev->devnum = fd;

	   if (rciIntrAttach (fd, monitorInterruptRun, (int)dev->queue) < 0)
	    { errorMsg (getErrorCode(0), nullMsg, fxnName);
	      free ((char*)dev);
	      freeTaskEntry(dev->queue);
	      return (-1);
	    }
	   LINK (dev, devList);
	 }
	
	/* Make sure the task is referenced in the queue for this
	   device */

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

	return (1);
#endif
}

dereferenceInterrupt (td, fd)
RCI_DESC *td;
int fd;
{
#ifndef LINUX_CFG
	/* Remove 'td' from the list of tasks attached to device 'fd'. */

	char *fxnName = "dereferenceInterrupt()";

	INTERDEV *devList = &interruptDevList;
	INTERDEV *dev;
	int rv;
	
	INIT_CHECK();

	/* find the device fd */

	for (dev = devList->next; dev != devList; dev = dev->next)
	 { if (dev->devnum == fd)
	    { break;
	    }
	 }
	if (dev == devList)
	 { return (0);
	 }

	deleteTaskEntry (td, dev->queue);

	rv = 1;
	if (dev->queue->next == dev->queue)
	 { 
	   /* if device queue is empty, delete device queue */

	   if (rciIntrDetach(dev->devnum) < 0)
	    { errorMsg (EInternal, nullMsg, fxnName);
	      rv = -1;
	    }
	   freeTaskEntry (dev->queue);
	   UNLINK (dev);
	   FREE (dev);
	 }
	return (rv);
#endif
}

dereferenceAllInterrupts ()
{
#ifndef LINUX_CFG
	/* called by rciClose() */

	INTERDEV *devList = &interruptDevList;
	INTERDEV *dev, *dev_next;

	INIT_CHECK();

	for (dev = devList->next; dev != devList; dev = dev_next)
	 { TASK_ENTRY *entry, *entry_next;
	   dev_next = dev->next;
	   for (entry=dev->queue->next; entry!=dev->queue; entry=entry_next)
	    { entry_next = entry->next;
	      freeTaskEntry (entry);
	    }
	   rciIntrDetach(dev->devnum);
	   freeTaskEntry (dev->queue);
	   UNLINK (dev);
	   FREE (dev);
	 }
#endif
}
