/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
*     National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
*     Operator of Los Alamos National Laboratory.
* EPICS BASE Versions 3.13.7
* and higher are distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution. 
\*************************************************************************/
#define ER_DEBUG /**/
/*
 *      Author:		John R. Winans
 *      Date:   	07-21-93
 */

#include <vxWorks.h>
#include <stdlib.h>
#include <stdio.h>
#include <iosLib.h>
#include <taskLib.h>
#include <semLib.h>
#include <memLib.h>
#include <rebootLib.h>
#include <intLib.h>
#include <lstLib.h>
#include <vme.h>
#include <sysLib.h>
#include <iv.h>

#include <module_types.h>
#include <devSup.h>
#include <recGbl.h>
#include <dbDefs.h>
#include <dbAccess.h>
#include <epicsPrint.h>
#include <link.h>
#include <dbScan.h>
#include <eventRecord.h>
#include <dbCommon.h>

#include <erDefs.h>
#include <erRecord.h>
#include <ereventRecord.h>
#include <devApsEr.h>


#ifdef ER_DEBUG
#define STATIC
#else
#define STATIC static
#endif

typedef struct ApsErStruct
{
  unsigned short	Control;
  unsigned short	EventRamAddr;
  unsigned short	EventRamData;
  unsigned short	OutputPulseEnables;
  unsigned short	OutputLevelEnables;
  unsigned short	TriggerEventEnables;
  unsigned short	EventCounterLo;
  unsigned short	EventCounterHi;
  unsigned short	TimeStampLo;
  unsigned short	TimeStampHi;

  unsigned short	EventFifo;
  unsigned short	EventTimeHi;
  unsigned short	DelayPulseEnables;
  unsigned short	DelayPulseSelect;
  unsigned short	PulseDelay;
  unsigned short	PulseWidth;
  unsigned short	IrqVector;
  unsigned short	IrqEnables;

} ApsErStruct;


/* 
 * CONTROL_REG_OR_STRIP = bits that we should never leave set when or-ing
 * on other bits in the control register.
 */
#define CONTROL_REG_OR_STRIP	(~0x3c9f)

#define NUM_EVENTS	256

#define ER_IRQ_ALL	0x000f
#define ER_IRQ_OFF	0x0000
#define ER_IRQ_TELL	0xffff
#define ER_IRQ_EVENT	0x0008
#define ER_IRQ_HEART	0x0004
#define ER_IRQ_FIFO	0x0002
#define ER_IRQ_TAXI	0x0001

typedef struct ErLinkStruct
{
  struct erRecord	*pRec;
  int			Card;
  long			IrqVector;
  long			IrqLevel;
  ApsErStruct		*pEr;
  SEM_ID		LinkLock;	/* Lock for the card */
  unsigned short	ErEventTab[NUM_EVENTS]; /* current view of the RAM */
  IOSCANPVT		IoScanPvt[NUM_EVENTS];	/* event-based rec processing */
  EVENT_FUNC		EventFunc;
  ERROR_FUNC		ErrorFunc;
} ErLinkStruct;

#define NUM_ER_LINKS	4
static int              ErNumLinks = 0;	/* User configurable link limit */
static int		ConfigureLock = 0;
STATIC ErLinkStruct	ErLink[NUM_ER_LINKS];
int			ErDebug = 0;

int			ErStallIrqs = 1;

STATIC long ErMasterEnableGet(ApsErStruct *pParm);
STATIC long ErMasterEnableSet(ApsErStruct *pParm, int Enable);
STATIC long ErSetTrg(ApsErStruct *pParm, int Channel, int Enable);
STATIC long ErSetOtp(ApsErStruct *pParm, int Channel, int Enable);
STATIC long ErSetOtl(ApsErStruct *pParm, int Channel, int Enable);
STATIC long ErSetDg(ApsErStruct *pParm, int Channel, int Enable, unsigned short Delay, unsigned short Width);
STATIC unsigned short ErEnableIrq(ApsErStruct *pParm, unsigned short Mask);
STATIC int ErRegisterIrq(ErLinkStruct *pLink, int IrqVector, int IrqLevel);
STATIC long ErCheckTaxi(ApsErStruct *pParm);
STATIC int ErGetRamStatus(ApsErStruct *pParm, int Ram);
STATIC int ErResetAll(ApsErStruct *pParm);
STATIC int ErUpdateRam(ApsErStruct *pParm, unsigned short *RamBuf);
STATIC int ErProgramRam(ApsErStruct *pParm, unsigned short *RamBuf, int Ram);
STATIC int ErEnableRam(ApsErStruct *pParm, int Ram);
STATIC long proc(struct erRecord  *pRec);

/******************************************************************************
 *
 * Routine used to verify that a given card number is valid.
 * Returns zero if valid, nonzero otherwise.
 *
 ******************************************************************************/
