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

/* Driver for the VME parallel port device built by NRC for McCRCIM */

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

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

#define CYCLES_PER_MS	300		/* rough, for a 68030 */
#define DEFAULT_TIMEOUT	10*CYCLES_PER_MS

extern unsigned char *rciIOpage;

#define SEND_SIGNAL()	  *pioPbuf ^= PIO_SIGOUT; bitTest ^= PIO_SIGIN;

static char *format = "pbuf=0x%x obuf=0x%x ibuf=0x%x test=0x%x\n";
#define WAIT_FOR_SIGNAL(x) \
	while ((PIO_SIGIN & *pioPbuf) == bitTest && --k) \
	   ; \
	if (k == 0) \
	 { errorMsg(0,format,piodev->pbuf,piodev->obuf,piodev->ibuf,bitTest);\
	   errorMsg(ETimeout,driTimeoutMsg,fxnName,(x)); \
	   return (-1); \
	 }

static union { unsigned short s;
	unsigned char c[2];
  } swap;

/* this port uses negated logic on the input and output ports, so we invert
   the data read from and written to swap.s */
#define	READ_BUF(p) 	(~*(p) & 0xffff)
#define WRITE_BUF(p,v)	(*(p) = ~(v))

static int probeTest();

pioRead (dev, buf, size)
IO_DEVICE *dev;
void *buf;
int size;
{
	char *fxnName = "pioRead()";
	struct pioreg *piodev = (struct pioreg *)dev->location;

	register unsigned char *pioPbuf;   /* parallel port protocal bits */
	register unsigned short *pioBuf;    /* 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  	*/

	pioBuf = &(piodev->ibuf);
	pioPbuf = &(piodev->pbuf);
	bitTest = (*pioPbuf & PIO_SIGIN);	/* get initial csr value */

	WRITE_BUF(&piodev->obuf, PIO_READ_READY);

	k = dev->blocktime * CYCLES_PER_MS;
#if 0
	while (READ_BUF(&piodev->ibuf) != PIO_WRITE_READY && --k)
	   ;
	if (k == 0)
	 { errorMsg (ETimeout, driTimeoutMsg, fxnName, 0);
	   *pioPbuf &= ~PIO_SIGOUT;
	   return (-1);
	 }
#endif
	WAIT_FOR_SIGNAL(1);

	WRITE_BUF(&piodev->obuf, 0);
	count = (int)READ_BUF(pioBuf);
	dev->byteCount = count*sizeof(short);

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

	if (count < 0 || size < dev->byteCount)
	 { errorMsg (ECount, driCountMsg, fxnName, count);
	   *pioPbuf &= ~PIO_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 = READ_BUF(pioBuf);
	   myChecksum += *dataPtr++;
	 }

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

	SEND_SIGNAL();
	WAIT_FOR_SIGNAL(3);

	theirChecksum = READ_BUF(pioBuf);
	
	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);
}

pioWrite (dev, buf, size)
IO_DEVICE *dev;
void *buf;
int size;
{
	char *fxnName = "pioWrite()";
	struct pioreg *piodev = (struct pioreg *)dev->location;

	register unsigned char *pioPbuf;   /* parallel port protocal bits */
	register unsigned short *pioBuf;    /* 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  */

	pioBuf = &(piodev->obuf);
	pioPbuf = &(piodev->pbuf);

        count = size/sizeof(short);
	if (size % sizeof(short))
	 { count++;
	 }
        if (count < 0)
         { errorMsg (ECount, driCountMsg, fxnName, count);
	   WRITE_BUF(pioBuf,0);
	   *pioPbuf &= ~PIO_SIGOUT;
	   return (-1);
	 }

	WRITE_BUF(pioBuf, PIO_WRITE_READY);
	
	k = dev->blocktime * CYCLES_PER_MS;
	while (READ_BUF(&piodev->ibuf) != PIO_READ_READY && --k)
	   ;
	if (k == 0)
	 { errorMsg (ETimeout, driTimeoutMsg, fxnName, 0);
	   WRITE_BUF(pioBuf,0);
	   *pioPbuf &= ~PIO_SIGOUT;
	   return (-1);
	 }
        WRITE_BUF(pioBuf,count);
	bitTest = (*pioPbuf & PIO_SIGIN) ^ PIO_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;
	   WRITE_BUF (pioBuf, *dataPtr++);		/* load the pio */

	   SEND_SIGNAL();
	   WAIT_FOR_SIGNAL(2);
         }

	WRITE_BUF (pioBuf, checksum);
	SEND_SIGNAL();
	WAIT_FOR_SIGNAL(3);

	WRITE_BUF (pioBuf, 0);
	SEND_SIGNAL();

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

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

	return (size);
}

