/* share/src/drv/drvDvx.c
 * base/src/drv $Id$
 *
 * subroutines which are used to interface with the analogic 2502
 * A/D scanner cards
 *
 * Author:      Matthew Stettler
 * Date:        5-23-90
 *
 * AT-8 hardware design
 *
 * Modules:
 *
 * dvx_driver_init      Finds and initializes all 2502 cards
 * dvx_dma_init         Initializes Am9516 DMA controller
 * dvx_driver           Reads data from 2502
 * dvx_int              Interrupt service routine
 *
 * Test Routines:
 *
 * dvx_dump             dumps RAM buffer
 * dvx_dread		command line interface to dvx_driver
 * dvx_fempty		clears fifo from command line
 * dvx_dma_stat		displays DMA channel status
 *
 *	Experimental Physics and Industrial Control System (EPICS)
 *
 *	Copyright 1991, the Regents of the University of California,
 *	and the University of Chicago Board of Governors.
 *
 *	This software was produced under  U.S. Government contracts:
 *	(W-7405-ENG-36) at the Los Alamos National Laboratory,
 *	and (W-31-109-ENG-38) at Argonne National Laboratory.
 *
 *	Initial development by:
 *		The Controls and Automation Group (AT-8)
 *		Ground Test Accelerator
 *		Accelerator Technology Division
 *		Los Alamos National Laboratory
 *
 *	Co-developed with
 *		The Controls and Computing Group
 *		Accelerator Systems Division
 *		Advanced Photon Source
 *		Argonne National Laboratory
 *
 * Modification Log:
 * -----------------
 * MS 6/22/90	Modifications to aid in debugging interface to Mizar timing
 *		system. Interrupt service routine now counts words when clearing
 *		fifo, dvx_dump provides fifo status, and dvx_fempty added to
 *		clear fifo from command line.
 *
 * MS 7/9/90	Modifications to speed up interrupt service routine.
 *
 * MS 7/23/90	Added DMA control logic.
 *
 * MS 8/20/90	Added support for interrupt scanned records.
 *
 * MS 9/20/90	Changed data conversion to offset binary 
 *			(only test routines affected)
 *
 * JH 07/25/91	added dvx_reset() and dvx_reset() on control X reboot
 *
 * JH 11/14/91	changed a sysBCLSet to sysIntEnable so ioc_core
 *		will load into the nat inst cpu030
 *
 * JH 11/14/91	removed sysBCLSet enabling STD non priv and super
 *		access since this is already enabled if we are
 *		processor 0
 * JH 11/14/91 	changed DVX_INTLEV from a mask to a level
 *		to support use of sysIntEnable() which
 *		is architecture independent
 * BG 4/22/92   added sysBusToLocalAddr() for both short and standard
 *              addresses for this module.  Moved DVX_ADDR0 to  
 *              ai_addrs[DVX2502]in module_types.h.  Also moved DVX_IVECO
 *              to module_types.h.
 * BG 6/23/92   combined dvx_driver.c and drvDvx.c 
 * BG 06/26/92   added level to dvx_io_report in drvDvx structure.
 * JH 06/29/92	moved the rest of the dvx io_report here
 * BG 7/2/92	removed the semaphores from dvx_io_report
 * JH 08/03/92 	Removed hkv2f dependent base addr
 * JH 08/03/92 	moved interrupt vector base to module_types.h	
 * JH 08/05/92	dvx driver init is called from drvDvx now	
 * JH 08/10/92	merged dvx private include file into this source
 * JH 09/09/92	ran through UNIX C beautifier and converted to ANSI C
 * JH 09/09/92 	check to see if A24 is open prior to mapping
 *		the seq ram.
 * JH 09/14/92	Made taskDelays() CPU tick rate independent 
 *		tick rate
 * JH 09/15/92	made interrupt vector CPU independent
 * MRK 09/16/92 Added support for new I/O event scanning
 * JH 09/16/92 	dont write wordcnt every time the fifo is unloaded	
 * JH 09/16/92 	Use sysLocalToBusAdrs() to translate from an internal
 *		to a VME A24 address in case the internal base for A24
 *		addressed local memory is not on a 16MB boundary.
 *		sysLocalToBusAdrs() is called for each malloc'ed
 *		pointer used by the DVX to verify that each one is
 *		within the portion of local memory mapped to A24.
 * JH 09/17/92  one more sysLocalToBusAdrs() address translation 
 * 		needed	
 * JRW 01/18/92 Replaced init code to allow user to select the interrupt
 *              level value and to select the ports that are to be read.
 * MGB 08/04/93 Removed V5/V4 and EPICS_V2 conditionals
 * FRL 11/17/93 Added gain parameter to dvx_program
 *
 *
 * NOTE (JRW 11-18-92):
 *  In a phone conversation with Analogic, Tech support said that when the start
 *  signal is de-asserted (when using external start/stop mode), the DMAC is
 *  told to start transferring more data.  This is in case the sampling 
 *  completes with less than 512 bytes in the fifo.
 *
 *  In another phone conversation with Analogic, tech support told me that when
 *  the start signal is de-asserted (when using external start/stop mode), the
 *  sequence program pointer is reset to 0 (zero).  This is fine and dandy, but
 *  it can cause the mux stage/pipeline to start improperly.  Thus causing the
 *  first data sample to be corrupt.
 *
 * BUGS:
 *  The driver should inspect the VXI make and model codes and use a data type
 *  for the DMA buffer that is appropriate.
 */ 

static char *SccsId = "$Id$";

#include	<vxWorks.h>
#include	<stdioLib.h>
#include	<vme.h>
#include	<dbDefs.h>
#include	<drvSup.h>
#include	<module_types.h>
#include	<iv.h>
#include	<dbScan.h>
#include	<devLib.h>

/* general constants */
#define DVX_ID		0xCFF5		/* analogic ID code */
#define MAX_DVX_CARDS	5		/* max # of 2502 cards per IOC */
#define MAX_PORTS	3		/* max # of 2601 cards per 2502 */
#define DVX_DRATE	0xFFEC		/* default scan rate of 184 KHz */
#define DVX_SRATE	0xF201		/* slow scan used for run away mode */
#define DVX_RAMSIZE	2048		/* sequence RAM size (words) */
#define DVX_NBUF	1		/* default # of input buffers */