long ErHaveReceiver(int Card)
{
  if ((Card < 0)||(Card >= ErNumLinks))
    return(-1);
  if (ErLink[Card].pEr == NULL)
    return(-1);
  return(256);	/* Return the number of possible event codes, 0-255 */
}
/******************************************************************************
 *
 * Register a listener for the event system.  Every time we get an event from
 * the event receiver, we will call the registered listener and pass in the
 * event number received and the tick counter value when that event was
 * received.
 *
 ******************************************************************************/
long ErRegisterEventHandler(int Card, EVENT_FUNC func)
{
  if (ErDebug)
    printf("ErRegisterEventHandler(%d, %p\n", Card, func);

  ErLink[Card].EventFunc = func;
  return(0);
}
/******************************************************************************
 *
 * Register a listener for the event system.  Every time we get an event from
 * the event receiver, we will call the registered listener and pass in the
 * event number received and the tick counter value when that event was
 * received.
 *
 ******************************************************************************/
long ErRegisterErrorHandler(int Card, ERROR_FUNC func)
{
  if (ErDebug)
    printf("ErRegisterEventHandler(%d, %p\n", Card, func);

  ErLink[Card].ErrorFunc = func;
  return(0);
}
/******************************************************************************
 *
 * Return the current tick counter value.
 *
 ******************************************************************************/
long ErGetTicks(int Card, unsigned long *Ticks)
{
  int key;
  if (ErHaveReceiver(Card) < 0)
    return(-1);

  /* BUG -- Do we read the HI first or the low? */

  key = intLock();
  *Ticks = ErLink[Card].pEr->EventCounterLo;
  *Ticks += ErLink[Card].pEr->EventCounterHi << 16;
  intUnlock(key);

  return(0);
}

/******************************************************************************
 *
 * This routine is to be called in the startup script in order to init the
 * card addresses and the associated IRQ vectors and levels.
 *
 * By default there are no cards configured.
 *
 ******************************************************************************/
int ErConfigure(int Card, unsigned long CardAddress, unsigned int IrqVector, unsigned int IrqLevel)
{
  short	Junk;

  if (ConfigureLock != 0)
  {
    epicsPrintf("devApsEr: Cannot change configuration after init.  Request ignored\n");
    return(-1);
  }
  if (Card >= NUM_ER_LINKS)
  {
    epicsPrintf("devApsEr: Card number invalid, must be 0-%d\n", NUM_ER_LINKS);
    return(-1);
  }
  if (CardAddress > 0xffff)
  {
    epicsPrintf("devApsEr: Card address invalid, must be 0x0000-0xffff\n");
    return(-1);
  }
  if(sysBusToLocalAdrs(VME_AM_SUP_SHORT_IO, (char*)CardAddress, (char**)&ErLink[Card].pEr)!=OK)
  {
    epicsPrintf("devApsEr: Failure mapping requested A16 address\n");
    ErLink[Card].pEr = NULL;
    return(-1);
  }
  if (vxMemProbe((char*)&ErLink[Card].pEr->Control, READ, sizeof(short), (char*)&Junk) != OK)
  {
    epicsPrintf("devApsEr: Failure probing for event receiver... Card disabled\n");
    ErLink[Card].pEr = NULL;
    return(-1);
  }
  ErLink[Card].IrqVector = IrqVector;
  ErLink[Card].IrqLevel = IrqLevel;

  if (Card >= ErNumLinks)
    ErNumLinks = Card+1;

  return(0);
}
/******************************************************************************
 *
 * A function to shut up an event receiver in case we get a ^X style reboot.
 *
 ******************************************************************************/
static void ErRebootFunc(void)
{
  int	j = 0;

  while (j < ErNumLinks)
  {
    if (ErLink[j].pEr != NULL)
      ErEnableIrq(ErLink[j].pEr, ER_IRQ_OFF);
    ++j;
  }
  return;
}
/******************************************************************************
 *
 * Register and init the IRQ handlers for each ER card.
 *
 ******************************************************************************/
