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

/* RCI device driver to connect a BiT3 shared memory card on a SUN4/VME
   host to a BiT3 card on a target VME system. This driver is pretty
    much identical to the 'mem' driver in memDriver.c */

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

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

#define DEFAULT_TIMEOUT	200
#define CYCLES_PER_MS	3000	/* on a microVAX ! */

#define WAIT_FOR(x,loc)	\
	while(!(x) && --k) ; \
	if (k == 0) \
	 { errorMsg (ETimeout, driTimeoutMsg, fxnName, (loc)); \
	   return (-1); \
	 }

extern unsigned char *rciBus;
extern unsigned char *rciIOpage;

#define MAXMEMUNITS	10	/* max. no. of units allowed */

struct vemregs *vemregs;	/* main csr for all the memory devs */

vemRead (dev, buf, size)
IO_DEVICE *dev;
void *buf;
int size;
{
	/* Reads words from the target device into buf, up to a maximum
	   of 'size' words. Blocks until the target device is ready to write.
	 */

	char *fxnName = "vemRead()";
	struct vemdev *vemdev = (struct vemdev *)dev->location;

	register unsigned short *memBuf;    /* input buffer area	*/
	register unsigned short *dataPtr;   /* pointer to data buffer	*/
	register int count;		    /* word count register	*/

	unsigned short myChecksum;	    /* computes checksum	*/
	unsigned long k;		    /* timeout counter		*/
	unsigned short int theirChecksum;

	memBuf = vemdev->targetBuf;

	/* Announce we are ready to read and wait until the target	*/
	/* is ready to write.						*/

	vemdev->hostStatus = VEM_READ_READY;

	k = dev->blocktime * CYCLES_PER_MS;
	WAIT_FOR (vemdev->targetStatus == VEM_WRITE_READY, 1);

	k = dev->timeout * CYCLES_PER_MS;
	WAIT_FOR (vemdev->targetReady, 2);

	/* Get the word count and read in all the data. Convert the	*/
	/* word count from words to bytes. 				*/

	count = (int)vemdev->targetCount;
	dev->byteCount = count*sizeof(short);

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

	if (count < 0 || size < dev->byteCount)
	 { errorMsg (ECount, driCountMsg, fxnName, count);
	   return (-1);
	 }
	dataPtr = (unsigned short *)buf;
	myChecksum = 0;
	while (count--)
         { *dataPtr = *memBuf++;
	   myChecksum += *dataPtr++;
	 }
	theirChecksum = vemdev->targetChecksum;

	/* BEGIN fix added by Mike Parker to re-read data if the
	   checksum reports error. This is because the BIT3 is
	   flakey and will give bad data sometimes because of
	   timing trouble */

	if (myChecksum != theirChecksum)
	 { unsigned short int cksum2;
	   int count2;
	   int i;
	   unsigned short int v;
	   

	   dataPtr = (unsigned short *)buf;
	   rciPrintf ("BIT3 checksum error\n");

	   count2 = (int)vemdev->targetCount;
	   cksum2 = 0;
	   for (i=0;i<count2;i++)
	    { v = vemdev->targetBuf[i];
#if 0
	      if (v != ((unsigned short int *)buf)[i])
	       { rciPrintf(
		"data word %d reread mismatch: first 0x%x second 0x%x\n",
		i,(int)(((unsigned short int *)buf)[i]),(int)v);
	       }
#endif
	      *dataPtr++ = v;
	      cksum2 += v;
	    }
	   v = vemdev->targetChecksum;
#if 0
	   if (v != theirChecksum)
	    { rciPrintf(
		"checksum reread mismatch: first 0x%x second 0x%x\n",
		(int)theirChecksum,(int)v);
	    }
#endif
	   if (v != cksum2)
	    { errorMsg (ETransmit, driChecksumMsg, 
				   fxnName, theirChecksum, myChecksum);
	      return (-1);
	    }
	 }

	/* END fix by Mike Parker */

	/* synchronize with other device that the read has finished */

	vemdev->hostStatus = 0;
	k = dev->timeout * CYCLES_PER_MS;
	WAIT_FOR (vemdev->targetReady == 0, 3);
	return (dev->byteCount);
}