/* modes of operation */
#define INIT_MODE	0		/* initialization mode */
#define RUN_MODE	1		/* aquisition mode */

/* self test constants */
#define TST_RATE	0x3ED		/* self test scan rate */
#define TST_THRESH	0xD00		/* mux card test threshold reg value */

/* csr command bits */
#define CSR_RESET	0x01		/* software reset */
#define CSR_M_START	0x10		/* internal scan start */
#define CSR_M_ETRIG	0x40		/* external trigger enable */
#define CSR_M_ESTART	0x20		/* external start enable */
#define CSR_M_SYSFINH	0x02		/* system fail inhibit */
#define CSR_M_A24	0x8000		/* enable sequence RAM */
#define CSR_M_INT	0x80		/* interrupt enable */
#define CSR_M_MXTST	0x3A		/* mux card test bits */

/* csr status bits */
#define S_NEPTY	0x02			/* fifo not empty when = 1 */

/* Sequence Program control codes */
#define GAIN_CHANNEL	0x80
#define ADVANCE_TRACK	0x40
#define ADVANCE_HOLD	0xC0
#define RESTART		0x00

/* analogic 2502 memory structure */
struct dvx_2502
{
	unsigned short dev_id;		/* device id code (CFF5) */
	unsigned short dev_type;	/* type code (B100) */
	unsigned short csr;		/* control and status register */
	unsigned short seq_offst;	/* sequence RAM offset register */
	unsigned short mem_attr;	/* memory attribute register */
	unsigned short samp_rate;	/* sample rate register */
	unsigned short dma_point;	/* DMA pointer register */
	unsigned short dma_data;	/* DMA data register */
	unsigned short thresh;		/* threshold register */
	unsigned short fifo;		/* input fifo */
	unsigned short end_pad[54];	/* pad to 64 byte boundary */
};

/* input buffer */
struct dvx_inbuf
{
	struct dvx_inbuf *link;		/* link to next buffer */
	int	wordcnt;		/* # of words read to clear fifo */
	short	*data;			/* data buffer */
};

/* analogic 2502 control structure */
struct dvx_rec
{
	struct	dvx_2502 *pdvx2502;	/* pointer to device registers */
	short	*sr_ptr;		/* pointer to sequence RAM */
	struct	dvx_inbuf *inbuf;	/* pointer to current buffer */
	short unsigned csr_shadow;	/* csr shadow register */
	short	mode;			/* operation mode (init or run) */
	int	int_vector;		/* interrupt vector */
	int	intcnt;			/* interrupt count # */
	int	cnum;			/* card number */

	int	dmaSize;		/* samples to read before IRQ */
	unsigned int numChan;		/* total number of ports to read */
	unsigned long	pgmMask[8];	/* ports to be read by seq-program */
	unsigned short	gain[8];	/* port gains */

	int	RearmMode;		/* zero if auto-rearm, else manual */

	IOSCANPVT *pioscanpvt;
};

/* dma chain table size */
#define DVX_CTBL	34		/* max size of chain table */

/* am9516 register select constants.
 * The DMA control registers are accessed through the dvx2502 registers
 * dma_point and dma_data. The constants below are the addresses which must
 * be loaded into the pointer register to access the named register through
 * the data register. All dual registers are commented as #2. To access channel
 * #1, OR the value M_CH1 with the channel #2 address. 
 */
#define DMA_MMR		0x38		/* master mode register */
#define DMA_CSR		0x2C		/* command/status register #2 */
#define DMA_CARAH	0x18		/* current address reg A high #2 */
#define DMA_CARAL	0x08		/* current address reg A low #2 */
#define DMA_CARBH	0x10		/* current address reg B high #2 */
#define DMA_CARBL	0x00		/* current address reg B low #2 */
#define DMA_BARAH	0x1C		/* base address reg A high #2 */
#define DMA_BARAL	0x0C		/* base address reg A low #2 */
#define DMA_BARBH	0x14		/* base address reg B high #2 */
#define DMA_BARBL	0x04		/* base address reg B low #2 */
#define DMA_COC		0x30		/* current operation count #2 */
#define DMA_BOC		0x34		/* base operation count #2 */
#define DMA_ISR		0x28		/* interrupt save register #2 */
#define DMA_IVR		0x58		/* interrupt vector register #2 */
#define DMA_CMRH	0x54		/* channel mode register #2 */
#define DMA_CMRL	0x50		/* channel mode register #2 */
#define DMA_CARH	0x24		/* chain address reg high #2 */
#define DMA_CARL	0x20		/* chain address reg low #2 */
#define M_CH1		0x2		/* mask for chan 1 reg addresses */

/* am9516 command constants 
 *   All dual commands are commented as #1. To command channel #2, OR the
 * valur M_CH2 with the channel #1 command. 
 */
#define MMR_ENABLE	0x0D		/* chip enable value */
#define CMR_RESET	0x0		/* reset all channels */
#define CMR_START	0xA0		/* start channel #1 */
#define CMR_SSR		0x42		/* set software request #1 */
#define CMR_CSR		0x40		/* clear software request #1 */
#define CMR_SHM		0x82		/* set hardware mask #1 */
#define CMR_CHM		0x80		/* clear hardware mask #1 */
#define CMR_SC		0x22		/* set CIE/IP #1 */
#define CMR_CC		0x20		/* clear CIE/IP #1 */
#define CMR_SFB		0x62		/* set flip bit #1 */
#define CMR_CFB		0x60		/* clear flip bit #1 */
#define M_CIE		0x10		/* int enable bit mask (SC/CC cmd) */
#define M_IP		0x4		/* int pending bit mask (SC/CC cmd) */
#define M_CH2		0x1		/* mask for channel #2 commands */

