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

#include "../h/cdefs.h"
#include "../h/IOdevice.h"
#include "../h/priDevice.h"
#include "../h/errorCodes.h"

#include "../drivers/driErrorMsgs.h"

#define CYCLES_PER_MS	500
#define DEFAULT_TIMEOUT	2*CYCLES_PER_MS

extern unsigned char *rciIOpage;

#define SEND_SIGNAL()	  *priCsr ^= PRI_SIGOUT; bitTest ^= PRI_SIGIN;

static char *format = "csr=0x%hx obuf=0x%hx ibuf=0x%hx test=0x%hx\n";
#define WAIT_FOR_SIGNAL(x) \
	while ((PRI_SIGIN & *priCsr) == bitTest && --k) \
	   ; \
	if (k == 0) \
	 { errorMsg (0, format, priCsr[0], priCsr[1], priCsr[2], bitTest); \
	   errorMsg (ETimeout, driTimeoutMsg, fxnName, (x)); \
	   return (-1); \
	 }

priRead (dev, buf, size)
IO_DEVICE *dev;
void *buf;
int size;
{
	char *fxnName = "priRead()";
	struct prireg *pridev = (struct prireg *)dev->location;

	register unsigned short *priCsr;    /* parallel port csr	*/
	register unsigned short *priBuf;    /* parallel port input buf	*/
	register unsigned short *dataPtr;   /* pointer to data buffer	*/
	register unsigned short bitTest;    /* used to test for 'ready'	*/
	register int count;		    /* word count register	*/

	short myChecksum;		/* computes checksum		*/
	short theirChecksum;
	unsigned long k;		/* timeout counter		*/
	int byteCount;			/* number of bytes read in  	*/

	priBuf = &(pridev->ibuf);
	priCsr = &(pridev->csr);
	bitTest = (*priCsr & PRI_SIGIN);	/* get initial csr value 	*/

	pridev->obuf = PRI_READ_READY;

	k = dev->blocktime * CYCLES_PER_MS;
	WAIT_FOR_SIGNAL(1);

	pridev->obuf = 0;
	count = (int)*priBuf;		/* get word/bytes counts	*/
	dev->byteCount = count*sizeof(short);

	/* ----- check for bogus count: negative, or too big ----- */

	if (count < 0 || size < dev->byteCount)
	 { errorMsg (ECount, driCountMsg, fxnName, count);
	   *priCsr &= ~PRI_SIGOUT;
	   return (-1);
	 }
	
	/* ----- now read in all the words ----- */

	k = (count+2)*dev->timeout;			

	dataPtr = (unsigned short *)buf;
	myChecksum = 0;
	while (count--)
         { SEND_SIGNAL();
	   WAIT_FOR_SIGNAL(2);
	  	
	   *dataPtr = *priBuf;
	   myChecksum += *dataPtr++;
	 }

	/* ----- now get their checksum ----- */

	SEND_SIGNAL();
	WAIT_FOR_SIGNAL(3);

	theirChecksum = *priBuf;
	
	SEND_SIGNAL();
	WAIT_FOR_SIGNAL(4);

	/* input buffer is now clear */

	if (myChecksum != theirChecksum)
	 { errorMsg (ETransmit, driChecksumMsg, 
				fxnName, theirChecksum, myChecksum);
	   return (-1);
	 }
	return (dev->byteCount);
}

priWrite (dev, buf, size)
IO_DEVICE *dev;
void *buf;
int size;
{
	char *fxnName = "priWrite()";
	struct prireg *pridev = (struct prireg *)dev->location;

	register unsigned short *priCsr;    /* parallel port csr	*/
	register unsigned short *priBuf;    /* parallel port output buf	*/
	register unsigned short *dataPtr;   /* pointer to data buffer	*/
	register unsigned short bitTest;    /* used to test for 'ready'	*/
	register int count;		    /* word count register	*/

	short checksum;			    /* computes checksum	*/
	unsigned long k;		    /* timeout counter		*/
	int byteCount;			    /* number of bytes read in  */

	priBuf = &(pridev->obuf);
	priCsr = &(pridev->csr);

        count = size/sizeof(short);
	if (size % sizeof(short))
	 { count++;
	 }
        if (count < 0)
         { errorMsg (ECount, driCountMsg, fxnName, count);
	   *priBuf = 0;
	   *priCsr &= ~PRI_SIGOUT;
	   return (-1);
	 }

	pridev->obuf = PRI_WRITE_READY;
	
	k = dev->blocktime * CYCLES_PER_MS;
	while (pridev->ibuf != PRI_READ_READY && --k)
	   ;
	if (k == 0)
	 { errorMsg (ETimeout, driTimeoutMsg, fxnName, 0);
	   *priBuf = 0;
	   *priCsr &= ~PRI_SIGOUT;
	   return (-1);
	 }
        *priBuf = count;
	bitTest = (*priCsr & PRI_SIGIN) ^ PRI_SIGIN;	/* get signal bit */

	k = (count+2)*dev->timeout;
	SEND_SIGNAL();
	WAIT_FOR_SIGNAL(1);

	dataPtr = (unsigned short *)buf;
	
	/* ----- send words down the chute ----- */

	checksum = 0;
	while (count--)
	 { checksum += *dataPtr;
	   *priBuf = *dataPtr++;		/* load the pri */

	   SEND_SIGNAL();
	   WAIT_FOR_SIGNAL(2);
         }

	*priBuf = checksum;
	SEND_SIGNAL();
	WAIT_FOR_SIGNAL(3);

	*priBuf = 0;
	SEND_SIGNAL();

	/* make sure signal bit is clear before we leave routine */

#if 1
	if (*priCsr & PRI_SIGOUT)
	 { WAIT_FOR_SIGNAL(4);
	   SEND_SIGNAL();
	 }
#else
	k = 100;
	while (--k)
	   ;
	*priCsr &= ~PRI_SIGOUT;
#endif

	return (size);
}