STATIC long ErInitDev(int pass)
{
  int	j;
  int	k;
  static int OneShotFlag = 1;

  if (ErDebug)
    printf("ErInitDev(%d)\n", pass);

  if (OneShotFlag)
  {
    OneShotFlag = 0;
    rebootHookAdd((FUNCPTR)ErRebootFunc);

    ConfigureLock = 1;	/* Prevent any future ErConfigure's */

    /* Clear the record pointers */
    for (j=0;j<ErNumLinks;j++)
    {
      if (ErHaveReceiver(j) >= 0)
      {
        if ((ErLink[j].LinkLock = semBCreate(SEM_Q_PRIORITY, SEM_FULL)) == NULL)
	  return(-1);
        for (k=0;k<256;k++)
        {
	  ErLink[j].Card = j;
	  ErLink[j].ErEventTab[k] = 0;
	  /* ErLink[j].EventFunc = NULL; Should be NULL because is static */
	  /* ErLink[j].ErrorFunc = NULL; */
	  scanIoInit(&ErLink[j].IoScanPvt[k]);
        }
        ErLink[j].pRec = NULL;
        ErResetAll(ErLink[j].pEr);
        ErRegisterIrq(&ErLink[j], ErLink[j].IrqVector, ErLink[j].IrqLevel);
      }
    }
  }
  if (pass == 1)
  {
    for (j=0;j<ErNumLinks;j++)
      if (ErHaveReceiver(j) >= 0)
        ErEnableIrq(ErLink[j].pEr, ER_IRQ_ALL);
  }
  return(0);
}
#if 0
scum() 
{ 
  int	j;

  for (j=0;j<ErNumLinks;j++) 
    if (ErHaveReceiver(j) >= 0) 
      ErEnableIrq(ErLink[j].pEr, ER_IRQ_ALL); 
}
#endif
/******************************************************************************
 *
 * Init routine for the ER record type.
 *
 ******************************************************************************/
STATIC long init_record(struct erRecord *pRec)
{
  ErLinkStruct  *pLink;

  if (ErDebug)
    printf("devApsEr::init_record() entered\n");

  if (ErHaveReceiver(pRec->out.value.vmeio.card) < 0)
  {
    recGblRecordError(S_db_badField, (void *)pRec, "devApsEr::ErInitErRec() Invalid card number");
    return(S_db_badField);
  }
  pLink = &ErLink[pRec->out.value.vmeio.card];

  /* Make sure we only have one record for a given ER card */
  if (pLink->pRec != NULL)
  {
    recGblRecordError(S_db_badField, (void *)pRec, "devApsEr::ErInitErRec() onlyone record allowed per card");
    return(S_db_badField);
  }
  pLink->pRec = pRec;

  proc(pRec);

  return(0);
}
/******************************************************************************
 *
 * Process routine for the ER record type.
 *
 ******************************************************************************/
STATIC long proc(struct erRecord  *pRec)
{
  ErLinkStruct  *pLink = &ErLink[pRec->out.value.vmeio.card];

  if (pRec->tpro > 10)
    printf("devApsEr::proc(%s) entered\n", pRec->name);

  /* Make sure the card is present */
  if (ErHaveReceiver(pRec->out.value.vmeio.card) < 0)
    return(0);

  semTake(pLink->LinkLock, WAIT_FOREVER);

  if (pRec->enab != ErMasterEnableGet(pLink->pEr))
    ErMasterEnableSet(pLink->pEr, pRec->enab);

  ErSetTrg(pLink->pEr, 0, pRec->trg0);
  ErSetTrg(pLink->pEr, 1, pRec->trg1);
  ErSetTrg(pLink->pEr, 2, pRec->trg2);
  ErSetTrg(pLink->pEr, 3, pRec->trg3);
  ErSetTrg(pLink->pEr, 4, pRec->trg4);
  ErSetTrg(pLink->pEr, 5, pRec->trg5);
  ErSetTrg(pLink->pEr, 6, pRec->trg6);

  ErSetOtp(pLink->pEr, 0, pRec->otp0);
  ErSetOtp(pLink->pEr, 1, pRec->otp1);
  ErSetOtp(pLink->pEr, 2, pRec->otp2);
  ErSetOtp(pLink->pEr, 3, pRec->otp3);
  ErSetOtp(pLink->pEr, 4, pRec->otp4);
  ErSetOtp(pLink->pEr, 5, pRec->otp5);
  ErSetOtp(pLink->pEr, 6, pRec->otp6);
  ErSetOtp(pLink->pEr, 7, pRec->otp7);
  ErSetOtp(pLink->pEr, 8, pRec->otp8);
  ErSetOtp(pLink->pEr, 9, pRec->otp9);
  ErSetOtp(pLink->pEr, 10, pRec->otpa);
  ErSetOtp(pLink->pEr, 11, pRec->otpb);
  ErSetOtp(pLink->pEr, 12, pRec->otpc);
  ErSetOtp(pLink->pEr, 13, pRec->otpd);

  ErSetOtl(pLink->pEr, 0, pRec->otl0);
  ErSetOtl(pLink->pEr, 1, pRec->otl1);
  ErSetOtl(pLink->pEr, 2, pRec->otl2);
  ErSetOtl(pLink->pEr, 3, pRec->otl3);
  ErSetOtl(pLink->pEr, 4, pRec->otl4);
  ErSetOtl(pLink->pEr, 5, pRec->otl5);
  ErSetOtl(pLink->pEr, 6, pRec->otl6);

  ErSetDg(pLink->pEr, 0, pRec->dg0e, pRec->dg0d, pRec->dg0w);
  ErSetDg(pLink->pEr, 1, pRec->dg1e, pRec->dg1d, pRec->dg1w);
  ErSetDg(pLink->pEr, 2, pRec->dg2e, pRec->dg2d, pRec->dg2w);
  ErSetDg(pLink->pEr, 3, pRec->dg3e, pRec->dg3d, pRec->dg3w);

  if (ErCheckTaxi(pLink->pEr) != 0)
    pRec->taxi = 1;
  else
    pRec->taxi = 0;

  semGive(pLink->LinkLock);

  pRec->udf = 0;
  return(0);
}
ErDsetStruct devEr={ 5, NULL, ErInitDev, init_record, NULL, proc};