/* am9516 chain reload constants */
#define R_CAR		0x1		/* chain address */
#define R_CMR		0x2		/* channel mode */
#define R_IVR		0x4		/* interrupt vector */
#define R_PMR		0x8		/* pattern and mask */
#define R_BOC		0x10		/* base operation count */
#define R_BAB		0x20		/* base address register B */
#define R_BAA		0x40		/* base address register A */
#define R_COC		0x80		/* current operation count */
#define R_CAB		0x100		/* current address register B */
#define R_CAA		0x200		/* current address register A */

/* If any of the following does not exist replace it with #define <> NULL */
long 	dvx_io_report(int level);
static long 	dvx_driver_init(void);

struct {
	long	number;
	DRVSUPFUN	report;
	DRVSUPFUN	init;
} drvDvx={
	2,
	dvx_io_report,
	dvx_driver_init};

static struct dvx_rec dvx[MAX_DVX_CARDS] = {
{ NULL, NULL, NULL, -1, -1, -1, -1, -1, 128, 0, {0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff}
,{0, 0, 0, 0, 0, 0, 0, 0},
0 , NULL
},
{ NULL, NULL, NULL, -1, -1, -1, -1, -1, 128, 0, {0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff}
,{0, 0, 0, 0, 0, 0, 0, 0},
0, NULL
},
{ NULL, NULL, NULL, -1, -1, -1, -1, -1, 128, 0, {0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff}
,{0, 0, 0, 0, 0, 0, 0, 0},
0, NULL
},
{ NULL, NULL, NULL, -1, -1, -1, -1, -1, 128, 0, {0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff}
,{0, 0, 0, 0, 0, 0, 0, 0},
0, NULL
}
};

static int DVX_INTLEV=5;		/* allow this to be user setable */
static int dvxOnline = 0;		/* 1 after init invoked */

#ifdef __STDC__
int	lclToA24(void *pLocal, void **ppA24);
static void	dvx_reset(void);
static void  	dvx_int(struct dvx_rec *dvxptr);
static int 	muxtst(int card);
static int 	sramld(int card);
static int 	dvx_driver( int	card, int chan, short *pval);
int	dvx_dread(int card,int chan);
int 	dvx_dump(int card,int firstchan,int lastchan);
int 	dvx_chan_print(int dvx_card, int firstchan, int lastchan);
static int 	dvx_fempty(int card);
static int	dvx_dma_init(struct dvx_rec *ptr);
static int	dvx_dma_reset(struct dvx_rec *ptr);
int 	dvx_dma_stat(int card, int chan);
#else /* __STDC__ */

int	lclToA24();
static void	dvx_reset();
static void  	dvx_int();
static int 	muxtst();
static int 	sramld();
int 	dvx_driver();
int	dvx_dread();
int 	dvx_dump();
int 	dvx_chan_print();
static int 	dvx_fempty();
static int	dvx_dma_init();
static int	dvx_dma_reset();
int 	dvx_dma_stat();


#endif /* __STDC__ */


int	dvxDebug = 0;


/*
 * dvx_int
 *
 * interrupt service routine
 *
 */
LOCAL void
dvx_int(struct dvx_rec *dvxptr)
{
	/* BUG --- why are there STATIC variables in here????? */

	static short i, junk;
	register struct dvx_2502 *cptr;
	static unsigned int tick, t, intlev;

	cptr = dvxptr->pdvx2502;
	cptr->dma_point = DMA_CSR;
	cptr->dma_data = CMR_CC | M_IP | M_CH2;	/* clear dma int channel #2 */
	cptr->csr = dvxptr->csr_shadow & 0xff5f;	/* disable fifo interrupts */
	switch (dvxptr->mode)
	{
	/*
  	 * interrupt recieved during initialization
  	 * - empty fifo and throw away data
  	 */
	case INIT_MODE:
		if(dvxDebug)
		  logMsg("dvx_int: INIT_MODE\n");
		dvxptr->intcnt = 0;			/* initialize interrupt count */
		for (i = 0; cptr->csr & 0x2; i++)
			junk = cptr->fifo;
		break;
	/*
  	 * interrupt recieved during data aquisition
  	 * - empty fifo into next input buffer, then make it current
    	 */
	case RUN_MODE:
		if(dvxDebug)
		  logMsg("dvx_int: RUN_MODE\n");
		dvxptr->intcnt++;			/* incriment interrupt count */

		dvxptr->inbuf = dvxptr->inbuf->link;	/* update current data buffer */

		for (i = 0; cptr->csr & S_NEPTY; i++, junk = cptr->fifo);

		dvxptr->inbuf->wordcnt = i;		/* record # of words to clear fifo */

		if (dvxptr->RearmMode == 0)
		{
		  	/* enable DMA opeations */
		  cptr->dma_point = DMA_CSR;

		  	/* enable int channel #2 */
		  cptr->dma_data = CMR_SC | M_CIE | M_CH2;

		  	/* start channel #2 */
		  cptr->dma_data = CMR_START | M_CH2;		
		}

		scanIoRequest(*(dvxptr->pioscanpvt));
		break;
	}
	cptr->csr = dvxptr->csr_shadow;
}

int dvx_RearmModeSet(int card, int mode)
{
  /* make sure hardware exists */
  if ((card >= ai_num_cards[DVX2502]) || (card < 0))	
    return(-1);
  
  dvx[card].RearmMode = mode;
  return(0);
}

int dvx_rearm(int card)
{
  struct dvx_rec *dvxptr;
  struct dvx_2502 *cptr;
  int i;
  short junk;

  /* make sure hardware exists */
  if ((card >= ai_num_cards[DVX2502]) || (card < 0))	
    return(-1);
  else if(dvx[card].pdvx2502 == NULL)
    return(-2);

  dvxptr = &dvx[card];
  cptr = dvxptr->pdvx2502;
 
  if (dvxptr->RearmMode == 0)
    return(-3);

#if 0
  cptr = dvxptr->pdvx2502;
  cptr->dma_point = DMA_CSR;
  cptr->dma_data = CMR_CC | M_IP | M_CH2;	/* clear dma int channel #2 */
  cptr->csr = dvxptr->csr_shadow & 0xff5f;	/* disable fifo interrupts */
#endif

  /* Drain the fifo of any crud */
  for (i = 0; cptr->csr & S_NEPTY; i++, junk = cptr->fifo);

  if (dvxDebug)
    printf("dvx_rearm(%d) fifo residual = %d\n", card, i);

    /* enable DMA opeations */
  cptr->dma_point = DMA_CSR;

    /* enable int channel #2 */
  cptr->dma_data = CMR_SC | M_CIE | M_CH2;

    /* start channel #2 */
  cptr->dma_data = CMR_START | M_CH2;

  return(0);
}