priProbe (dev)
IO_DEVICE *dev;
{
	int addr = (0x1fff & dev->address);

	if (rciBadAddr (rciIOpage + addr,     sizeof(short)) ||
	    rciBadAddr (rciIOpage + addr + 2, sizeof(short)) ||
	    rciBadAddr (rciIOpage + addr + 4, sizeof(short)))
 	 { return (-1);
	 }
	else
	 { dev->location = (unsigned char*)(rciIOpage + addr);
	   dev->timeout = DEFAULT_TIMEOUT;
	   dev->blocktime = PRI_DEFAULT_BLOCKTIME;
	   dev->size = sizeof (struct prireg);
	   return (sizeof (struct prireg));
	 }
}

priAttach (dev)
IO_DEVICE *dev;
{
	char *fxnName = "priAttach()";
	struct prireg *pridev = (struct prireg *)dev->location;
	unsigned long k;

	if (pridev->ibuf != PRI_LISTEN)
	 { errorMsg (EConnect, driListeningMsg, fxnName);
	   return (-1);
	 }
	pridev->obuf = PRI_LISTEN;

	pridev->csr &= ~PRI_CSR1;
	pridev->csr |= PRI_CSR1;

	k = dev->timeout*100;
	while (pridev->ibuf && --k)
	   ;
	if (k == 0)
	 { errorMsg (ETimeout, driTimeoutMsg, fxnName, 1);
	   return (-1);
	 }

	pridev->csr &= ~PRI_CSR1;
	pridev->obuf = 0;

#if 1
	k = dev->timeout*100;
	while ((pridev->csr & PRI_SIGIN) && --k)
	   ;
	if (k == 0)
	 { errorMsg (ETimeout, driTimeoutMsg, fxnName, 1);
	   return (-1);
	 }
#else
	for (k=1000; k>0; k--)	/* XXX Give other device 1.5 msec. to notice */
	   ;
#endif

	return (1);	
}

priDetach (dev)
IO_DEVICE *dev;
{
	struct prireg *pridev;

	if (dev->location)
	 { pridev = (struct prireg *)dev->location;
	   pridev->csr = 0;
	   pridev->obuf = 0;
	   return (1);
	 }
	else
	 { return (0);
	 }
}

priSignal (dev, n)
IO_DEVICE *dev;
int n;
{
	char *fxnName = "priSignal()";
	struct prireg *pridev = (struct prireg *)(rciIOpage +
						  (0x1fff & dev->address));
       	int k;
	int retval = 1;

	if (n < 1 || n > 2)
	 { errorMsg (ECount, driCountMsg, fxnName, n);
	   return (-1);
	 }
	if (pridev->csr & PRI_CSR0 || pridev->ibuf == PRI_SIGNAL_ACK)
	 { errorMsg (ESync, driSyncMsg, fxnName);
	   return (-1);
	 }
	pridev->obuf = n;
	pridev->csr |= PRI_CSR0;
	k = dev->timeout * 100;
	while (pridev->ibuf != PRI_SIGNAL_ACK && --k)
	   ;
	if (k == 0)
	 { errorMsg (ETimeout, driTimeoutMsg, fxnName, 1);
	   retval = -1;
	 }
	pridev->csr &= ~PRI_CSR0;	/* clear interrupt */
	pridev->obuf = 0;

	k = dev->timeout * 100;
	while (pridev->ibuf == PRI_SIGNAL_ACK && --k)
	   ;
	if (k == 0)
	 { errorMsg (ETimeout, driTimeoutMsg, fxnName, 2);
	   return (-1);
	 }

	return (retval);
}

priIoctl (dev, com, data)
IO_DEVICE *dev;
int com;
void *data;
{
	char *fxnName = "priIoctl()";
	struct prireg *pridev = (struct prireg *)dev->location;

	switch (com)
	 { case IOC_SIGINTER_ENABLE:
	    { pridev->csr |= (PRI_IEA);
	      break;
	    }
	   case IOC_SIGINTER_DISABLE:
	    { pridev->csr &= ~(PRI_IEA);
	      break;
	    }
	   case IOC_TIMEOUT:
	    { dev->timeout = *(int *)data;
	      break;
	    }
	   case IOC_GET_BLOCKTIME:
	    { *(int *)data = dev->blocktime;
	      break;
	    }
	   case IOC_SET_BLOCKTIME:
	    { dev->blocktime = *(int *)data;
	      break;
	    }
	   default:
	    { errorMsg (EBadIoctl, driIoctlMsg, fxnName, com);
	      return (-1);
	    }
	 }
	return (1);
}

priTestRead (dev)
IO_DEVICE *dev;
{
	if (((struct prireg *)(dev->location))->ibuf == PRI_WRITE_READY)
	 { return (1);
	 }
 	else
	 { return (0);
	 }
}

priTestWrite (dev)
IO_DEVICE *dev;
{
	if (((struct prireg *)(dev->location))->ibuf == PRI_READ_READY)
	 { return (1);
	 }
 	else
	 { return (0);
	 }
}