/******************************************************************************
 *
 * Init routine for the EREVENT record type.
 *
 ******************************************************************************/
STATIC long ErEventInitRec(struct ereventRecord *pRec)
{
  if (ErDebug)
    printf("ErEventInitRec(%s)\n", pRec->name);

  if (ErHaveReceiver(pRec->out.value.vmeio.card) < 0)
  {
    recGblRecordError(S_db_badField, (void *)pRec, "devApsEr::ErEventInitRec() invalid card number in INP field");
    return(S_db_badField);
  }
  if ((pRec->out.value.vmeio.signal < 0)||(pRec->out.value.vmeio.signal > 255))
  {
    recGblRecordError(S_db_badField, (void *)pRec, "devApsEr::ErEventInitRec() invalid signal number in INP field");
    return(S_db_badField);
  }

  pRec->lenm = 300;	/* Force setting on first process */
  pRec->lout = 0;	/* Clear the 'last' event mask */
  return(0);
}
/******************************************************************************
 *
 * Process routine for the EREVENT record type.
 *
 ******************************************************************************/
STATIC long ErEventProc(struct ereventRecord  *pRec)
{
  ErLinkStruct  *pLink = &ErLink[pRec->out.value.vmeio.card];
  int		DownLoadRam = 0;
  unsigned 	short Mask;
  
  if (pRec->tpro > 10)
    printf("ErEventProc(%s) entered\n", pRec->name);

  /* Make sure the card is present */
  if (ErHaveReceiver(pRec->out.value.vmeio.card) < 0)
    return(0);

  if (pRec->enab != 0)
  {
    if (pRec->tpro > 10)
      printf("ErEventProc(%s) enable=true\n", pRec->name);

    semTake(pLink->LinkLock, WAIT_FOREVER);
    if (pRec->enm != pRec->lenm)
    {
      if (pRec->tpro > 10)
	printf("ErEventProc(%s) event number changed %ld-%ld\n", pRec->name, pRec->lenm, pRec->enm);
      /* Transfer the event info to new position */ 
      if ((pRec->lenm < 256) && (pRec->lenm >= 0))
        pLink->ErEventTab[pRec->lenm] = 0;

      DownLoadRam = 1;
      pRec->lenm = pRec->enm;
    }
    /* Build event mask */
    /* NOTE: that we never latch the time in the 32 bit latch */

    Mask = 0;
    if (pRec->out0 != 0) Mask |= 0x0001;
    if (pRec->out1 != 0) Mask |= 0x0002;
    if (pRec->out2 != 0) Mask |= 0x0004;
    if (pRec->out3 != 0) Mask |= 0x0008;
    if (pRec->out4 != 0) Mask |= 0x0010;
    if (pRec->out5 != 0) Mask |= 0x0020;
    if (pRec->out6 != 0) Mask |= 0x0040;
    if (pRec->out7 != 0) Mask |= 0x0080;
    if (pRec->out8 != 0) Mask |= 0x0100;
    if (pRec->out9 != 0) Mask |= 0x0200;
    if (pRec->outa != 0) Mask |= 0x0400;
    if (pRec->outb != 0) Mask |= 0x0800;
    if (pRec->outc != 0) Mask |= 0x1000;
    if (pRec->outd != 0) Mask |= 0x2000;
    if (pRec->vme != 0)  Mask |= 0x8000;

    if (pRec->tpro > 10)
      printf("ErEventProc(%s) New RAM mask is 0x%4.4X\n", pRec->name, Mask);
    if (Mask != pRec->lout)
      DownLoadRam = 1;

    if (DownLoadRam != 0)
    {
      if (pRec->enm != 0)
      {
        pLink->ErEventTab[pRec->enm] = Mask;
        pRec->lout = Mask;
      }
      ErUpdateRam(pLink->pEr, pLink->ErEventTab);
    }
    if (pRec->tpro > 10)
      printf("ErEventProc(%s) I/O operations complete\n", pRec->name);
    semGive(pLink->LinkLock);
  }
  else
  {
    /* BUG kill event if was ever enabled! */
  }

  return(0);
}
ErDsetStruct devErevent={ 5, NULL, NULL, ErEventInitRec, NULL, ErEventProc};
/******************************************************************************
 *
 * Routine to initialize an event record.  This is a regular EPICS event 
 * record!! Not an ER or EG event record.  We allow the user to use the
 * regular epics event records to request processing on an event that is
 * received from the event receiver.
 *
 * All we gotta do here is set the dpvt field to point to the proper 
 * IOSCANEVENT field so that we can later return it on getIoScanInfo() calls.
 *
 ******************************************************************************/