/*
 * dvx_driver_init
 *
 * initialization for 2502 cards
 *
 */
LOCAL long dvx_driver_init(void)
{
	int			i;
	int			j;
	int			status;
	unsigned short		card_id;
	short 			*ramptr;
	struct dvx_inbuf 	*ibptr;
	struct dvx_inbuf	*ibptra;
	int 			intvec = DVX_IVEC0;
	struct dvx_2502 	*pDvxA16;
	short 			*pDvxA24;
	short 			*pDvxA24Bus;

	/*
	 * dont continue DMA while vxWorks is control X
 	 * rebooting (and changing bus arbitration mode)
 	 *   joh 072591
 	 */
	rebootHookAdd(dvx_reset);

	dvxOnline = 1;	/* do not allow any more user config modifications */

	status = sysBusToLocalAdrs(
			VME_AM_SUP_SHORT_IO,
			ai_addrs[DVX2502],
			&pDvxA16);
	if (status != OK){
		logMsg(	"%s: A16 base addr problems DVX 2502\n",
			__FILE__);
		return ERROR;
	}


	/*
	 * search for DVX cards
	 */
	for (	i = 0, pDvxA24Bus = (short *)ai_memaddrs[DVX2502]; 
		i < ai_num_cards[DVX2502]; 
		i++, pDvxA16++, pDvxA24Bus += DVX_RAMSIZE)	
	{
#		ifdef DEBUG
			logMsg("Probing for DVX at %x\n", pDvxA16);
#		endif

		dvx[i].pdvx2502 = NULL;
		status = vxMemProbe (
				&pDvxA16->dev_id,
				READ,
				sizeof(card_id),
				&card_id);
		if (status <0){
			continue;
		}
		if (card_id != DVX_ID){	/* see if detected card is a 2502 */
			logMsg("%s: Card installed at addr=0X%x is not a dvx2502\n", 
				__FILE__,
				pDvxA16);
			continue;
		}

		/* Card found!  Finish the init for it. */

		dvx[i].cnum = i;		/* record card number */
		pDvxA16->csr = CSR_RESET;	/* software reset */

		status = sysBusToLocalAdrs(
				VME_AM_STD_SUP_DATA,
				pDvxA24Bus,
				&pDvxA24);
		if (status != OK){
			logMsg(	"%s: A24 base addr problems DVX 2502 A24=%x\n",
				__FILE__,
				pDvxA24Bus);
			continue;
		}

		/*
		 * check for incorrectly addressed card in A24 
		 */
		status = vxMemProbe (pDvxA24, READ, sizeof(card_id), &card_id);
		if (status == OK){
			logMsg(	"%s: A24 responding where DVX should be addr=%x\n",
				__FILE__,
				pDvxA24);
			logMsg(	"%s: DVX card=%d ignored\n", 
				__FILE__,
				i);
			continue;
		}

		
		if ((dvx[i].pioscanpvt = (IOSCANPVT *) malloc(sizeof(IOSCANPVT))) == NULL)
			return(-1);
		if (dvx[i].dmaSize == 0)
		{
		  logMsg("%s: No channels selected on card %d, init aborted\n", i);
		  continue;
		}

		dvx[i].mode = INIT_MODE;	/* initialization mode */
		/* create linked list of input buffers */
		for (	j = 0, ibptra = NULL; 
			j < DVX_NBUF; 
			j++, ibptra = ibptr)
		{

			ibptr = (struct dvx_inbuf *)malloc(sizeof (struct dvx_inbuf));
			/*
			 * exit with error if buffer allocation fails
			 */
			if (ibptr == NULL)
				return -1;	/* unsuccessfull */

			/* Needs to come from A24 mappable memory...? */
#if 0
			if ((ibptr->data = (short *) malloc(dvx[i].dmaSize * sizeof(short))) == NULL)
#else
			if ((ibptr->data = (short *) devLibA24Malloc(dvx[i].dmaSize * sizeof(short))) == NULL)
#endif
				return(-1);

			if (dvxDebug)
			  printf("dvx_driver_init(%d) allocated ibp at %p buffer at %p\n", i, ibptr, ibptr->data);
			if (j == 0){
				dvx[i].inbuf = ibptr;		/* initialize if first */
			}

			ibptr->wordcnt = 0;
			ibptr->link = ibptra;		/* LINK TO last buffer */
		}
		dvx[i].inbuf->link = ibptr;	/* close list */

		/*
		 * locate sequence RAM in an unassigned portion of VME A24
		 *
		 * Use the A24 bus address since the processor may not have placed
		 * A24 on a 16 MB boundary
		 */
		pDvxA16->seq_offst = (int)pDvxA24Bus>>8;
		dvx[i].csr_shadow = CSR_M_A24;		/* enable sequence RAM (shadow csr) */
		pDvxA16->csr = dvx[i].csr_shadow;	/* enable sequence RAM */

		/*
      		 * record card address and allocate input buffers
     		 */
		dvx[i].pdvx2502 = pDvxA16;	/* record card address */

		/*
	         *  locate and enable sequence RAM
    		 */
		dvx[i].sr_ptr = pDvxA24;		/* record seq RAM address */

		/*
		 *      set up interrupt handler 
     		 */
		dvx[i].csr_shadow |= (intvec<<8);	/* load int vector (shadow csr) */
		pDvxA16->csr = dvx[i].csr_shadow;	/* load int vector */
		dvx[i].int_vector = intvec;		/* save interrupt vector # */
		status = intConnect(INUM_TO_IVEC(intvec),dvx_int,&dvx[i]);
		if (status != OK)
			return -2;			/* abort if can't connect */
		sysIntEnable(DVX_INTLEV);		/* enable interrupt level */

		/* make sure the DMA chip is fully disabled */
		dvx_dma_reset(dvx[i].pdvx2502);

		dvx[i].csr_shadow |= CSR_M_INT;    	/* enable fifo int (shadow csr) */
		pDvxA16->csr = dvx[i].csr_shadow;	/* enable fifo interrupts */
		intvec++;				/* advance to next vector # */

		/*
	         * test mux cards and load sequence RAM
	         */
		muxtst(i);				/* test mux cards */
		sramld(i);				/* load scan program */

		dvx[i].csr_shadow ^= CSR_M_INT;		/* disable fifo int (shadow csr) */
		pDvxA16->csr = dvx[i].csr_shadow;	/* disable fifo interrupts */
		/*
	         * initialize DMA
	         */
		dvx_dma_init(&dvx[i]);			/* initialize DMA controller */
		/* 
	         * set scan rate and enable external start 
      		 */
		pDvxA16->samp_rate = DVX_DRATE;		/* scan rate of 184 KHz */
		dvx[i].csr_shadow |= CSR_M_ESTART;	/* enable ext start (shadow csr) */
		pDvxA16->csr = dvx[i].csr_shadow;	/* enable external start */
		dvx[i].mode = RUN_MODE;			/* ready to aquire data */
		scanIoInit(dvx[i].pioscanpvt);
	}

	return 0;	/* return 0 to database */
}


