/*-=-=-=-=-=-=-=-=-=-=-=- 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.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/

/*=~=~=~=~ McGill-Computer-Vision-and-Robotics-Laboratory--C-Module ~=~=~=~=
 *		
 * MODULE NAME:	serialDev.c
 *
 * DESCRIPTION:	Very simple serial line unit interface functions for the 
 *		LSI11 microkernel.
 * 
 * AUTHOR/DATE:	John Lloyd, Spring 1988
 *
 *~=~=~=~=~=~= McGill-Computer-Vision-and-Robotics-Laboratory ~=~=~=~=~=~=*/

#include "sys.h"

#define S_READY		0200
#define S_IEA		0100

static SLU **slubufs = 0;

static sluFlush (slu)
SLU *slu;
{
	/* flushes the serial line unit, clearing all read and 
	   write buffers */

	slu->rqueue = 0;
	slu->rtail = 0;
	slu->wqueue = 0;
	slu->whead = 0;
}

int sluRintr();
int sluWintr();

sluCreate(slu, csr, vect)
SLU *slu;
unsigned short *csr;
unsigned vect;
{
	/* Called once per program. Takes a serial line unit descriptor
	   (type SLU) and instantiates it with the unit defined by 
	   the bus address 'csr' and the vector number 'vect'. The
	   generic serial line read and write interrupt handlers are
	   attached to the vector, and the buffer information in the
	   SLU struct is cleared */
	   
	VECT_DESC vd;
	VECTOR *rv;
	VECTOR *wv;
	int c;
	
	vd.saveregs = 1;
	vd.arg = (unsigned)slu;
	vd.fxn = sluRintr;
	rv = (VECTOR*)vectorAlloc (vect, &vd);
	vd.fxn = sluWintr;
	wv = (VECTOR*)vectorAlloc (vect+4, &vd);

	slu->rcsr = csr;
	slu->rbuf = csr + 1;
	slu->wcsr = csr + 2;
	slu->wbuf = csr + 3;
	slu->ipending = 0;
	slu->rqueue = 0;
	slu->rsize = 0;
	slu->rhead = 0;
	slu->rtail = 0;
	slu->wqueue = 0;
	slu->wsize = 0;
	slu->whead = 0;
	slu->wtail = 0;

	c = *slu->rbuf;		/* flush the read register */
}

static int numSDevs = 0;

sluInit (num, unitDesc)
int num;
DEV_DESC *unitDesc;
{
	/* Called once at program start up time.

	   Goes through a table describing each serial line unit to be
	   configured into the system and allocates a serial line unit
	   descriptor for each one. */
	  
	int k;

	if (num)
	 { slubufs = (SLU**)getmem(num*sizeof(SLU**));

	   for (k=0; k<num; k++)
	    { slubufs[k] = (SLU*)getmem(sizeof(SLU));
	      sluCreate (slubufs[k], unitDesc[k].addr, unitDesc[k].vect);
	    }
	 }
	numSDevs = num;
}

SLU *sluGet (num)
int num;
{
	/* Gets the serial line unit descriptor corresponding to the
	   indicated number. This number is the same as the index into
	   the table 'serialDevs' describing all the serial line units. */

	if (0 <= num && num < numSDevs)
	 { return (slubufs[num]);
	 }
	else
	 { return ((SLU*)-1);
	 }
}

sluOn (slu)
SLU *slu;
{
	/* Turns 'on' a serial line unit by flushing it and enabling 
	   interrupts */

	sluFlush (slu);

	*(slu->rcsr) |= S_IEA;
	*(slu->wcsr) |= S_IEA;
}

sluOff (slu)
SLU *slu;
{
	/* Turns a serial line unit off by disabling interrupts */

	*(slu->rcsr) &= ~S_IEA;
	*(slu->wcsr) &= ~S_IEA;
	sluFlush (slu);
}

sluRintr (slu)
register SLU *slu;
{
	/* Interrupt service routine for receiving characters. If a read
	   buffer has been set up (with the sluRead() call) the character
	   is put into the buffer. If the buffer is full, then the
	   reference to the read buffer is deleted and other characters
	   are ignored */

	int c = *(slu->rbuf);

	if (slu->rqueue)		/* read buffer is there */
	 { register int j;

	   if ((j = slu->rtail) < slu->rsize)
	    { slu->rqueue[j] = c;
	      slu->rtail = j+1;
	    }
	   else
	    { slu->rqueue = 0;		/* buffer is full */
	    }
	 }
}

sluRead (slu, buf, size)
register SLU *slu;
char *buf;
int size;
{	
	/* tells the serial line unit to put all characters it receives into
	   the buffer 'buf' which is 'size' bytes long. */
	 
	int s;
	s = spl7();
	
	slu->rsize = size;
	slu->rtail = 0;
	slu->rqueue = buf;

	splx(s);
}

sluRcount (slu)
SLU *slu;
{	
	/* Returns the number of characters that have been read by the serial
	   line unti since the last call to sluRead() */

	return (slu->rtail);
}

sluWintr (slu)
register SLU *slu;
{
	/* interrupt service routine for sending characters. If there is
	   a buffer of characters to send, the routine output the next
	   character in the buffer. When all characters have been sent,
	   the reference to the buffer is deleted. */

	if (slu->wqueue)
	 { register int j;
	   if ((j = slu->whead) == slu->wsize)
	    { slu->wqueue = 0;
	    }
	   else
	    { *(slu->wbuf) = slu->wqueue[j++];
	      slu->whead = j;
	    }
	 }
}

sluWrite (slu, buf, size)
register SLU *slu;
char *buf;
int size;
{
	/* Tells the serial line unit to send out a buffer of characters
	   'buf' which is 'size' bytes long. */

	int s, expecting;
	s = spl7();

	slu->wqueue = buf;
	slu->wsize = size;
	slu->whead = 0;
	if ((*slu->wcsr) & S_READY)
	 { sluWintr (slu);
	 }
	splx(s);
}

sluWcount(slu)
SLU *slu;
{
	/* Returns the number of characters that have been written since
	   sluWrite() was last called */
	
	return (slu->whead);
}