STATIC long ErEpicsEventInit(struct eventRecord *pRec)
{
  pRec->dpvt = NULL;	/* In case problems arise below */

  if(ErDebug)
    printf("ErEpicsEventInit(%s) %d %d\n", pRec->name, pRec->inp.value.vmeio.card, pRec->inp.value.vmeio.signal);

  if (ErHaveReceiver(pRec->inp.value.vmeio.card) < 0)
  {
    recGblRecordError(S_db_badField, (void *)pRec, "devApsEr::ErEventInitRec() invalid card number in INP field");
    return(S_db_badField);
  }
  if ((pRec->inp.value.vmeio.signal < 0)||(pRec->inp.value.vmeio.signal > 255))
  {
    recGblRecordError(S_db_badField, (void *)pRec, "devApsEr::ErEventInitRec() invalid signal number in INP field");
    return(S_db_badField);
  }

  pRec->dpvt = &ErLink[pRec->inp.value.vmeio.card].IoScanPvt[pRec->inp.value.vmeio.signal];
  return(0);
}
/******************************************************************************
 *
 * Routine to get the IOSCANPVT value for an EVENT record type.
 *
 ******************************************************************************/
STATIC long ErEpicsEventGetIoScan(int cmd, struct eventRecord *pRec, IOSCANPVT *pPvt)
{
  if (pRec->dpvt) {
      *pPvt = *((IOSCANPVT*)(pRec->dpvt));
      return(0);
  }
  return TRUE; /* No hardware */
}
ErDsetStruct devErEpicsEvent={5, NULL, NULL, ErEpicsEventInit, ErEpicsEventGetIoScan, NULL};

/******************************************************************************
 *
 * Inquire as to if the card is enabled or not.
 *
 ******************************************************************************/
STATIC long ErMasterEnableGet(ApsErStruct *pParm)
{
  volatile ApsErStruct  *pEr = pParm;
  return((pEr->Control & 0x8000) != 0);
}
/******************************************************************************
 *
 * Enable/disable the card.
 *
 ******************************************************************************/
STATIC long ErMasterEnableSet(ApsErStruct *pParm, int Enable)
{
  volatile ApsErStruct  *pEr = pParm;

  if (Enable != 0)
    pEr->Control = (pEr->Control&CONTROL_REG_OR_STRIP)|0x8000;
  else
    pEr->Control = (pEr->Control&CONTROL_REG_OR_STRIP)&~0x8000;
  return(0);
}
/******************************************************************************
 *
 * Check to see if we have a taxi violation and reset it if so.
 *
 ******************************************************************************/
STATIC long ErCheckTaxi(ApsErStruct *pParm)
{
  volatile ApsErStruct  *pEr = pParm;

  if (pEr->Control & 0x0001)
  {
    pEr->Control = (pEr->Control&CONTROL_REG_OR_STRIP)|0x0001;
    return(1);
  }
  return(0);
}
/******************************************************************************
 *
 * Set/clear an enable bit in the trigger mask.
 *
 ******************************************************************************/
STATIC long ErSetTrg(ApsErStruct *pParm, int Channel, int Enable)
{
  volatile ApsErStruct  *pEr = pParm;
  unsigned short	mask;

  mask = 1;
  mask <<= Channel;

  if (Enable != 0)
    pEr->TriggerEventEnables |= mask;
  else
    pEr->TriggerEventEnables &= ~mask;
  return(0);
}
/******************************************************************************
 *
 * Set/clear an enable bit in the one-shot mask.
 *
 ******************************************************************************/
STATIC long ErSetOtp(ApsErStruct *pParm, int Channel, int Enable)
{
  volatile ApsErStruct  *pEr = pParm;
  unsigned short	mask;

  mask = 1;
  mask <<= Channel;

  if (Enable != 0)
    pEr->OutputPulseEnables |= mask;
  else
    pEr->OutputPulseEnables &= ~mask;
  return(0);
}
/******************************************************************************
 *
 * Set/clear an enable bit in the level-trigger mask.
 *
 ******************************************************************************/
STATIC long ErSetOtl(ApsErStruct *pParm, int Channel, int Enable)
{
  volatile ApsErStruct  *pEr = pParm;
  unsigned short	mask;

  mask = 1;
  mask <<= Channel;

  if (Enable != 0)
    pEr->OutputLevelEnables |= mask;
  else
    pEr->OutputLevelEnables &= ~mask;
  return(0);
}
/******************************************************************************
 *
 * Set/clear an enable bit in the programmable pulse delay mask.  If enabling
 * a channel, the delay and width are also set.
 *
 ******************************************************************************/