/*
 * muxtst
 *
 * test multiplexer cards 
 * I suspect this test does nothing more than light the pass LED on all
 * the 2601 multiplexer cards.
 *
 */
LOCAL int muxtst(int card)
{
	int i;
	short *ramptr;

	/* 
	 * inhibit sys fail and load test setup parameters
    	 */
	dvx[card].pdvx2502->csr = dvx[card].csr_shadow | CSR_M_SYSFINH;
	ramptr = dvx[card].sr_ptr;		/* pointer to sequence RAM */
	dvx[card].pdvx2502->thresh = TST_THRESH;	/* load test threshold */
	dvx[card].pdvx2502->samp_rate = TST_RATE;	/* test sample rate */
	/*
	 * load test program into sequence RAM
	 */
	for (i = 0; i < MAX_PORTS; i++)
	{
		*ramptr++ = GAIN_CHANNEL;		/* first sequence RAM value */
		*ramptr++ = ADVANCE_HOLD | i;		/* mux card select */
	}
	*ramptr++ = RESTART;			/* end of scan */
	*ramptr = RESTART;
	/*
 	 * run test and restore csr
 	 */
	dvx[card].pdvx2502->csr = dvx[card].csr_shadow | CSR_M_MXTST;
	taskDelay(sysClkRateGet());		/* let test run */
	dvx[card].pdvx2502->csr = dvx[card].csr_shadow;
	dvx[card].pdvx2502->thresh = 0;	/* restore threshold */
}

/*
 * dvx_program() is used to define what ports on what boards to scan
 * when taking a sample.  Each time a start pulse is supplied to the DVX
 * board, it will read from the ports in the specified by the user.  When 
 * dmaSize samples have been taken (possibly after many start pulses) the DMA 
 * controller will terminate transferring data and generate a completion event.
 * After this event, the whole process starts over again with the next start 
 * pulse.
 *
 * The dvx_program() parms are simply a bit mask of what ports to read from
 * each board.  There may be up to 8 boards on one DVX master.  The bit masks
 * are 32 bit unsigned numbers that should be assigned during the startup
 * script when booting the IOC.
 *
 * To program a DVX card (card 0) to read all ports from 2 S/H muxes (boards 2 
 * and 6) and 1 mux (board 1).  You may do the following.
 *
 * dvx_program(0, 2, 0x0000ffff, 0, 0)	 -- 16 ports from board 2  gain = 1
 * dvx_program(0, 6, 0x0000ffff, 0, 2)	 -- 16 ports from board 6  gain = 4
 * dvx_program(0, 1, 0xffffffff, 64, 3)	 -- 32 ports from board 1  gain = 8
 *
 * The 64 on the last line specified that we want to read 64 samples before the
 * DMA is considered complete.  The last dvx_program() value for the dma size 
 * is the only one that is used, all previous are discarded.
 *
 * If so desired, the above example could have used 128 for the DMA size value.
 * in this case, the dvx 'system' would not consider the sample complete until
 * all the ports were read twice.
 *
 * NOTE: Each card has its own notion of DMA size.  Each card also has its
 *       own notion of default values.  If no programming is done for a 
 *       specific card number, its default values will be used.
 *
 * The ports are read from the lowest board number and lowest port number first.
 */
int
dvx_program(int card, int board, unsigned long mask, int dmaSize, int gain)
{
  int	i;
  unsigned long maskCheck;
  int	numSamp;
  static int firstTime = 1;

  if (dvxOnline)
  {
    printf("DVX cards are already on line, no modifications allowed\n");
    return(-1);
  }
  if ((card < 0) || (card > ai_num_cards[DVX2502]))
  {
    printf("dvx_program(%d, %d, 0x%08.8X): invalid card number specified\n", card, board, mask);
    return(1);
  }
  if ((board < 0) || (board > 7))
  {
    printf("dvx_program(%d, %d, 0x%08.8X): invalid board number specified\n", card, board, mask);
    return(2);
  }
  if ((gain < 0) || (gain > 3))
  {
    errPrintf(-1, __FILE__, __LINE__, 
	 "dvx_program(%d, %d, 0x%08.8X, %d, %d): invalid gain specified\n", card, board,
				 mask, dmaSize, gain);
    return(2);
  }
  if (firstTime)
  { /* Clear out the default port numbers, this is the first dvx_program call */
    int i;

    firstTime=0;
    for (i=0; i<8; i++)
      dvx[card].pgmMask[i] = 0;
  }

  dvx[card].pgmMask[board] = mask;
  dvx[card].dmaSize = dmaSize;
  dvx[card].gain[board] = gain; 

  return(0);
}

