/*-=-=-=-=-=-=-=-=-=-=-=- 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/drjDevice.h"
#include "../h/errorCodes.h"

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

#define DEFAULT_TIMEOUT	200
#define CYCLES_PER_MS	500

extern unsigned char *rciIOpage;

#define SEND_SIGNAL()	  *drjOsig ^= DRJ_SIGOUT; bitTest ^= DRJ_SIGIN;

#define WAIT_FOR_SIGNAL(loc) \
	while ((DRJ_SIGIN & *drjIsig) == bitTest && --k) \
	   ; \
	if (k == 0) \
	 { errorMsg (ETimeout, driTimeoutMsg, fxnName, (loc)); \
	   return (-1); \
	 }

/* The DRV11-J registers are used as follows:

	bufa	is the input signal buffer
	bufb	is the output signal buffer
	bufc	is the input data buffer
	bufd	is the output data buffer
*/

drjRead (dev, buf, size)
IO_DEVICE *dev;
void *buf;
int size;
{
	char *fxnName = "drjRead()";
	struct drjreg *drjdev = (struct drjreg *)dev->location;

	register unsigned short *drjOsig;   /* output signal register	*/
	register unsigned short *drjIsig;   /* input signal register	*/
	register unsigned short *drjBuf;    /* 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  	*/

	drjBuf = &(drjdev->bufc);
	drjOsig = &(drjdev->bufb);
	drjIsig = &(drjdev->bufa);

	bitTest = (*drjIsig & DRJ_SIGIN);	/* get initial handshake bit value */

	*drjOsig |= DRJ_READ_READY;

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

	*drjOsig &= ~(DRJ_READ_READY);
	count = (int)*drjBuf;		/* 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, size);
	   *drjOsig &= ~DRJ_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 = *drjBuf;
	   myChecksum += *dataPtr++;
	 }

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

	SEND_SIGNAL();
	WAIT_FOR_SIGNAL(3);

	theirChecksum = *drjBuf;
	
	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);
}

drjWrite (dev, buf, size)
IO_DEVICE *dev;
void *buf;
int size;
{
	char *fxnName = "drjWrite()";
	struct drjreg *drjdev = (struct drjreg *)dev->location;

	register unsigned short *drjOsig;   /* output signal register	*/
	register unsigned short *drjIsig;   /* input signal register	*/
	register unsigned short *drjBuf;    /* 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  */

	drjBuf = &(drjdev->bufd);
	drjIsig = &(drjdev->bufa);
	drjOsig = &(drjdev->bufb);

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

	*drjOsig |= DRJ_WRITE_READY;
	
	k = dev->blocktime * CYCLES_PER_MS;
	while (!(*drjIsig & DRJ_READ_READY) && --k)
	   ;
	if (k == 0)
	 { errorMsg (ETimeout, driTimeoutMsg, fxnName, 0);
	   *drjOsig = 0;
	   return (-1);
	 }
        *drjBuf = count;
	bitTest = (*drjIsig & DRJ_SIGIN) ^ DRJ_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;
	   *drjBuf = *dataPtr++;		/* load the drj */

	   SEND_SIGNAL();
	   WAIT_FOR_SIGNAL(2);
         }

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

	*drjBuf = 0;
	SEND_SIGNAL();

	/* hack for ocsilliscope: we want to make sure that the we leave
	   the routine with the signal bit clear */

	k = 100;
	while (--k)
	   ;
	*drjOsig &= ~DRJ_SIGOUT;

	return (size);
}

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

	for (i=0; i < sizeof(struct drjreg); i += sizeof(short))
	 { if (rciBadAddr (rciIOpage + addr + i, sizeof(short)))
	    { return (-1);
	    }
	 }
	dev->location = (unsigned char*)(rciIOpage + addr);
	dev->timeout = DEFAULT_TIMEOUT;
	dev->blocktime = DRJ_DEFAULT_BLOCKTIME;
	dev->size = sizeof (struct drjreg);
	return (sizeof (struct drjreg));
}

drjAttach (dev)
IO_DEVICE *dev;
{
	char *fxnName = "drjAttach()";
	struct drjreg *drjdev = (struct drjreg *)dev->location;
	unsigned long k;

	if (!(drjdev->bufa & DRJ_LISTEN))
	 { errorMsg (EConnect, driListeningMsg, fxnName);
	   return (-1);
	 }

	/* configure the port so that registers bufa and bufc are
	   input registers and bufb and bufd are output registers */
	drjdev->csra &= ~DRJCSR_DIR;
	drjdev->csrb |= DRJCSR_DIR;
	drjdev->csrc &= ~DRJCSR_DIR;
	drjdev->csrd |= DRJCSR_DIR;

	drjdev->bufb |= DRJ_LISTEN;

	drjdev->bufb &= ~DRJ_INTERRUPT1;
	drjdev->bufb |= DRJ_INTERRUPT1;

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

	drjdev->bufb = 0;

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

	return (1);	
}

drjDetach (dev)
IO_DEVICE *dev;
{
	struct drjreg *drjdev;

	if (dev->location)
	 { drjdev = (struct drjreg *)dev->location;
	   drjdev->csra = 0;	/* disable interrupts */
	   drjdev->bufb = 0;
	   drjdev->bufd = 0;
	   return (1);
	 }
	else
	 { return (0);
	 }
}

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

	if (n < 1 || n > 2)
	 { errorMsg (ECount, driCountMsg, fxnName, 1);
	   return (-1);
	 }
	if (drjdev->bufb & DRJ_INTERRUPT0 || (drjdev->bufa & DRJ_SIGNAL_ACK))
	 { errorMsg (ESync, driSyncMsg, fxnName);
	   return (-1);
	 }
	drjdev->bufd = n;
	drjdev->bufb |= DRJ_INTERRUPT0;
	k = dev->timeout * 100;
	while (!(drjdev->bufa & DRJ_SIGNAL_ACK) && --k)
	   ;
	if (k == 0)
	 { errorMsg (ETimeout, driTimeoutMsg, fxnName, 2);
	   retval = -1;
	 }
	drjdev->bufb = 0;	/* clear interrupt */

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

	return (retval);
}

drjIoctl (dev, com, data)
IO_DEVICE *dev;
int com;
void *data;
{
	char *fxnName = "drjIoctl()";
	struct drjreg *drjdev = (struct drjreg *)dev->location;

	switch (com)
	 { case IOC_SIGINTER_ENABLE:
	    { drjdev->csra |= (DRJCSR_IE);
	      break;
	    }
	   case IOC_SIGINTER_DISABLE:
	    { drjdev->csra &= ~(DRJCSR_IE);
	      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);
}

drjTestRead (dev)
IO_DEVICE *dev;
{
	if (((struct drjreg *)(dev->location))->bufa & DRJ_WRITE_READY)
	 { return (1);
	 }
 	else
	 { return (0);
	 }
}

drjTestWrite (dev)
IO_DEVICE *dev;
{
	if (((struct drjreg *)(dev->location))->bufa & DRJ_READ_READY)
	 { return (1);
	 }
 	else
	 { return (0);
	 }
}