STATIC long ErSetDg(ApsErStruct *pParm, int Channel, int Enable, unsigned short Delay, unsigned short Width)
{
  volatile ApsErStruct  *pEr = pParm;
  unsigned short	mask;

  mask = 1;
  mask <<= Channel;

  if (Enable != 0)
  {
    pEr->DelayPulseSelect = Channel;
    pEr->PulseDelay = Delay;
    pEr->PulseWidth = Width;
    pEr->DelayPulseEnables |= mask;
  }
  else
    pEr->DelayPulseEnables &= ~mask;
  return(0);
}
/******************************************************************************
 *
 * A debugging aid that dumps out all the regs on the ER board.
 *
 ******************************************************************************/
STATIC int ErDumpRegs(ApsErStruct *pParm)
{
  volatile ApsErStruct	*pEr = pParm;

  printf("%4.4X %4.4X %4.4X %4.4X %4.4X %4.4X %4.4X %4.4X %4.4X %4.4X\n",
	pEr->Control, pEr->EventRamAddr, pEr->EventRamData, pEr->OutputPulseEnables,
	pEr->OutputLevelEnables, pEr->TriggerEventEnables, pEr->EventCounterLo,
	pEr->EventCounterHi, pEr->TimeStampLo, pEr->TimeStampHi);

  printf("%4.4X %4.4X %4.4X %4.4X %4.4X %4.4X %4.4X %4.4X\n",
	pEr->EventFifo, pEr->EventTimeHi, pEr->DelayPulseEnables, pEr->DelayPulseSelect,
	pEr->PulseDelay, pEr->PulseWidth, pEr->IrqVector, pEr->IrqEnables);

  return(0);
}

/******************************************************************************
 *
 * Reset, clear and disable everything on the board.
 *
 ******************************************************************************/
STATIC int ErResetAll(ApsErStruct *pParm)
{
  volatile ApsErStruct  *pEr = pParm;

  pEr->Control = 0x201d;
  pEr->Control = 0x208d;

  while(pEr->Control & 0x0080)
    taskDelay(1);

  pEr->OutputPulseEnables = 0;
  pEr->OutputLevelEnables = 0;
  pEr->DelayPulseEnables = 0;
  pEr->DelayPulseSelect = 0;
  pEr->IrqEnables = 0;

  pEr->Control = (pEr->Control&CONTROL_REG_OR_STRIP)|0x8000;

  return(0);
}
/******************************************************************************
 *
 * Receive a hardware IRQ from an ER board.
 *
 * BUG -- should add errMessage calls for problem events & include storm
 *        control on them.
 *
 ******************************************************************************/
STATIC void ErIrqHandler(ErLinkStruct *pLink)
{
  volatile ApsErStruct  *pEr = pLink->pEr;
  unsigned short	mask;
  unsigned short	j;
  unsigned short	k;
  short			z;
  unsigned long		Time;

  mask = pEr->Control;
  if(ErDebug > 9)
    epicsPrintf("ErIrqHandler() control =%4.4X\n", mask);

  if (mask&0x1000)
  { /* Got a lost heart beat */
    pEr->Control = (mask&CONTROL_REG_OR_STRIP)|0x1000;

    if (pLink->ErrorFunc != NULL)
      (*pLink->ErrorFunc)(pLink->Card, ERROR_HEART);

    if(ErDebug)
      epicsPrintf("ErIrqHandler() lost heart beat\n");
  }

  /* Read any stuff from the FIFO */
  if (mask&0x0800)
  {
    pEr->Control = (mask&CONTROL_REG_OR_STRIP)|0x0800;
    if(ErDebug > 10)
      epicsPrintf("ErIrqHandler() clearing event with %4.4X\n", (mask&CONTROL_REG_OR_STRIP)|0x0800);

    z = 10;	/* Make sure we don't get into a major spin loop */
    while ((pEr->Control & 0x0002)&&(--z > 0))
    {
      unsigned short	EventNum;

      j = pEr->EventFifo;
      k = pEr->EventTimeHi;

      /* Mark the event time in the proper array place */
      EventNum = j & 0x00ff;
      Time = (k<<8)|(j>>8);

      if (pLink->EventFunc != NULL)
      {
	(*pLink->EventFunc)(pLink->Card, EventNum, Time);
        if(ErDebug)
          epicsPrintf("ErIrqHandler() Calling %p", pLink->EventFunc);
      }

      if(ErDebug)
        epicsPrintf("ErIrqHandler() %6.6X-%2.2X\n", Time, EventNum);

      /* Schedule processing for any event-driven records */
      scanIoRequest(pLink->IoScanPvt[EventNum]);
    }
  }
  /* Kill any fifo overflow status */
  if (mask&0x0004)
  {
    pEr->Control = (mask&CONTROL_REG_OR_STRIP)|0x0004;

    if (pLink->ErrorFunc != NULL)
      (*pLink->ErrorFunc)(pLink->Card, ERROR_LOST);

    if(ErDebug)
    epicsPrintf("ErIrqHandler() FIFO overflow %4.4X\n", (mask&CONTROL_REG_OR_STRIP)|0x0004);
  }
  if (mask&0x0001)
  {
    pEr->Control = (mask&CONTROL_REG_OR_STRIP)|0x0001;

    if (pLink->ErrorFunc != NULL)
      (*pLink->ErrorFunc)(pLink->Card, ERROR_TAXI);

    if(ErDebug)
      epicsPrintf("ErIrqHandler() taxi violation %4.4X\n", pEr->Control = (mask&CONTROL_REG_OR_STRIP)|0x0001);
  }
  /*
   * Disable and then re-enable the IRQs so board re-issues any 
   * that are still pending
   */

  pEr->Control = (mask&CONTROL_REG_OR_STRIP)&~0x4000;
  pEr->Control = (mask&CONTROL_REG_OR_STRIP)| 0x4000;

  return;
}