vemWrite (dev, buf, size)
IO_DEVICE *dev;
void *buf;
int size;
{
	/* send a buffer of 'size' bytes to the target device. Block
	   until the target device is ready to read (or we timeout). */
	   
	char *fxnName = "vemWrite()";
	struct vemdev *vemdev = (struct vemdev *)dev->location;

	register unsigned short *memBuf;    /* output buffer area	*/
	register unsigned short *dataPtr;   /* pointer to data buffer	*/
	register int count;		    /* word count register	*/

	short checksum;			    /* computes checksum	*/
	unsigned long k;		    /* timeout counter		*/

	memBuf = vemdev->hostBuf;

	/* first, write all the data into the output buffer and compute 
	   the checksum. Also, convert 'size' bytes into a word count */

        count = size/sizeof(short);
	if (size % sizeof(short))
	 { count++;
	 }
        if (count < 0)
         { errorMsg (ECount, driCountMsg, fxnName, count);
	   return (-1);
	 }
	dataPtr = (unsigned short *)buf;
	vemdev->hostCount = count;
	checksum = 0;
	while (count--)
	 { checksum += *dataPtr;
	   *memBuf++ = *dataPtr++;
         }
	vemdev->hostChecksum = checksum;

	/* Announce to the target device that we are ready. Block until 
	   target device is ready to read. Then complete the handshake */


	vemdev->hostStatus = VEM_WRITE_READY;

	k = dev->blocktime * CYCLES_PER_MS;
	WAIT_FOR (vemdev->targetStatus == VEM_READ_READY, 1)

	vemdev->hostReady = 1;

	k = (count+2)*dev->timeout * CYCLES_PER_MS;
	WAIT_FOR (vemdev->targetStatus == 0, 2);

	vemdev->hostStatus = 0;
	vemdev->hostReady = 0;

	return (size);
}

vemProbe (dev)
IO_DEVICE *dev;
{
	/* Initialize device memory areas. Make sure that the device
	   is present in memory */

	int addr = dev->address;
	int i;

	for (i=0; i< (sizeof(struct vemdev)/sizeof(short)); i++)
	 { 
	   if (rciBadAddr (rciBus + addr + i*sizeof(short), sizeof(short)))
	    { return (-1);
	    }
	 }

	/* get the register address from driver argument 0 */

	vemregs = (struct vemregs *)(rciIOpage + dev->args[0]);
	if (rciBadAddr (&vemregs->addrModify, sizeof(char)))
	 { return (-1);
	 }

	vemregs->addrModify = MEM_VME_AMSSIO;

	dev->location = (unsigned char*)(rciBus + addr);
	dev->timeout = DEFAULT_TIMEOUT;
	dev->blocktime = VEM_DEFAULT_BLOCKTIME;
	dev->size = sizeof (struct vemdev);
	return (sizeof (struct vemdev));
}

vemAttach (dev)
IO_DEVICE *dev;
{
	/* Makes sure that the target device is ``there'' by doing
	   a handshake with it. The handshake is done using the 
	   Mode field of the memory area. */
	
	char *fxnName = "vemAttach()";
	struct vemdev *vemdev = (struct vemdev *)dev->location;
	unsigned long k;

	if (vemdev->targetMode != VEM_LISTEN)
	 { errorMsg (EConnect, driListeningMsg, fxnName);
	   return (-1);
	 }
	vemdev->hostMode = VEM_LISTEN;
	vemdev->hostStatus = 0;
	vemdev->hostSignal = 0;
	vemdev->hostSigReady = 0;
	vemdev->hostReady = 0;

	/* send interrupt --- read from a location in the shared memory
	   that is mapped to a VME location that will generate a
	   mailbox interrupt on the other end */

	k = *(char *)(rciBus + dev->args[1]);

	k = dev->timeout*100;
	WAIT_FOR (vemdev->targetMode == 0, 1);
	vemdev->hostMode = VEM_CONNECTED;

	k = dev->timeout*100;
	WAIT_FOR (vemdev->targetMode == VEM_CONNECTED, 2)

	return (1);	
}