/*
 * Allow the user to specify the interrupt level number
 *
 * It returns the 'old' IRQ level value.
 *
 * NOTE that off the shelf DVX2502s are set to use IRQ level 1
 */

int dvx_setIrqLevel(int level)
{
  int i;
 
  if (dvxOnline)
  {
    printf("DVX card(s) already initialized at level %d, new IRQ level ignored\n", DVX_INTLEV);
    return(DVX_INTLEV);
  }
  i = DVX_INTLEV;
  DVX_INTLEV = level;
  return(i);
}

/*
 * This can be called by a user program to get information about how
 * the dvx card is programmed.
 */
int dvx_getProgram(int card, int *dmaSize, int *numChan)
{
  *dmaSize = dvx[card].dmaSize;
  *numChan = dvx[card].numChan;

  if (dvxDebug)
    printf("total DMA samples=%d, total number of physical channels=%d\n", *dmaSize, *numChan);

  return(-1);
}

/*
 * This version of the sequence program loader allows the number of channels
 * to be programmable.  It is assumed that the programmable constants will
 * be fetched from the user.
 *
 */
LOCAL
int sramld(int card)
{
  short *ramptr;
  int i, port, firstPort;
  unsigned long mask;

  /* load sequence program */
  ramptr = dvx[card].sr_ptr;		/* point to sequence RAM */
  dvx[card].numChan = 0;

  for (i=0; i<8; i++)
  {
    mask = 1;
    port = 0;
    firstPort = -1;

    while(port < 32)
    {
      if (mask & dvx[card].pgmMask[i])
      { /* I need to read a sample from this port */
	if (firstPort == -1)
	{ /* save this one for prescan */
	  firstPort = port;
	}
	else
	{
	  *ramptr++ = GAIN_CHANNEL | (dvx[card].gain[i] <<3)  | ((port >> 3) & 3);
	  *ramptr++ = ADVANCE_HOLD | ((port & 0x07) << 3) | i;
	  dvx[card].numChan++;
	  if (dvxDebug)
            printf("board %d, port %d\n", i, port);
	}
      }
      mask <<= 1;
      port++;
    }
    if (firstPort != -1)
    { /* Put the first port number to read on each board, last in scan list. */
      *ramptr++ = GAIN_CHANNEL | (dvx[card].gain[i] << 3) | ((firstPort >> 3) & 3);
      *ramptr++ = ADVANCE_HOLD | ((firstPort & 0x07) << 3) | i;
      dvx[card].numChan++;
      if (dvxDebug)
        printf("board %d, port %d\n", i, firstPort);
    }
  }
  if (dvxDebug)
    printf("Total channels read in %d\n", dvx[card].numChan);

#if 0 /* Force the user to reference board 7 on their own */
  /* Make the extender system reset by referencing board 7 (No sample taken) */
  *ramptr++ = GAIN_CHANNEL | ((0xF0 >> 3) & 3);
  *ramptr++ = (((0xF0 & 7) << 3) | ((0xF0 & 0xE0) >> 5));
#endif

  *ramptr++ = 0;	/* mark the end of the sequence program */
  *ramptr++ = 0;

  /* set scan rate and run it once */
  dvx[card].pdvx2502->samp_rate = DVX_DRATE;
  dvx[card].pdvx2502->csr = dvx[card].csr_shadow | CSR_M_START;
  taskDelay(sysClkRateGet());     /* let scan run */
  dvx[card].pdvx2502->csr = dvx[card].csr_shadow; /* restore csr */
}

/*
 * dvx_driver
 *
 * interface to analog input buffer
 *
 */
int dvx_driver(
int		card,
int		chan,
short		*pval
)
{
	short ival;

	if ((card >= ai_num_cards[DVX2502]) || (card < 0))	/* make sure hardware exists */
		return -1;
	else if (dvx[card].pdvx2502 == NULL)
		return -2;
	else if (chan > dvx[card].dmaSize)
		return -2;

	*pval = dvx[card].inbuf->data[chan];
	return 0;
}

/*
 * dvxReadWf
 *
 * Allows a waveform record to read all samples from the dvx buffer as a
 * waveform.
 */
int dvxReadWf(int card, int start, int num, short *pwf, unsigned long *numRead)
{
  int	dataIndex;
  
  if(dvxDebug)
    printf("dvxReadWf(%d, %d, %d, 0x%08.8X, 0x%08.8X)\n", card, start, num, pwf, numRead);

  *numRead = 0;			/* in case we have an error condition */

  /* make sure hardware exists */
  if ((card >= ai_num_cards[DVX2502]) || (card < 0))	
    return(-1);
  else if ((dvx[card].pdvx2502 == NULL)||(start > dvx[card].dmaSize)||
	   (start < 0)||(num < 1))
    return(-2);

  /* if user asked for too many, chop off the length to that available */
  if (start+num > dvx[card].dmaSize)
    num -= (start+num) - dvx[card].dmaSize;

  dataIndex = start+num;
  *numRead = num;

  if (dvxDebug)
    printf("dvxReadWf(): Actual elements read: %d\n", num);

  while(num)
  {
    num--;
    dataIndex--;
    pwf[num] = dvx[card].inbuf->data[dataIndex];
  }
  return(0);
}


/*
 * dvx_dread
 *
 * stand alone interface to dvx_driver
 *
 */
int	dvx_dread(int card,int chan)
{
	short stat;
	short unsigned data;
	float volts;

	stat = dvx_driver(card,chan,(short *)&data);
	volts = data * 10./32767. - 10;
	printf("channel # %d\tdata = %x\tvolts = %f\n"
	    ,chan,data,volts);
}


/*
 * dvx_dump
 *
 * dump RAM buffer
 *
 */