/******************************************************************************
 *
 * Set up the IRQs for the receiver.
 * (Should only be called once.)
 *
 ******************************************************************************/
STATIC int ErRegisterIrq(ErLinkStruct *pLink, int IrqVector, int IrqLevel)
{
  volatile ApsErStruct  *pEr = pLink->pEr;

  if (IrqVector != -1)
  {
    intConnect(INUM_TO_IVEC(IrqVector), ErIrqHandler, pLink);
    sysIntEnable(IrqLevel);
    pEr->IrqVector = IrqVector;
  }
  return(0);
}

/******************************************************************************
 *
 * Enable or disable the IRQs from the Event Receiver.
 *
 ******************************************************************************/
STATIC unsigned short ErEnableIrq(ApsErStruct *pParm, unsigned short Mask)
{
  volatile ApsErStruct  *pEr = pParm;
  if (Mask == ER_IRQ_OFF)
  {
    pEr->IrqEnables = 0;
    pEr->Control = (pEr->Control&CONTROL_REG_OR_STRIP) & ~0x4000;
  }
  else if (Mask == ER_IRQ_TELL)
  {
    if (pEr->Control & 0x4000)
      return(pEr->IrqEnables & ER_IRQ_ALL);
  }
  else
  {
    pEr->IrqEnables = Mask;
    pEr->Control = (pEr->Control&CONTROL_REG_OR_STRIP)|0x4000;
  }

  return(0);
}

/******************************************************************************
 *
 * We load what ever RAM is not running. And Enable it.
 *
 ******************************************************************************/
STATIC int ErUpdateRam(ApsErStruct *pParm, unsigned short *RamBuf)
{
  int	ChosenRam = 2;

  /* Find the idle RAM */
  if (ErGetRamStatus(pParm, 1) == 0)
    ChosenRam = 1;	/* RAM 1 is currently idle */

  ErProgramRam(pParm, RamBuf, ChosenRam);

  ErEnableRam(pParm, ChosenRam);
  return(0);
}
/******************************************************************************
 *
 * Download the entire RAM from a buffer.
 *
 ******************************************************************************/
STATIC int ErProgramRam(ApsErStruct *pParm, unsigned short *RamBuf, int Ram)
{
  volatile ApsErStruct  *pEr = pParm;
  int			j;

  if(ErDebug)
    printf("RAM download for RAM %d\n", Ram);

  if (Ram == 1)
  {
    if ((pEr->Control&0x0300) == 0x0200)
      return(-1);		/* RAM 1 is enabled */

    /* select VME access to ram 1 */
    pEr->Control = (pEr->Control&CONTROL_REG_OR_STRIP) & ~0x0040;	
  }
  else
  if (Ram == 2)
  {
    if ((pEr->Control&0x0300) == 0x0300)
      return(-1);		/* RAM 2 is enabled */

    /* select VME access to ram 2 */
    pEr->Control = (pEr->Control&CONTROL_REG_OR_STRIP)|0x0040;	
  }
  else
    return(-1);

  /* Reset RAM address */
  pEr->Control = (pEr->Control&CONTROL_REG_OR_STRIP)|0x0010;

  /* enable auto increment */
  pEr->Control = (pEr->Control&CONTROL_REG_OR_STRIP)|0x0020;	

  pEr->EventRamAddr = 0;

  for (j=0; j<256; j++)
  {
    pEr->EventRamData = RamBuf[j];
    if((ErDebug)&&(RamBuf[j] != 0))
      printf("%d: %d\n", j, RamBuf[j]);
  }
  /* Turn off auto-increment */
  pEr->Control = (pEr->Control&CONTROL_REG_OR_STRIP) & ~0x0020;	
  return(0);
}

/******************************************************************************
 *
 * Print the entire RAM from a buffer.  We skip the null events.
 *
 ******************************************************************************/