vemDetach (dev)
IO_DEVICE *dev;
{
	struct vemdev *vemdev;

	if (dev->location)
	 { vemdev = (struct vemdev *)dev->location;
	   vemdev->hostSigReady = 0;
	   vemdev->hostReady = 0;
	   vemdev->hostStatus = 0;
	   vemdev->hostMode = 0;	
	   return (1);
	 }
	else
	 { return (0);
	 }
}

vemSignal (dev, n)
IO_DEVICE *dev;
int n;
{
	char *fxnName = "vemSignal()";
	struct vemdev *vemdev = (struct vemdev *)(rciBus + dev->address);
       	int k;

	if (n < 1 || n > 2)		/* only sigs 1 and 2 supported */
	 { errorMsg (ECount, driCountMsg, fxnName, n);
	   return (-1);
	 }
	if (vemdev->targetSigReady == VEM_SIGNAL_ACK)
	 { errorMsg (ESync, driSyncMsg, fxnName);
	   return (-1);
	 }
	/* deposit the signal number into memory, send an interrupt to
	   the target device, and then do a full handshake */	    

	vemdev->hostSigReady = VEM_SIGNAL_ACK;
	vemdev->hostSignal = n;

	/* send interrupt --- read from a location in the shared memory
	   that is mapped to a VME location that will generate a
	   mailbox interrupt on the other end */

	k = *(char *)(rciBus + dev->args[1]);

	k = dev->timeout * 100;
	WAIT_FOR (vemdev->targetSigReady == VEM_SIGNAL_ACK, 1);
	vemdev->hostSigReady = 0;
	k = dev->timeout * 100;
	WAIT_FOR (vemdev->targetSigReady == 0, 2)
	return (1);
}

vemIoctl (dev, com, data)
IO_DEVICE *dev;
int com;
void *data;
{
	/* Controls various I/O parameters and also allows interrupts to
	   be enabled or disabled */

	char *fxnName = "vemIoctl()";
	struct vemdev *vemdev = (struct vemdev *)dev->location;

	switch (com)
	 { case IOC_SIGINTER_ENABLE:
	    { struct vemregs *vemregs;

	      vemregs = (struct vemregs *)(rciIOpage + dev->args[0]);
	      if (!rciBadAddr (&vemregs->remnodeCmd, sizeof(char)))
	       { vemregs->remnodeCmd = VEM_CLEARPRIT;
	       }
	      else
	       { return (-1);
	       }
	      break;
	    }
	   case IOC_SIGINTER_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;
	    }
	   case IOC_PRINT_REGS:
	    { /* print out the IO registers */

	      int i;
	      unsigned char *IOregs;

	      IOregs = (unsigned char*)(rciIOpage + dev->args[0]);
	      for (i=0; i<14; i++)
	       { if (!rciBadAddr(&IOregs[i], sizeof(char)))
		  { rciPrintf ("%2d %2x\n", i, IOregs[i]);
		  }
		 else
		  { rciPrintf ("%2d ??\n", i);
		  }
	       }
	      break;	      
	    }
	   default:
	    { errorMsg (EBadIoctl, driIoctlMsg, fxnName, com);
	      return (-1);
	    }
	 }
	return (1);
}

vemTestRead (dev)
IO_DEVICE *dev;
{
	unsigned short int tstat;
	int failcount;

	/* sees if the target device is ready to do a write; i.e., a call
	   to memRead() would not block */
	for (failcount=0;failcount<3;failcount++)
	 { tstat = ((struct vemdev *)(dev->location))->targetStatus;
	   switch (tstat)
	    { case VEM_WRITE_READY:
		 return(1);
		 break;
	      case VEM_READ_READY:
	      case 0:
		 return(0);
		 break;
	      default:
		 rciPrintf ("BIT3 VTR 0x%x\n", (int)tstat);
		 tstat = ((struct vemdev *)
			  (dev->location))->targetStatus;
		 continue;
		 break;
	    }
	 }
	rciPrintf("BIT3 VTR giving up after 3 tries\n");
}

vemTestWrite (dev)
IO_DEVICE *dev;
{
	/* sees if the target device is ready to do a read; i.e., a call
	   to memWrite() would not block */

	if (((struct vemdev *)(dev->location))->targetStatus == VEM_READ_READY)
	 { return (1);
	 }
 	else
	 { return (0);
	 }
}