int dvx_dump(int card,int firstchan,int lastchan)
{
	int i, port, ix, printing, tmp;
	short unsigned data;
	unsigned long mask;
	float volts;

	printf("Entering dvx_dump with card = %d,firstchan = %d,lastchan = %d\n",card,firstchan,
	    lastchan);
	if ((card >= ai_num_cards[DVX2502]) || (card < 0))
		return -1;
	else if (dvx[card].pdvx2502 == 0)
		return -2;
	printf("buffer address = %x word count = %d interrupt count = %d\n",
	    dvx[card].inbuf,dvx[card].inbuf->wordcnt,dvx[card].intcnt);
	if (dvx[card].pdvx2502->csr & 0x2)
		printf("fifo status = not empty,");
	else
		printf("fifo status = empty,");
	printf(" current input channel = %x\n",
	    (dvx[card].pdvx2502->csr & 0x3fc0)>>6);

  ix = 0;
  printing= 1;
  while ((ix < dvx[card].dmaSize) && (ix < lastchan) && printing)
  {
    printing = 0;
    for (i=0; i<8; i++)
    {
      mask = 1;
      port = 0;
      while(port < 32)
      {
        if (mask & dvx[card].pgmMask[i])
        { 
	  if (ix >= firstchan)
	  {
	    tmp = dvx[card].inbuf->data[ix];
	    tmp &= 0x0000ffff;

	    volts = tmp * 10./32767.;
            printf("signal %2d, board %d, port %2d, data 0x%04.4X, voltage %f\n", ix, i, port, tmp, volts);
	  }

          ix++;
	  printing = 1;
        }
        mask <<= 1;
        port++;
      }
    }
  }

  printf("end of list\n");

  return 0;
}

dvx_getioscanpvt(int card, IOSCANPVT *scanpvt)
{
	if ((card >= ai_num_cards[DVX2502]) || (card < 0))return(0);
	if (dvx[card].pdvx2502 == 0) return(0);
	*scanpvt = *(dvx[card].pioscanpvt);
	return(0);
}

/*
 *
 *   dvx_io_report
 *
 *
*/
long dvx_io_report(int level)
{
	short int i;
	unsigned short card_id;

	for (i = 0; i < ai_num_cards[DVX2502]; i++){
		if (!dvx[i].pdvx2502)
			continue;

		/* If detected card is a 2502 print out its number.  */
		printf("AI: DVX2505:\tcard %d\n",i);

		if(level > 0 ){
			int	firstchan;
			int	lastchan;
	
			printf("Enter number of the first channel you wish to read:\n");
			scanf("%d",&firstchan);
			printf("First channel is %d\n",firstchan);
			printf("Enter number of the last channel you wish to read:\n");
			scanf("%d",&lastchan);
			printf("Last channel is %d\n",lastchan);
			dvx_dump(i,firstchan,lastchan);
		}
	}

	return OK;
}


	
/*
 * dvx_fempty
 *
 * empty fifo
 *
 */
LOCAL int dvx_fempty(int card)
{
	int i, junk;

	if ((card>= ai_num_cards[DVX2502]) || (card < 0))
		return -1;
	else if (dvx[card].pdvx2502 == 0)
		return -2;
	for (i = 0; dvx[card].pdvx2502->csr & 0x2; i++)
		junk = dvx[card].pdvx2502->fifo;
	printf("%d words read from fifo\n",i);
	return 0;
}


LOCAL dvx_dma_reset(struct dvx_2502 *dev)
{
  dev->dma_point = DMA_CSR;
  dev->dma_data = CMR_RESET;	/* reset the thing */
  return(0);
}

/*
 * dvx_dma_init
 *
 * am9516 DMA controller initialization
 *
 * local to A24 bus addr conversions below are necessary on processors
 * that dont place the local base for A24 on an even 16 MB boundary
 */
LOCAL dvx_dma_init(struct dvx_rec *ptr)
{
	int i, j;
	int status;
	short *cptr, *cptra, *cptr0, *pext;
	short *BusPtr;
	struct dvx_2502 *dev;
	struct dvx_inbuf *bpnt;

	dev = ptr->pdvx2502;				/* point to hardware */

	dvx_dma_reset(dev);				/* reset the DMA chip */

	/* build chain table, needs to come from A24 mappable memory */
#if 0
	if ((cptr = cptr0 = (short *)malloc(DVX_CTBL)) == NULL)
#else
	if ((cptr = cptr0 = (short *)devLibA24Malloc(DVX_CTBL)) == NULL)
#endif
		return -1;
	dev->dma_point = DMA_MMR;		        /* enable chip */
	dev->dma_data = MMR_ENABLE;

	/*
	 * The 2502 uses A24 priv data VME addr mods
	 * for its DMAchain operations 
	 */
	status = sysLocalToBusAdrs(
			VME_AM_STD_SUP_DATA,
			cptr, 
			&BusPtr);
	if(status < 0){
		logMsg(	"%s:Local chain addr 0x%X does not map to A24 bus addr\n",
			__FILE__,
			cptr);
		return -1;
	}
	dev->dma_point = DMA_CARH;   			/* load init chain address */
	dev->dma_data = (int) BusPtr>>8 & 0xff00;
	dev->dma_point = DMA_CARL;
	dev->dma_data = (int) BusPtr & 0xffff;

	for (	i = 0, bpnt = ptr->inbuf->link; 
		i < DVX_NBUF;
	    	i++, cptr = cptra, bpnt = bpnt->link)
	{		/* create chain for each input buffer */
		if ((i + 1) == DVX_NBUF)
			cptra = cptr0;		/* close list */
		else
			/* needs to come from A24 mapped memory */
#if 0
			if ((cptra = (short *)malloc(DVX_CTBL)) == NULL)
#else
			if ((cptra = (short *)devLibA24Malloc(DVX_CTBL)) == NULL)
#endif
				return -1;	/* allocate next chain */

		/* Set the reload word */
		*cptr++ = R_CAA | R_CAB | R_COC | R_CMR | R_CAR; /* load mask */

		/*
		 *
		 * source options:
 		 *	1) data (not chain) operation
		 *	2) hold (dont auto incr or decr) the src address 
		 *
		 * The src addr here is ignored by the dvx2502 hardware
		 * and is therefore set to zero. The source is always
		 * the dvx 2502 fifo.
		 */
		*cptr++ = 0xd0;
		*cptr++ = 0;	/* address reg A (src) */

		/*
		 * The 2502 uses A24 non-priv data VME addr mods
		 * for its DMA data transfers
		 */
		status = sysLocalToBusAdrs(
				VME_AM_STD_USR_DATA,
				bpnt->data, 
				&BusPtr);
		if(status < 0){
			logMsg(	"%s: Local dest addr 0x%X does not map to A24 addr\n",
				__FILE__,
				bpnt->data);
			return -1;
		}
		*cptr++ = (((int)BusPtr>>8) & 0xff00) | 0xc0;
		*cptr++ = (int) BusPtr & 0xffff;	/* address reg B (dest) */

		*cptr++ = ptr->dmaSize;			/* operation count */
		*cptr++ = 0x4;
		*cptr++ = 0x0252;			/* dma mode control */

		/*
		 * The 2502 uses A24 priv data VME addr mods
		 * for its DMA chain operations 
		 */
		status = sysLocalToBusAdrs(
				VME_AM_STD_SUP_DATA,
				cptra, 
				&BusPtr);
		if(status < 0){
			logMsg(	"%s:Local addr 0x%X does not map to A24 addr\n",
				__FILE__,
				cptra);
			return -1;
		}
		*cptr++ = (int)BusPtr>>8 & 0xff00;
		*cptr = (int)BusPtr& 0xffff;		/* next chain address */
	}
	/* enable DMA opeations */
	dev->dma_point = DMA_CSR;
	dev->dma_data = CMR_SC | M_CIE | M_CH2;	/* enable int channel #2 */
	dev->dma_data = CMR_START | M_CH2;	/* start channel #2 */
	return 0;
}