STATIC int ErDumpRam(ApsErStruct *pParm, int Ram)
{
  volatile ApsErStruct  *pEr = pParm;
  int			j;
  int			x, y;

  if (Ram == 1)
  {
    if ((pEr->Control&0x0300) == 0x0200)
      return(-1);		/* RAM 1 is enabled */

    /* select VME access to ram 1 */
    pEr->Control = (pEr->Control&CONTROL_REG_OR_STRIP) & ~0x0040;	
  }
  else
  if (Ram == 2)
  {
    if ((pEr->Control&0x0300) == 0x0300)
      return(-1);		/* RAM 2 is enabled */

    /* select VME access to ram 2 */
    pEr->Control = (pEr->Control&CONTROL_REG_OR_STRIP)|0x0040;	
  }
  else
    return(-1);

  /* Reset RAM address */
  pEr->Control = (pEr->Control&CONTROL_REG_OR_STRIP)|0x0010;

  /* enable auto increment */
  pEr->Control = (pEr->Control&CONTROL_REG_OR_STRIP)|0x0020;	

  pEr->EventRamAddr = 0;

  for (j=0; j<256; j++)
  {
    x = pEr->EventRamAddr & 0x00ff;
    y = pEr->EventRamData;
    if (y != 0)
      printf("RAM %d, %d\n", x, y);
  }

  /* turn off auto-increment */
  pEr->Control = (pEr->Control&CONTROL_REG_OR_STRIP) & ~0x0020;	
  
  return(0);
}

/******************************************************************************
 *
 * Select the specified RAM for operation
 *
 ******************************************************************************/
STATIC int ErEnableRam(ApsErStruct *pParm, int Ram)
{
  volatile ApsErStruct  *pEr = pParm;

  if (Ram == 0)
  {
    pEr->Control = (pEr->Control&CONTROL_REG_OR_STRIP) & ~0x0200;
    return(0);
  }
  if (Ram == 1)
    pEr->Control = (pEr->Control&CONTROL_REG_OR_STRIP) & ~0x0100;
  else if (Ram == 2)
    pEr->Control = (pEr->Control&CONTROL_REG_OR_STRIP) | 0x0100;
  else
    return(-1);

  /* Set the map enable bit */
  pEr->Control = (pEr->Control&CONTROL_REG_OR_STRIP) | 0x0200;	

  return(0);
}
/******************************************************************************
 *
 * Return 1 if the requested RAM is enabled, 0 otherwise.  We return -1 on
 * error.
 *
 ******************************************************************************/
STATIC int ErGetRamStatus(ApsErStruct *pParm, int Ram)
{
  volatile ApsErStruct  *pEr = pParm;

  /* Check if RAM map is not enabled */
  if ((pEr->Control & 0x0200) == 0)
    return(0);

  if (Ram == 1)
    return((pEr->Control & 0x0100) == 0);

  if (Ram == 2)
    return((pEr->Control & 0x0100) != 0);

  return(-1);	/* We should never get here, invalid RAM number */
}

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/
/******************************************************************************/


static int DumpEventFifo(ApsErStruct *pParm)
{
  unsigned short        j;
  unsigned short        k;
  volatile ApsErStruct  *pEr = pParm;

  while (pEr->Control & 0x0002)
  {
    j = pEr->EventFifo;
    k = pEr->EventTimeHi;
    printf("FIFO event: %4.4X%4.4X\n", k, j);
  }
  return(0);
}

static void MainMenu(void)
{
  printf("r - Reset everything on the event receiver\n");
  printf("f - drain the event FIFO\n");
  printf("1 - dump event RAM 1\n");
  printf("2 - dump event RAM 2\n");
  printf("z - Dump event receiver regs\n");
  printf("q - quit\n");
}
int ER(void)
{
  static int	Card = 0;
  char 		buf[100];

  printf("Event receiver testerizer\n");
  printf("Enter Card number [%d]:", Card);
  fgets(buf, sizeof(buf), stdin);
  sscanf(buf, "%d", &Card);

  if (ErHaveReceiver(Card) < 0)
  {
    printf("Invalid card number specified\n");
    return(-1);
  }
  printf("Card address: %p\n", ErLink[Card].pEr);

  while(1)
  {
    MainMenu();

    if (fgets(buf, sizeof(buf), stdin) == NULL)
      break;

    switch (buf[0])
    {
    case 'r': ErResetAll(ErLink[Card].pEr); break;
    case 'd': ErEnableRam(ErLink[Card].pEr, 0); break;
    case 'f': DumpEventFifo(ErLink[Card].pEr); break;
    case '1': ErDumpRam(ErLink[Card].pEr, 1); break;
    case '2': ErDumpRam(ErLink[Card].pEr, 2); break;
    case 'z': ErDumpRegs(ErLink[Card].pEr); break;
    case 'q': return(0);
    }
  }
  return(0);
}
