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

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

#define DEFAULT_TIMEOUT	200
#define CYCLES_PER_MS	300	/* 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;

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

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

memRead (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 = "memRead()";
	struct memdev *memdev = (struct memdev *)dev->location;

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

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

	memBuf = memdev->targetBuf;

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

	memdev->hostStatus = MEM_READ_READY;

	k = dev->blocktime * CYCLES_PER_MS;
	WAIT_FOR (memdev->targetStatus == MEM_WRITE_READY, 1);

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

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

	count = (int)memdev->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++;
	 }

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

	memdev->hostStatus = 0;
	k = dev->timeout * CYCLES_PER_MS;
	WAIT_FOR (memdev->targetReady == 0, 3);
	
	if (myChecksum != memdev->targetChecksum)
	 { errorMsg (ETransmit, driChecksumMsg, 
				fxnName, memdev->targetChecksum, myChecksum);
	   return (-1);
	 }
	return (dev->byteCount);
}

memWrite (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 = "memWrite()";
	struct memdev *memdev = (struct memdev *)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 = memdev->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;
	memdev->hostCount = count;
	checksum = 0;
	while (count--)
	 { checksum += *dataPtr;
	   *memBuf++ = *dataPtr++;
         }
	memdev->hostChecksum = checksum;

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


	memdev->hostStatus = MEM_WRITE_READY;

	k = dev->blocktime * CYCLES_PER_MS;
	WAIT_FOR (memdev->targetStatus == MEM_READ_READY, 1)

	memdev->hostReady = 1;

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

	memdev->hostStatus = 0;
	memdev->hostReady = 0;

	return (size);
}

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

	int addr = dev->address;
	int i;

	extern unsigned char *rciIOpage;

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

	/* XXX hard wire the csr addresses for now XXX */

	memregs = (struct memregs *)(rciIOpage + dev->args[0]);
	memregs->addrModify = MEM_VME_AMSSIO;

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

#define MAILBOXADDR	0x7e001

memAttach (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 = "memAttach()";
	struct memdev *memdev = (struct memdev *)dev->location;
	unsigned long k;

	if (memdev->targetMode != MEM_LISTEN)
	 { errorMsg (EConnect, driListeningMsg, fxnName);
	   return (-1);
	 }
	memdev->hostMode = MEM_LISTEN;
	memdev->hostStatus = 0;
	memdev->hostSignal = 0;
	memdev->hostSigReady = 0;
	memdev->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->blocktime*CYCLES_PER_MS;
	WAIT_FOR (memdev->targetMode == 0, 1);
	memdev->hostMode = MEM_CONNECTED;

	k = dev->timeout*CYCLES_PER_MS;
	WAIT_FOR (memdev->targetMode == MEM_CONNECTED, 2);

	return (1);	
}

memDetach (dev)
IO_DEVICE *dev;
{
	struct memdev *memdev;

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

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

	if (n < 1 || n > 2)		/* only sigs 1 and 2 supported */
	 { errorMsg (ECount, driCountMsg, fxnName, n);
	   return (-1);
	 }
	if (memdev->targetSigReady == MEM_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 */	    

	memdev->hostSigReady = MEM_SIGNAL_ACK;
	memdev->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 * CYCLES_PER_MS;
	WAIT_FOR (memdev->targetSigReady == MEM_SIGNAL_ACK, 1);
	memdev->hostSigReady = 0;
	k = dev->timeout * CYCLES_PER_MS;
	WAIT_FOR (memdev->targetSigReady == 0, 2);
	return (1);
}

memIoctl (dev, com, data)
IO_DEVICE *dev;
int com;
void *data;
{
	char *fxnName = "memIoctl()";

	/* Controls various I/O parameters and also allows interrupts to
	   be enabled or disabled */

	struct memdev *memdev = (struct memdev *)dev->location;

	switch (com)
	 { case IOC_SIGINTER_ENABLE:
	    { 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;
	    }
	   default:
	    { errorMsg (EBadIoctl, driIoctlMsg, fxnName, com);
	      return (-1);
	    }
	 }
	return (1);
}

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

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

memTestWrite (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 memdev *)(dev->location))->targetStatus == MEM_READ_READY)
	 { return (1);
	 }
 	else
	 { return (0);
	 }
}