/*
 * dvx_dma_stat
 *
 * reads status of dma channel
 *
 */
int 	dvx_dma_stat(int card, int chan)
{
	struct dvx_2502 *ptr;
	short unsigned temp;

	if ((card < 0) || (card > ai_num_cards[DVX2502]))
		return -1;
	else if (dvx[card].pdvx2502 == 0)
		return -2;
	else
	{
		ptr = dvx[card].pdvx2502;
		temp = (chan & 0x1)<<1;
		ptr->dma_point = DMA_CSR | temp;
		printf("dma status = %X\n",ptr->dma_data);
		ptr->dma_point = DMA_MMR;
		printf("master mode register = %X\n",ptr->dma_data);
		ptr->dma_point = DMA_CARH | temp;
		printf("chain address = %4.4X",ptr->dma_data);
		ptr->dma_point = DMA_CARL | temp;
		printf("%4.4X\n",ptr->dma_data);
		ptr->dma_point = DMA_CARAH | temp;
		printf("current address register A = %4.4X",ptr->dma_data);
		ptr->dma_point = DMA_CARAL | temp;
		printf("%4.4X\n",ptr->dma_data);
		ptr->dma_point = DMA_CARBH | temp;
		printf("current address register B = %4.4X",ptr->dma_data);
		ptr->dma_point = DMA_CARBL | temp;
		printf("%4.4X\n",ptr->dma_data);
		ptr->dma_point = DMA_BARAH | temp;
		printf("base address register A = %4.4X",ptr->dma_data);
		ptr->dma_point = DMA_BARAL | temp;
		printf("%4.4X\n",ptr->dma_data);
		ptr->dma_point = DMA_BARBH | temp;
		printf("base address register B = %4.4X",ptr->dma_data);
		ptr->dma_point = DMA_BARBL | temp;
		printf("%4.4X\n",ptr->dma_data);
		ptr->dma_point = DMA_COC | temp;
		printf("current operation count = %4.4X\n",ptr->dma_data);
		ptr->dma_point = DMA_BOC | temp;
		printf("base operation count = %4.4X\n",ptr->dma_data);
	}
	return 0;
}



/*
 *
 *	dvx_reset
 *	joh 072591
 *
 */
#if 0	/* This is hideous... should reset only those we started! */
LOCAL void
dvx_reset(void)
{
	struct dvx_2502 *pDvxA16;
	unsigned short 	card_id;
	int 		i;
	int 		status;
	int		card_found = FALSE;

	status = sysBusToLocalAdrs(
			VME_AM_SUP_SHORT_IO,
			ai_addrs[DVX2502],
			&pDvxA16);
	if (status != OK){
		logMsg(	"%s: A16 base addr problems DVX 2502\n",
			__FILE__);
		return;
	}

	/* 
	 * search for cards 
	 */
	for (i = 0; i < ai_num_cards[DVX2502]; i++, pDvxA16++){
		status = vxMemProbe (
				&pDvxA16->dev_id,
				READ,
				sizeof(card_id),
				&card_id);
		if (status != OK)
			continue;
		/* 
		 * see if detected card is a 2502 
		 * and reset if so
		 */
		if (card_id == DVX_ID){
			/* reset the DMA controller */
			dvx_dma_reset(pDvxA16);

			pDvxA16->csr = CSR_RESET;
			card_found = TRUE;
		}
	}

	/*
	 * wait long enough for the current DMA to end
	 *
	 *	1 sec
	 */
	if(card_found){
		printf("Waiting for DVX 2502 DMA to complete...");
		taskDelay(sysClkRateGet());
		printf("done\n");
	}
}
#else
LOCAL void
dvx_reset(void)
{
  int	i;
  int	CardFound = 0;

  /* 
   * search for cards 
   */
  for (i = 0; i < ai_num_cards[DVX2502]; i++)
  {
    if (dvx[i].pdvx2502 != NULL)
    {
      dvx_dma_reset(dvx[i].pdvx2502);
      dvx[i].pdvx2502->csr = CSR_RESET;
      CardFound = 1;
    }
  }
  /*
   * wait long enough for the current DMA to end
   *
   *	1 sec
   */
  if(CardFound)
  {
    printf("Waiting for DVX 2502 DMA to complete...");
    taskDelay(sysClkRateGet());
    printf("done\n");
  }
}
#endif