pioProbe (dev)
IO_DEVICE *dev;
{
	unsigned char *location;
	struct pioreg *pio;
	char *cp;
	int i;

	location = rciIOpage + dev->address;
	if (!probeTest(location))
	 { return (-1);
	 }
	pio = (struct pioreg *)(location);
	dev->location = location;
	dev->timeout = DEFAULT_TIMEOUT;
	dev->blocktime = PIO_DEFAULT_BLOCKTIME;
	dev->size = sizeof (struct pioreg);

	/* setup the hardware properly */
	pioConfig (pio);

	return (sizeof (struct pioreg));
}

pioAttach (dev)
IO_DEVICE *dev;
{
	char *fxnName = "pioAttach()";
	struct pioreg *piodev = (struct pioreg *)dev->location;
	unsigned long k;
	unsigned short *obuf;

	if (READ_BUF(&piodev->ibuf) != PIO_LISTEN)
	 { errorMsg (EConnect, driListeningMsg, fxnName);
	   rciPrintf ("ibuf = 0x%x\n", READ_BUF(&piodev->ibuf));
	   return (-1);
	 }
	obuf = &piodev->obuf;
	WRITE_BUF(obuf, PIO_LISTEN);

	piodev->pbuf &= ~PIO_CSR1;
	piodev->pbuf |= PIO_CSR1;

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

	piodev->pbuf &= ~PIO_CSR1;
	WRITE_BUF(obuf, 0);

#if 1
	k = dev->timeout*100;
	while ((piodev->pbuf & PIO_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);	
}

pioDetach (dev)
IO_DEVICE *dev;
{
	struct pioreg *piodev;

	if (dev->location)
	 { piodev = (struct pioreg *)dev->location;

	   piodev->pbuf = 0;
	   pioSetIntr(piodev, 0 /*=portA*/, 0 /*=disable*/);
	   WRITE_BUF(&piodev->obuf, 0);
	   return (1);
	 }
	else
	 { return (0);
	 }
}

pioSignal (dev, n)
IO_DEVICE *dev;
int n;
{
	char *fxnName = "pioSignal()";
	struct pioreg *piodev = (struct pioreg *)(rciIOpage +
						  (dev->address));
       	int k;
	int retval = 1;

	unsigned short *ibuf;
	unsigned short *obuf;

	ibuf = &piodev->ibuf;
	obuf = &piodev->obuf;

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

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

pioIoctl (dev, com, data)
IO_DEVICE *dev;
int com;
void *data;
{
	char *fxnName = "pioIoctl()";
	struct pioreg *piodev = (struct pioreg *)dev->location;

	switch (com)
	 { case IOC_SIGINTER_ENABLE:
	    { pioSetIntr(piodev, 0 /*=portA*/, 1 /*=enable*/);
	      break;
	    }
	   case IOC_SIGINTER_DISABLE:
	    { pioSetIntr(piodev, 0 /*=portA*/, 0 /*=disable*/);
	      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);
}

pioTestRead (dev)
IO_DEVICE *dev;
{
	unsigned short *ibuf;

	ibuf = &((struct pioreg *)(dev->location))->ibuf;

	if (READ_BUF(ibuf) == PIO_WRITE_READY)
	 { return (1);
	 }
 	else
	 { return (0);
	 }
}

pioTestWrite (dev)
IO_DEVICE *dev;
{
	unsigned short *ibuf;

	ibuf = &((struct pioreg *)(dev->location))->ibuf;
	if (READ_BUF(ibuf) == PIO_READ_READY)
	 { return (1);
	 }
 	else
	 { return (0);
	 }
}

pioIntrAttach (intr, enable)
IO_INTERRUPT *intr;
int enable;
{
	/* This function is presumed to be called at high ipl. The
	   'probe' routine may not have been called yet. */

	unsigned char *location;
	struct pioreg *pio;

	location = (rciIOpage + intr->address);
	if (!probeTest(location))
	 { return (-1);
	 }

	pio = (struct pioreg *)(location);
	intr->location = location;

	if (enable)
	 { pioConfig (pio);
	   *((unsigned char*)pio+VECT_REG_A_OFFSET) = intr->vector;
	   *((unsigned char*)pio+VECT_REG_B_OFFSET) = intr->vector;
	   pioSetIntr(pio, 0 /*=portA*/, 1 /*=enable*/);
	 }
	else
	 { pioSetIntr(pio, 0 /*=portA*/, 0 /*=disable*/);
	 }
	return (1);
}

pioIntrHandler(intr)
IO_INTERRUPT *intr;
{
	/* nothin to do here ... */
}

pioSetIntr(pio, port, enable)
struct pioreg *pio;
int port, enable;
{
	unsigned char enableCode;

	enableCode = enable ? INTR_ENABLE : INTR_DISABLE;
	if (port == 0)	/* Port A, output port */
	 { pio->crA = DUPLICATE_BYTES(CR_SUBMODE_OUTPUT|enableCode);
	 }
	else
	 { pio->crB |= DUPLICATE_BYTES(CR_SUBMODE_INPUT|enableCode);
	 }
}

#define INTR_VECT	24

pioConfig (pio)
struct pioreg *pio;
{
	pio->csr = DUPLICATE_BYTES (GCR_MODE_UNI_8_BIT|GCR_H34_ENA);
	pio->srr = 0;

	pio->ddrA = 0xffff;	/* set all bits of port A to outputs */
	pio->ddrB = 0;		/* set all bits of port B to inputs */
	pio->ddrC = DUPLICATE_BYTES (PIO_CSR0|PIO_CSR1); 
				/* csr0,1 are output bits */
				
	pio->crA = DUPLICATE_BYTES (CR_SUBMODE_OUTPUT|INTR_DISABLE);
	pio->crB = DUPLICATE_BYTES (CR_SUBMODE_INPUT|INTR_DISABLE);
	pio->tcr = 0;

	pio->pbuf = 0;
	WRITE_BUF (&pio->obuf, 0);
}

static probeTest(loc)
unsigned char* loc;
{
	char *cp;
	int i;

	for (cp=(char*)loc, i=0; i<sizeof(struct pioreg); i++)
	 { if (rciBadAddr (cp++, sizeof(char)))
	    { return (0);
	    }
	 }
	return (1);
}

#if 0

#include <vxWorks.h>
#include <iv68k.h>


handler()
{
	
	logMsg ("got it\n");
}

doConnect()
{
	
	intConnect (INUM_TO_IVEC(24), handler, 0);
}

doSet()
{
	pioSetIntr(0xfffffd80,0,1);
}

doConf()
{
	pioConfig(0xfffffd80);
}

doFoo()
{
	printf ("0x%x\n", *(unsigned short*)0xfffffd92);
}

pioPrint()
{
	struct pioreg *pio = (struct pioreg*)0xfffffd80;

	printf ("csr    = 0x%x\n", pio->csr);
	printf ("srr    = 0x%x\n", pio->srr);
	printf ("ddrA   = 0x%x\n", pio->ddrA);
	printf ("ddrB   = 0x%x\n", pio->ddrB);
	printf ("ddrC   = 0x%x\n", pio->ddrC);
	printf ("ivr    = 0x%x\n", pio->ivr);
	printf ("crA    = 0x%x\n", pio->crA);
	printf ("crB    = 0x%x\n", pio->crB);
	printf ("obuf   = 0x%x\n", pio->obuf);
	printf ("ibuf   = 0x%x\n", pio->ibuf);
	printf ("pbuf   = 0x%x\n", pio->pbuf);
	printf ("status = 0x%x\n", pio->status);
}

#endif
