From 90e8bd2c8b84d5002f2e881d2e29ebe8ac77ee13 Mon Sep 17 00:00:00 2001
From: David Schleef <ds@schleef.org>
Date: Thu, 10 May 2001 09:57:50 +0000
Subject: [PATCH] Added driver from Dan Christian <dac@ptolemy.arc.nasa.gov>

---
 comedi/drivers/rtd520.c | 1539 +++++++++++++++++++++++++++++++++++++++
 comedi/drivers/rtd520.h |  455 ++++++++++++
 2 files changed, 1994 insertions(+)
 create mode 100644 comedi/drivers/rtd520.c
 create mode 100644 comedi/drivers/rtd520.h

diff --git a/comedi/drivers/rtd520.c b/comedi/drivers/rtd520.c
new file mode 100644
index 00000000..befdd3a8
--- /dev/null
+++ b/comedi/drivers/rtd520.c
@@ -0,0 +1,1539 @@
+/*
+    comedi/drivers/rtd520.c
+    Comedi driver for Real Time Devices (RTD) PCI4520/DM7520
+    
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 2001 David A. Schleef <ds@schleef.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+    Created by Dan Christian, NASA Ames Research Center.
+
+    The PCI4520 is a PCI card.  The DM7520 is a PC/104-plus card.
+    Both have:
+    8/16 12 bit ADC with FIFO and channel gain table
+    8 bits high speed digital out (for external MUX) (or 8 in or 8 out)
+    8 bits high speed digital in with FIFO and interrupt on change (or 8 IO)
+    2 12 bit DACs with FIFOs
+    2 bits output
+    2 bits input
+    bus mastering DMA
+    timers: ADC sample, pacer, burst, about, delay, DA1, DA2
+    sample counter
+    3 user timer/counters
+    external interrupt
+
+    The DM7520 has slightly fewer features (fewer gain steps).
+
+    These boards can support external multiplexors and multi-board
+    synchronization, but this driver doesn't support that.
+
+    Board docs: http://www.rtdusa.com/dm7520.htm
+    Data sheet: http://www.rtdusa.com/pdf/dm7520.pdf
+    Example source: http://www.rtdusa.com/examples/dm/dm7520.zip
+    Call them and ask for the register level manual.
+    PCI chip: http://www.plxtech.com/products/toolbox/9080.htm
+
+    Notes:
+    This board is (almost) completely memory mapped.
+
+    I use a pretty loose naming style within the driver (rtd_blah).
+    All externally visible names should be rtd520_blah.
+    I use camelCase in and for structures.
+    I may also use upper CamelCase for function names.
+
+    This board somewhat related to the PCI4400 board.  
+
+    I borrowed heavily from the ni_mio_common, ni_atmio16d, and das1800,
+    since they have the best documented code.
+
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/comedidev.h>
+
+
+/*======================================================================
+  Board specific stuff
+======================================================================*/
+
+/* registers  */
+#define RTD_VENDOR_ID	0x1435
+/*
+  The board has three memory windows: las0, las1, and lcfg (the PCI chip)
+  Las1 has the data and can be burst DMAed 32bits at a time.
+*/
+#define LCFG_PCIINDEX	0
+/* PCI region 1 is a 256 byte IO space mapping.  Use??? */
+#define LAS0_PCIINDEX	2		/* PCI memory resources */
+#define LAS1_PCIINDEX	3
+#define LCFG_PCISIZE	0x100
+#define LAS0_PCISIZE	0x200
+#define LAS1_PCISIZE	0x10
+
+#define RTD_CLOCK_RATE	8000000		/* 8Mhz onboard clock */
+#define RTD_CLOCK_BASE	125		/* clock period in ns */
+
+#define RTD_MAX_SPEED	1600		/* in nanoseconds */
+#define RTD_MIN_SPEED	1000000000	/* in nanoseconds ??? */
+#define RTD_SETTLE_DELAY	1	/* in usec */
+#define RTD_ADC_TIMEOUT	1000		/* in usec */
+
+#include "rtd520.h"
+
+/*
+  Bit fields for one entry in the channel gain table
+*/
+typedef struct rtdChanGain_struct {
+    unsigned int    channel : 4;
+    unsigned int    gain    : 3;
+    unsigned int    nrse    : 1;	/* SE GND: 0=AGND, 1=AINSENSE */
+    unsigned int    range   : 2;	/* 0=+-5, 1=+-10, 2=+10 */
+    unsigned int    diff    : 1;	/* 0=SE, 1=differental */
+    unsigned int    pause   : 1;
+    unsigned int    dac1    : 1;
+    unsigned int    dac2    : 1;
+    unsigned int    skip    : 1;
+    unsigned int    reserved: 1;
+} rtdChanGain;
+
+/*======================================================================
+  Comedi specific stuff
+======================================================================*/
+
+/*
+  The board has 3 input modes and the gains of 1,2,4,...32 (, 64, 128)
+*/
+static comedi_lrange rtd_ai_7520_range = { 6, {
+					/* TODO: BIP_RANGE(10.0) */
+    BIP_RANGE(5.0),
+    BIP_RANGE(5.0/2),
+    BIP_RANGE(5.0/4),
+    BIP_RANGE(5.0/8),
+    BIP_RANGE(5.0/16),
+    BIP_RANGE(5.0/32),
+    /*UNI_RANGE(10.0),
+    UNI_RANGE(10.0/2),
+    UNI_RANGE(10.0/4),
+    UNI_RANGE(10.0/8),
+    UNI_RANGE(10.0/16),
+    UNI_RANGE(10.0/32),*/
+}};
+  /* PCI4520 has two more gains (6 more entries) */
+
+static comedi_lrange rtd_ao_range = { 4, {
+  RANGE(-10, 10),
+  RANGE(-5, 5),
+  RANGE(0, 10),
+  RANGE(0, 5),
+}};
+
+/*
+  Board descriptions
+ */
+typedef struct rtdBoard_struct{
+    char	*name;			/* must be first */
+    int		device_id;
+    int		aiChans;
+    int		aiBits;
+    int		aiMaxGain;
+    int		fifoLen;
+    int		haveDio;		/* is digital IO supported */
+} rtdBoard;
+
+rtdBoard rtd520Boards[] = {
+    {
+	name:		"DM7520",
+	device_id:	0x7520,
+	aiChans:	16,
+	aiBits:		12,
+	aiMaxGain:	32,
+	fifoLen:	1024,
+	haveDio:	0,
+    },
+    {
+	name:		"DM7520-8",
+	device_id:	0x7520,
+	aiChans:	16,
+	aiBits:		12,
+	aiMaxGain:	32,
+	fifoLen:	8192,
+	haveDio:	0,
+    },
+    {
+	name:		"PCI4520",
+	device_id:	0x4520,
+	aiChans:	16,
+	aiBits:		12,
+	aiMaxGain:	128,
+	fifoLen:	1024,
+	haveDio:	0,
+    },
+    {
+	name:		"PCI4520-8",
+	device_id:	0x4520,
+	aiChans:	16,
+	aiBits:		12,
+	aiMaxGain:	128,
+	fifoLen:	8192,
+	haveDio:	0,
+    },
+};
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((rtdBoard *)dev->board_ptr)
+
+/*
+   This structure is for data unique to this hardware driver.
+   This is also unique for each board in the system.
+*/
+typedef struct{
+					/* memory mapped board structures */
+    void	*las0;
+    void	*las1;
+    void	*lcfg;
+
+    unsigned long	intCount;	/* interrupt count */
+    unsigned long	aiCount;	/* total transfer size (samples) */
+    unsigned long	aiExtraInt;	/* ints but no data */
+    int			aboutWrap;
+
+					/* PCI device info */
+    struct pci_dev *pci_dev;
+
+					/* Used for AO readback */
+    lsampl_t ao_readback[2];
+
+    /* NEEDED???: these 3 arent used after init */
+    unsigned long physLas0;		/* configuation */
+    unsigned long physLas1;		/* data area */
+    unsigned long physLcfg;		/* PLX9080 */
+
+} rtdPrivate;
+
+/*
+ * most drivers define the following macro to make it easy to
+ * access the private structure.
+ */
+#define devpriv ((rtdPrivate *)dev->private)
+
+
+/* Macros to access registers */
+
+/* Reset board */
+#define RtdResetBoard(dev) \
+    writel (0, devpriv->las0+LAS0_BOARD_RESET)
+
+/* Reset channel gain table read pointer */
+#define RtdResetCGT(dev) \
+    writel (0, devpriv->las0+LAS0_CGT_RESET)
+
+/* Reset channel gain table read and write pointers */
+#define RtdClearCGT(dev) \
+    writel (0, devpriv->las0+LAS0_CGT_CLEAR)
+
+/* Reset channel gain table read and write pointers */
+#define RtdEnableCGT(dev,v) \
+    writel ((v > 0) ? 1 : 0, devpriv->las0+LAS0_CGT_ENABLE)
+
+/* Write channel gain table entry */
+#define RtdWriteCGTable(dev,v) \
+    writel (v, devpriv->las0+LAS0_CGT_WRITE)
+
+/* Write Channel Gain Latch */
+#define RtdWriteCGLatch(dev,v) \
+    writel (v, devpriv->las0+LAS0_CGL_WRITE)
+
+/* Reset ADC FIFO */
+#define RtdClearAdcFifo(dev) \
+    writel (0, devpriv->las0+LAS0_ADC_FIFO_CLEAR)
+
+/* Set ADC start conversion source select (write only) */
+#define RtdAdcConversionSource(dev,v) \
+    writel (v, devpriv->las0+LAS0_ADC_CONVERSION)
+
+/* Set burst start source select (write only) */
+#define RtdBurstStartSource(dev,v) \
+    writel (v, devpriv->las0+LAS0_BURST_START)
+
+/* Set Pacer start source select (write only) */
+#define RtdPacerStartSource(dev,v) \
+    writel (v, devpriv->las0+LAS0_PACER_START)
+
+/* Set Pacer stop source select (write only) */
+#define RtdPacerStopSource(dev,v) \
+    writel (v, devpriv->las0+LAS0_PACER_STOP)
+
+/* Set Pacer clock source select (write only) 0=external 1=internal */
+#define RtdPacerClockSource(dev,v) \
+    writel ((v > 0) ? 1 : 0, devpriv->las0+LAS0_PACER_SELECT)
+
+/* Set sample counter source select (write only) */
+#define RtdAdcSampleCounterSource(dev,v) \
+    writel (v, devpriv->las0+LAS0_ADC_SCNT_SRC)
+
+/* Set Pacer trigger mode select (write only) 0=single cycle, 1=repeat */
+#define RtdPacerTriggerMode(dev,v) \
+    writel ((v > 0) ? 1 : 0, devpriv->las0+LAS0_PACER_REPEAT)
+
+/* Set About counter stop enable (write only) */
+#define RtdAboutStopEnable(dev,v) \
+    writel ((v > 0) ? 1 : 0, devpriv->las0+LAS0_ACNT_STOP_ENABLE)
+
+/* Set external trigger polarity (write only) 0=positive edge, 1=negative */
+#define RtdTriggerPolarity(dev,v) \
+    writel ((v > 0) ? 1 : 0, devpriv->las0+LAS0_ETRG_POLARITY)
+
+/* Start single ADC conversion */
+#define RtdAdcStart(dev) \
+    writew (0, devpriv->las0+LAS0_ADC)
+
+/* Read one ADC data value (12bit+sign as 16bit) */
+/* Note: matches what DMA would get.  Actual value >> 3 */
+#define RtdAdcFifoGet(dev) \
+    readw (devpriv->las1+LAS1_ADC_FIFO)
+
+/* Read two ADC data values */
+#define RtdAdcFifoGet2(dev) \
+    readl (devpriv->las1+LAS1_ADC_FIFO)
+
+/* FIFO status */
+#define RtdFifoStatus(dev) \
+    readl (devpriv->las0+LAS0_ADC)
+
+/* pacer start/stop read=start, write=stop*/
+#define RtdPacerStart(dev) \
+    readl (devpriv->las0+LAS0_PACER)
+#define RtdPacerStop(dev) \
+    writel (0, devpriv->las0+LAS0_PACER)
+
+/* Interrupt status */
+#define RtdInterruptStatus(dev) \
+    readl (devpriv->las0+LAS0_IT)
+
+/* Interrupt mask */
+#define RtdInterruptMask(dev,v) \
+    writel (v,devpriv->las0+LAS0_IT)
+
+/* Interrupt status clear (only bits set in mask) */
+#define RtdInterruptClear(dev) \
+    readl (devpriv->las0+LAS0_CLEAR)
+
+/* Interrupt clear mask */
+#define RtdInterruptClearMask(dev,v) \
+    writel (v, devpriv->las0+LAS0_CLEAR)
+
+/* Interrupt overrun status */
+#define RtdInterruptOverrunStatus(dev) \
+    readl (devpriv->las0+LAS0_OVERRUN)
+
+/* Interrupt overrun clear */
+#define RtdInterruptOverrunClear(dev) \
+    writel (0, devpriv->las0+LAS0_OVERRUN)
+
+/* Pacer counter, 24bit */
+#define RtdPacerCount(dev) \
+    readl (devpriv->las0+LAS0_PCLK)
+#define RtdPacerCounter(dev,v) \
+    writel ((v) & 0xffffff,devpriv->las0+LAS0_PCLK)
+
+/* Burst counter, 10bit */
+#define RtdBurstCount(dev) \
+    readl (devpriv->las0+LAS0_BCLK)
+#define RtdBurstCounter(dev,v) \
+    writel ((v) & 0x3ff,devpriv->las0+LAS0_BCLK)
+
+/* Delay counter, 16bit */
+#define RtdDelayCount(dev) \
+    readl (devpriv->las0+LAS0_DCLK)
+#define RtdDelayCounter(dev,v) \
+    writel ((v) & 0xffff, devpriv->las0+LAS0_DCLK)
+
+/* About counter, 16bit */
+#define RtdAboutCount(dev) \
+    readl (devpriv->las0+LAS0_ACNT)
+#define RtdAboutCounter(dev,v) \
+    writel ((v) & 0xffff, devpriv->las0+LAS0_ACNT)
+
+/* ADC sample counter, 10bit */
+#define RtdAdcSampleCount(dev) \
+    readl (devpriv->las0+LAS0_ADC_SCNT)
+#define RtdAdcSampleCounter(dev,v) \
+    writel ((v) & 0x3ff, devpriv->las0+LAS0_ADC_SCNT)
+
+/* User timer/counter, 16bit (two step access, LSB,MSB) */
+/* UTC Status word must have the right counter and latching mode in it!!! */
+#define RtdUtcCount(dev,n) \
+    (readb (devpriv->las0 \
+        + ((n <= 0) ? LAS0_UTC0 : ((1 == n) ? LAS0_UTC1 : LAS0_UTC2))) \
+     | (readb (devpriv->las0 \
+        + ((n <= 0) ? LAS0_UTC0 : ((1 == n) ? LAS0_UTC1 : LAS0_UTC2))) << 8))
+
+#define RtdUtcCounter(dev,n,v) \
+    writeb ((v) & 0xff, devpriv->las0 \
+        + ((n <= 0) ? LAS0_UTC0 : ((1 == n) ? LAS0_UTC1 : LAS0_UTC2))), \
+    writeb (((v) >> 8) & 0xff, devpriv->las0 \
+        + ((n <= 0) ? LAS0_UTC0 : ((1 == n) ? LAS0_UTC1 : LAS0_UTC2)))
+
+/* Set UTC mode.  Forces proper latching mode and binary counting. */
+#define RtdUtcModeSet(dev,n,v) \
+    writeb (((n & 3) << 6) | (((v) & 7) < 1) | 0x30, \
+      devpriv->las0 + LAS0_UTC_CTRL)
+
+/* Get UTC mode. Forces proper latching mode and binary counting. */
+/* The docs say that you cant read mode, but dos driver implements it! */
+#define RtdUtcModeGet(dev,n) \
+    (writeb (((n & 3) << 1) | 0xE0, devpriv->las0 + LAS0_UTC_CTRL), \
+     readw (devpriv->las0 + LAS0_UTC_CTRL) & 0xff)
+
+/* Set UTCn clock source (write only) */
+#define RtdUtcClockSource(dev,n,v) \
+    writew (v, devpriv->las0 \
+        + ((n <= 0) ? LAS0_UTC0_CLOCK : \
+           ((1 == n) ? LAS0_UTC1_CLOCK : LAS0_UTC2_CLOCK)))
+
+/* Set UTCn gate source (write only) */
+#define RtdUtcGateSource(dev,n,v) \
+    writew (v, devpriv->las0 \
+        + ((n <= 0) ? LAS0_UTC0_GATE : \
+           ((1 == n) ? LAS0_UTC1_GATE : LAS0_UTC2_GATE)))
+
+/* User output N source select (write only) */
+#define RtdUsrOutSource(dev,n,v) \
+    writel (v,devpriv->las0+((n <= 0) ? LAS0_UOUT0_SELECT : LAS0_UOUT1_SELECT))
+
+/* PLX9080 interrupt mask and status */
+#define RtdPLXInterruptRead(dev) \
+    readl (devpriv->lcfg+LCFG_ITCSR)
+#define RtdPLXInterruptWrite(dev,v) \
+    writel (v, devpriv->lcfg+LCFG_ITCSR)
+
+
+/*
+ * The comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static int rtd_attach (comedi_device *dev, comedi_devconfig *it);
+static int rtd_detach (comedi_device *dev);
+
+comedi_driver rtd520Driver={
+	driver_name:	"rtd520",
+	module:		THIS_MODULE,
+	attach:		rtd_attach,
+	detach:		rtd_detach,
+
+	/* It is not necessary to implement the following members if you are
+	 * writing a driver for a ISA PnP or PCI card */
+	/* Most drivers will support multiple types of boards by
+	 * having an array of board structures.  These were defined
+	 * in rtd520Boards[] above.  Note that the element 'name'
+	 * was first in the structure -- Comedi uses this fact to
+	 * extract the name of the board without knowing any details
+	 * about the structure except for its length.
+	 * When a device is attached (by comedi_config), the name
+	 * of the device is given to Comedi, and Comedi tries to
+	 * match it by going through the list of board names.  If
+	 * there is a match, the address of the pointer is put
+	 * into dev->board_ptr and driver->attach() is called.
+	 *
+	 * Note that these are not necessary if you can determine
+	 * the type of board in software.  ISA PnP, PCI, and PCMCIA
+	 * devices are such boards.
+	 */
+	board_name:	rtd520Boards,
+	offset:		sizeof(rtdBoard),
+	num_names:	sizeof(rtd520Boards) / sizeof(rtdBoard),
+};
+
+static int rtd_ai_rinsn (comedi_device *dev, comedi_subdevice *s,
+			 comedi_insn *insn, lsampl_t *data);
+static int rtd_ao_winsn (comedi_device *dev, comedi_subdevice *s,
+			 comedi_insn *insn, lsampl_t *data);
+static int rtd_ao_rinsn (comedi_device *dev, comedi_subdevice *s,
+			 comedi_insn *insn, lsampl_t *data);
+static int rtd_dio_insn_bits (comedi_device *dev, comedi_subdevice *s,
+			      comedi_insn *insn, lsampl_t *data);
+static int rtd_dio_insn_config (comedi_device *dev, comedi_subdevice *s,
+				comedi_insn *insn, lsampl_t *data);
+static int rtd_ai_cmdtest (comedi_device *dev,comedi_subdevice *s,
+			   comedi_cmd *cmd);
+static int rtd_ai_cmd ( comedi_device *dev, comedi_subdevice *s);
+static int rtd_ai_cancel ( comedi_device *dev, comedi_subdevice *s);
+static int rtd_ns_to_timer (unsigned int *ns, int roundMode);
+static void rtd_interrupt ( int irq, void *d, struct pt_regs *regs);
+
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board.  If you specified a board_name array
+ * in the driver structure, dev->board_ptr contains that
+ * address.
+ */
+static int rtd_attach (
+    comedi_device *dev,
+    comedi_devconfig *it)		/* board name and options flags */
+{
+    comedi_subdevice *s;
+    struct pci_dev* pcidev;
+    int index;
+    int	ret;
+
+    printk ("comedi%d: rtd520 attaching.\n", dev->minor);
+
+    /*
+     * Allocate the private structure area.  alloc_private() is a
+     * convenient macro defined in comedidev.h.
+     */
+    if (alloc_private (dev, sizeof(rtdPrivate))<0)
+	return -ENOMEM;
+
+    /*
+     * Probe the device to determine what device in the series it is.
+     */
+    pci_for_each_dev (pcidev) {
+	if (pcidev->vendor == RTD_VENDOR_ID) {
+	    if (it->options[0] && it->options[1]) {
+		if (pcidev->bus->number == it->options[0]
+		    && PCI_SLOT(pcidev->devfn) == it->options[1]) {
+		    printk("rtd520: found bus=%d slot=%d\n",
+			   it->options[0], it->options[1]);
+		    break;		/* found it */
+		}
+	    } else {		/* specific board/slot not specified */
+		break;		/* found one */
+	    }
+	}
+    }
+
+    if (!pcidev) {
+	if (it->options[0] && it->options[1]) {
+	    printk ("No RTD card at bus=%d slot=%d.\n",
+		   it->options[0], it->options[1]);
+	} else {
+	    printk ("No RTD card found.\n");
+	}
+	return -EIO;
+    }
+
+    /* See if this is a model that we know about */
+    for (index=0; index < rtd520Driver.num_names; index++){
+	if (rtd520Boards[index].device_id == pcidev->device) {
+	    break;
+	}
+    }
+    if (index >= rtd520Driver.num_names) {
+	printk ("Found an RTD card, but not a supported type (%x).\n",
+	       pcidev->device);
+	return -EIO;
+    } else {
+	devpriv->pci_dev = pcidev;
+	dev->board_ptr = rtd520Boards+index;
+    }
+    /*
+     * Initialize dev->board_name.  Note that we can use the "thisboard"
+     * macro now, since we just initialized it in the last line.
+     */
+    dev->board_name = thisboard->name;
+
+    /*
+     * Initialize base addresses
+     */
+    /* Get the physical address from PCI config */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+    devpriv->physLas0 = devpriv->pci_dev->base_address[LAS0_PCIINDEX];
+    devpriv->physLas1 = devpriv->pci_dev->base_address[LAS1_PCIINDEX];
+    devpriv->physLcfg = devpriv->pci_dev->base_address[LCFG_PCIINDEX];
+#else
+    devpriv->physLas0 = devpriv->pci_dev->resource[LAS0_PCIINDEX].start;
+    devpriv->physLas1 = devpriv->pci_dev->resource[LAS1_PCIINDEX].start;
+    devpriv->physLcfg = devpriv->pci_dev->resource[LCFG_PCIINDEX].start;
+#endif
+    /* Now have the kernel map this into memory */
+					/* ASSUME page aligned */
+    devpriv->las0 = ioremap(devpriv->physLas0, LAS0_PCISIZE);
+    devpriv->las1 = ioremap(devpriv->physLas1, LAS1_PCISIZE);
+    devpriv->lcfg = ioremap(devpriv->physLcfg, LCFG_PCISIZE);
+
+    printk ("%s: ", dev->board_name);
+    /*printk ("%s: LAS0=%lx, LAS1=%lx, CFG=%lx.\n", dev->board_name,
+      devpriv->physLas0, devpriv->physLas1, devpriv->physLcfg);*/
+
+    /*
+     * Allocate the subdevice structures.  alloc_subdevice() is a
+     * convenient macro defined in comedidev.h.  It relies on
+     * n_subdevices being set correctly.
+     */
+    dev->n_subdevices=4;
+    if (alloc_subdevices(dev)<0)
+	return -ENOMEM;
+
+    s=dev->subdevices+0;
+    dev->read_subdev=s;
+    /* analog input subdevice */
+    s->type=COMEDI_SUBD_AI;
+    s->subdev_flags=SDF_READABLE;
+    s->n_chan=thisboard->aiChans;
+    s->maxdata=(1<<thisboard->aiBits)-1;
+    s->range_table = &rtd_ai_7520_range;
+    s->len_chanlist = thisboard->fifoLen;
+    s->insn_read = &rtd_ai_rinsn;
+    s->do_cmd = &rtd_ai_cmd;
+    s->do_cmdtest = &rtd_ai_cmdtest;
+    s->cancel = &rtd_ai_cancel;
+
+    s=dev->subdevices+1;
+    /* analog output subdevice */
+    s->type = COMEDI_SUBD_UNUSED;
+    /*s->type=COMEDI_SUBD_AO;*/
+    s->subdev_flags=SDF_WRITEABLE;
+    s->n_chan =2;
+    s->maxdata =(1<<thisboard->aiBits)-1;
+    s->range_table = &rtd_ao_range;
+    s->insn_write = &rtd_ao_winsn;
+    s->insn_read = &rtd_ao_rinsn;
+
+    s=dev->subdevices+2;
+    /* digital i/o subdevice */
+    if (thisboard->haveDio){
+	s->type=COMEDI_SUBD_DIO;
+	s->subdev_flags=SDF_READABLE|SDF_WRITEABLE;
+	s->n_chan=16;
+	s->maxdata=1;
+	s->range_table=&range_digital;
+	s->insn_bits = rtd_dio_insn_bits;
+	s->insn_config = rtd_dio_insn_config;
+    } else {
+	s->type = COMEDI_SUBD_UNUSED;
+    }
+
+    s=dev->subdevices+3;
+    s->n_chan=3;
+    /* 3 timer/counter subdevices */
+    s->type = COMEDI_SUBD_UNUSED;
+	
+    /* check if our interrupt is available and get it */
+    dev->irq = devpriv->pci_dev->irq;
+    if(dev->irq>0){
+	if((ret=comedi_request_irq (dev->irq, rtd_interrupt,
+				    0, "rtd520", dev))<0)
+	    return ret;
+	printk("( irq = %d )\n", dev->irq);
+    } else {
+	printk("( NO IRQ )");
+    }
+	
+					/* initialize board, per RTD spec */
+    RtdResetBoard (dev);
+    RtdInterruptMask (dev,0);
+    RtdInterruptClearMask (dev,~0);
+    RtdInterruptClear(dev);		/* clears bits set by mask */
+    RtdInterruptOverrunClear(dev);
+    RtdClearCGT (dev);
+    RtdClearAdcFifo (dev);
+					/* clear DA FIFO */
+					/* clear digital IO */
+    if (dev->irq) {			/* enable interrupt controller */
+	RtdPLXInterruptWrite (dev,
+			      RtdPLXInterruptRead (dev) | (0x0800));
+    }
+
+    printk("comedi%d: rtd520 driver attached.\n", dev->minor);
+
+    return 1;
+}
+
+/*
+ * _detach is called to deconfigure a device.  It should deallocate
+ * resources.  
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach().  dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int rtd_detach (
+    comedi_device *dev)
+{
+    printk("comedi%d: rtd520: removing (%ld ints, %ld extra ai)\n(int status 0x%x, overrun status 0x%x, fifo status 0x%x)...\n",
+	   dev->minor, devpriv->intCount, devpriv->aiExtraInt,
+	   0xffff & RtdInterruptStatus (dev),
+	   0xffff & RtdInterruptOverrunStatus (dev),
+	   0xffff & RtdFifoStatus (dev));
+    if (devpriv) {
+	/* Shut down any board ops by reseting it */
+	RtdResetBoard (dev);
+	RtdInterruptMask (dev, 0);
+	RtdInterruptClearMask (dev,~0);
+	RtdInterruptClear(dev);		/* clears bits set by mask */
+	RtdClearCGT (dev);
+	RtdClearAdcFifo (dev);
+
+	/* release DMA */
+
+	/* release IRQ */
+	if (dev->irq) {
+	    /* disable interrupt controller */
+	    RtdPLXInterruptWrite (dev,
+				  RtdPLXInterruptRead (dev) & ~(0x0800));
+	    comedi_free_irq (dev->irq, dev);
+	}
+
+	/* release all regions that were allocated */
+	if (devpriv->las0) {
+	    iounmap (devpriv->las0);
+	}
+	if (devpriv->las1) {
+	    iounmap (devpriv->las1);
+	}
+	if (devpriv->lcfg) {
+	    iounmap (devpriv->lcfg);
+	}
+    }
+
+    printk("comedi%d: rtd520: removed.\n",dev->minor);
+	
+    return 0;
+}
+
+/*
+  Convert a single comedi channel-gain entry to a RTD520 table entry
+*/
+static unsigned short rtdConvertChanGain (
+    unsigned int	comediChan)
+{
+    unsigned int chan, range, aref;
+    unsigned short r=0;
+
+    chan = CR_CHAN (comediChan);
+    range = CR_RANGE (comediChan);
+    aref = CR_AREF (comediChan);
+
+    r |= chan & 0xf;
+
+    /* TODO: Should also be able to switch into +-=10 range */
+    /* HACK!!! should not use a constant here */
+    if (range < 6) {		/* first 6 are bipolar */
+	r |= 0x000;			/* +-5 range */
+	r |= (range & 0x7) << 4;	/* gain */
+    } else {
+	r |= 0x200;			/* +10 range */
+	r |= ((range-6) & 0x7) << 4;	/* gain */
+    }
+
+    switch (aref) {
+    case AREF_GROUND:
+	break;
+
+    case AREF_COMMON:
+	r |= 0x80;			/* ref external analog common */
+	break;
+
+    case AREF_DIFF:
+	r |= 0x400;			/* DIFF */
+	break;
+
+    case AREF_OTHER:		/* ??? */
+	break;
+    }
+    printk ("chan=%d r=%d a=%d -> 0x%x\n",
+	    chan, range, aref, r);
+    return r;
+}
+
+/*
+  Setup the channel-gain table from a comedi list
+*/
+static void rtd_load_channelgain_list (
+    comedi_device *dev,
+    unsigned int n_chan,
+    unsigned int *list)
+{
+    if (n_chan > 1) {			/* setup channel gain table */
+	int	ii;
+	RtdClearCGT (dev);
+	RtdEnableCGT(dev, 1);		/* enable table */
+	for(ii=0; ii < n_chan; ii++){
+	    RtdWriteCGTable (dev, rtdConvertChanGain (list[ii]));
+	}
+    } else {				/* just use the channel gain latch */
+	RtdEnableCGT(dev, 0);		/* disable table, enable latch */
+	RtdWriteCGLatch (dev, rtdConvertChanGain (list[0]));
+    }
+}
+
+/*
+  "instructions" read/write data in "one-shot" or "software-triggered"
+  mode (simplest case).
+  This doesnt use interrupts.
+ */
+static int rtd_ai_rinsn (
+    comedi_device *dev,
+    comedi_subdevice *s,
+    comedi_insn *insn,
+    lsampl_t *data)
+{
+    int n, ii;
+    int	stat;
+					/* clear channel gain table */
+    /* write channel to multiplexer */
+    rtd_load_channelgain_list (dev, 1, &insn->chanspec);
+	
+					/* set conversion source */
+    RtdAdcConversionSource (dev, 0);	/* software */
+
+					/* wait for mux to settle */
+    udelay (RTD_SETTLE_DELAY);
+
+					/* clear any old fifo data */
+    RtdClearAdcFifo (dev);
+
+    stat = RtdFifoStatus (dev);		/* DEBUG */
+    if (stat & FS_ADC_EMPTY) {		/* 1 -> not empty */
+	printk ("rtd520: Warning: fifo didn't seem to clear! FifoStatus=0x%x\n",
+		stat);
+    }
+
+    /* convert n samples */
+    for (n=0; n < insn->n; n++) {
+	s16 d;
+	/* trigger conversion */
+	RtdAdcStart (dev);
+
+	/* wait for conversion to end */
+	udelay ((2*RTD_MAX_SPEED + RTD_MAX_SPEED - 1)/1000);
+	for (ii = 0; ii < RTD_ADC_TIMEOUT; ++ii) {
+	    /* by delaying here, we try to reduce system electrical noise */
+	    udelay (1);
+	    stat = RtdFifoStatus (dev);
+	    if (stat & FS_ADC_EMPTY)	/* 1 -> not empty */
+		break;
+	}
+	if (ii >= RTD_ADC_TIMEOUT) {
+	    printk ("rtd520: Error: Never got ADC done flag! FifoStatus=0x%x\n",
+		    stat);
+	    return -ETIMEDOUT;
+	}
+
+	/* read data */
+	d = RtdAdcFifoGet (dev);	/* get 2s comp value */
+	/*printk ("rtd520: Got 0x%x after %d usec\n", d, ii+1);*/
+	d = d >> 3;			/* low 3 bits are marker lines */
+	data[n] = d + 2048;		/* convert to comedi unsigned data */
+    }
+
+    /* return the number of samples read/written */
+    return n;
+}
+
+/*
+  Fifo is a least half full.  Get what we know is there.... Fast!
+  This uses 1/2 the bus cycles of read_dregs (below).
+
+  The manual claims that we can do a lword read, but it doesn't work here.
+*/
+static void ai_read_half_fifo (
+    comedi_device *dev,
+    comedi_subdevice *s)
+{
+    int ii;
+
+    for (ii = 0; ii < thisboard->fifoLen / 2; ii++) {
+	s16 d = RtdAdcFifoGet (dev);	/* get 2s comp value */
+	d = d >> 3;			/* low 3 bits are marker lines */
+
+	if (devpriv->aiCount <= 0) {
+					/* should never happen */
+	    devpriv->aiExtraInt++;
+	    continue;
+	}
+
+					/* check and deal with buffer wrap */
+	if (s->async->buf_int_ptr >= s->async->data_len) {
+	    s->async->buf_int_ptr = 0;
+	    comedi_eobuf(dev, s);
+	}
+					/* write into buffer */
+	*((sampl_t *)((void *)s->async->data + s->async->buf_int_ptr))
+	    = d + 2048;		/* convert to comedi unsigned data */
+	s->async->buf_int_count += sizeof(sampl_t);
+	s->async->buf_int_ptr += sizeof(sampl_t);
+	devpriv->aiCount--;
+    }
+}
+
+/*
+  unknown amout of data is waiting in fifo.
+*/
+static void ai_read_dregs (
+    comedi_device *dev,
+    comedi_subdevice *s)
+{
+    while (RtdFifoStatus (dev) & FS_ADC_EMPTY) { /* 1 -> not empty */
+	s16 d = RtdAdcFifoGet (dev); /* get 2s comp value */
+
+	d = d >> 3;		/* low 3 bits are marker lines */
+
+	if (devpriv->aiCount <= 0) {
+					/* should never happen */
+	    devpriv->aiExtraInt++;
+	    continue;
+	}
+
+					/* check and deal with buffer wrap */
+	if (s->async->buf_int_ptr >= s->async->data_len) {
+	    s->async->buf_int_ptr = 0;
+	    comedi_eobuf(dev, s);
+	}
+					/* write into buffer */
+	*((sampl_t *)((void *)s->async->data + s->async->buf_int_ptr))
+	    = d + 2048;		/* convert to comedi unsigned data */
+	s->async->buf_int_count += sizeof(sampl_t);
+	s->async->buf_int_ptr += sizeof(sampl_t);
+	devpriv->aiCount--;
+    }
+}
+
+/*
+  Handle all rtd520 interrupts.  
+  Runs atomically and is never re-entered.
+  This is a "slow handler";  other interrupts may be active.
+  The data conversion may someday happen in a "bottom half".
+*/
+static void rtd_interrupt (
+    int irq,				/* interrupt number (ignored) */
+    void *d,				/* our data */
+    struct pt_regs *regs)		/* cpu context (ignored) */
+{
+    comedi_device *dev = d;		/* must be called "dev" for devpriv */
+    u16 status = RtdInterruptStatus (dev);
+
+    devpriv->intCount++;
+
+    /* if interrupt was not caused by our board */
+    /* needed??? we dont claim to share interrupt lines */
+    if ((0 == status)
+       || !(dev->attached)) {
+	return;
+    }
+    /* Either end if a sequence (about), or time to flush the fifo (sample) */
+    /* You can only interrupt on fifo half full if doing DMA  */
+    if (status & (IRQM_ADC_ABOUT_CNT | IRQM_ADC_SAMPLE_CNT)) {
+	comedi_subdevice *s = dev->subdevices + 0; /* analog in subdevice */
+					/* only read in big chunks */
+	if (RtdFifoStatus (dev) & FS_ADC_HEMPTY) {
+	    ai_read_half_fifo (dev, s);
+	    comedi_bufcheck (dev, s);	/* signal something there */
+	}
+
+	if (status & IRQM_ADC_ABOUT_CNT) { /* about counter terminated */
+	    if (devpriv->aboutWrap) { /* multi-count wraps */
+		if (0 == devpriv->aiCount) { /* done! stop! */
+		    RtdPacerStop (dev); /* Stop PACER */
+		    RtdInterruptMask (dev, 0);	/* mask out ABOUT and SAMPLE */
+		    ai_read_dregs (dev, s);
+		    comedi_done (dev, s);	/* signal end to comedi */
+		} else if (devpriv->aiCount < devpriv->aboutWrap) {
+		    RtdAboutStopEnable (dev, 0); /* enable stop */
+		    devpriv->aboutWrap = 0;
+		}
+	    } else {			/* done */
+	    /* TODO: allow multiple interrupt sources */
+		RtdInterruptMask (dev, 0);/* mask out ABOUT and SAMPLE */
+		ai_read_dregs (dev, s);
+
+		comedi_done (dev, s);	/* signal end to comedi */
+	    }
+	}
+	
+    }
+					/* clear the interrupt */
+    RtdInterruptClearMask (dev, status);
+    RtdInterruptClear (dev);
+}
+
+/*
+  cmdtest tests a particular command to see if it is valid.
+  Using the cmdtest ioctl, a user can create a valid cmd
+  and then have it executed by the cmd ioctl (asyncronously).
+ 
+  cmdtest returns 1,2,3,4 or 0, depending on which tests
+  the command passes.
+*/
+
+static int
+rtd_ai_cmdtest (
+    comedi_device *dev,
+    comedi_subdevice *s,
+    comedi_cmd *cmd)
+{
+    int err=0;
+    int tmp;
+
+    /* step 1: make sure trigger sources are trivially valid */
+
+    tmp = cmd->start_src;
+    cmd->start_src &= TRIG_NOW;
+    if (!cmd->start_src && tmp != cmd->start_src) {
+	printk ("rtd520: cmdtest error! start_src does not include NOW %x\n",
+		cmd->start_src);
+	err++;
+    }
+
+    tmp=cmd->scan_begin_src;
+    cmd->scan_begin_src &= TRIG_TIMER|TRIG_EXT;
+    if (!cmd->scan_begin_src && tmp != cmd->scan_begin_src) {
+	printk ("rtd520: cmdtest error! scan_begin_src includes neither TIMER or EXT %x\n",
+		cmd->scan_begin_src);
+	err++;
+    }
+
+    tmp=cmd->convert_src;
+    cmd->convert_src &= TRIG_TIMER|TRIG_EXT;
+    if (!cmd->convert_src && tmp != cmd->convert_src) {
+	printk ("rtd520: cmdtest error! convert_src includes neither TIMER or EXT %x\n",
+		cmd->convert_src);
+	err++;
+    }
+
+    tmp=cmd->scan_end_src;
+    cmd->scan_end_src &= TRIG_COUNT;
+    if (!cmd->scan_end_src && tmp != cmd->scan_end_src) {
+	printk ("rtd520: cmdtest error! scan_end_src does not include COUNT %x\n",
+		cmd->scan_end_src);
+	err++;
+    }
+
+    tmp=cmd->stop_src;
+    cmd->stop_src &= TRIG_COUNT|TRIG_NONE;
+    if (!cmd->stop_src && tmp != cmd->stop_src) {
+	printk ("rtd520: cmdtest error! stop_src includes neither COUNT or NONE %x\n",
+		cmd->stop_src);
+	err++;
+    }
+
+    if (err)
+	return 1;
+
+    /* step 2: make sure trigger sources are unique
+       and mutually compatible */
+#if 0
+    /* note that mutual compatiblity is not an issue here */
+    if (cmd->scan_begin_src !=TRIG_TIMER &&
+	cmd->scan_begin_src !=TRIG_EXT) {
+	err++;
+    }
+    if (cmd->convert_src !=TRIG_TIMER &&
+	cmd->convert_src !=TRIG_EXT) {
+	err++;
+    }
+    if (cmd->stop_src != TRIG_TIMER &&
+	cmd->stop_src != TRIG_EXT) {
+	err++;
+    }
+
+    if (err) {
+	printk ("rtd520: cmdtest error! Some trigger compatibility test failed.\n");
+	return 2;
+    }
+#endif
+
+    /* step 3: make sure arguments are trivially compatible */
+
+    if (cmd->start_arg != 0) {
+	cmd->start_arg = 0;
+	printk ("rtd520: cmdtest: start_arg not 0\n");
+	err++;
+    }
+
+    if (cmd->scan_begin_src == TRIG_TIMER){
+	if (cmd->scan_begin_arg < RTD_MAX_SPEED) {
+	    cmd->scan_begin_arg = RTD_MAX_SPEED;
+	    printk ("rtd520: cmdtest: scan rate greater than max.\n");
+	    err++;
+	}
+	if (cmd->scan_begin_arg > RTD_MIN_SPEED) {
+	    cmd->scan_begin_arg = RTD_MIN_SPEED;
+	    printk ("rtd520: cmdtest: scan rate lower than min.\n");
+	    err++;
+	}
+    } else {
+	/* external trigger */
+	/* should be level/edge, hi/lo specification here */
+	/* should specify multiple external triggers */
+	if (cmd->scan_begin_arg > 9) {
+	    cmd->scan_begin_arg = 9;
+	    printk ("rtd520: cmdtest: scan_begin_arg out of range\n");
+	    err++;
+	}
+    }
+    if (cmd->convert_src==TRIG_TIMER) {
+	if (cmd->convert_arg < RTD_MAX_SPEED) {
+	    cmd->convert_arg = RTD_MAX_SPEED;
+	    printk ("rtd520: cmdtest: convert rate greater than max.\n");
+	    err++;
+	}
+	if (cmd->convert_arg > RTD_MIN_SPEED) {
+	    cmd->convert_arg = RTD_MIN_SPEED;
+	    printk ("rtd520: cmdtest: convert rate lower than min.\n");
+	    err++;
+	}
+    } else {
+	/* external trigger */
+	/* see above */
+	if (cmd->convert_arg > 9) {
+	    cmd->convert_arg = 9;
+	    printk ("rtd520: cmdtest: convert_arg out of range\n");
+	    err++;
+	}
+    }
+
+#if 0
+    if (cmd->scan_end_arg != cmd->chanlist_len) {
+	cmd->scan_end_arg = cmd->chanlist_len;
+	err++;
+    }
+#endif
+    if (cmd->stop_src==TRIG_COUNT) {
+	/* TODO check for rounding error due to counter wrap */
+
+    } else {
+	/* TRIG_NONE */
+	if (cmd->stop_arg!=0) {
+	    cmd->stop_arg=0;
+	    printk ("rtd520: cmdtest: stop_arg not 0\n");
+	    err++;
+	}
+    }
+
+    if (err) {
+	printk ("rtd520: cmdtest error! Some argument compatibility test failed.\n");
+	return 3;
+    }
+
+    /* step 4: fix up any arguments */
+
+    if (cmd->scan_begin_src == TRIG_TIMER) {
+	tmp=cmd->scan_begin_arg;
+	rtd_ns_to_timer(&cmd->scan_begin_arg,
+			cmd->flags&TRIG_ROUND_MASK);
+	if (tmp!=cmd->scan_begin_arg) {
+	    err++;
+	}
+    }
+    if (cmd->convert_src == TRIG_TIMER){
+	tmp=cmd->convert_arg;
+	rtd_ns_to_timer(&cmd->convert_arg,
+			cmd->flags&TRIG_ROUND_MASK);
+	if (tmp!=cmd->convert_arg) {
+	    err++;
+	}
+	if (cmd->scan_begin_src == TRIG_TIMER
+	    && (cmd->scan_begin_arg
+		< (cmd->convert_arg * cmd->scan_end_arg))) {
+	    cmd->scan_begin_arg=cmd->convert_arg*cmd->scan_end_arg;
+	    err++;
+	}
+    }
+
+    if (err) {
+	printk ("rtd520: cmdtest error! Some timer value was altered.\n");
+	return 4;
+    }
+
+    return 0;
+}
+
+/*
+  Execute a analog in command with many possible triggering options.
+  The data get stored in the async structure of the subdevice.
+  This is usually done by an interrupt handler.
+  Userland gets to the data using read calls.
+*/
+static int rtd_ai_cmd (
+    comedi_device *dev,
+    comedi_subdevice *s)
+{
+    comedi_cmd *cmd=&s->async->cmd;
+    int timer;
+    int	justPoll = 0;			/* can we do a simple poll */
+
+					/* stop anything currently running */
+    RtdPacerStop (dev);			/* Stop PACER */
+
+    /* start configuration */
+    rtd_load_channelgain_list (dev, cmd->chanlist_len, cmd->chanlist);
+
+    /* setup the common case and override if needed */
+    if (cmd->chanlist_len > 1) {
+	printk ("rtd520: Multi channel setup\n");
+	RtdPacerStartSource (dev, 0);	/* software triggers pacer */
+	RtdBurstStartSource (dev, 1);	/* PACER triggers burst */
+	RtdAdcConversionSource (dev, 2); /* BURST triggers ADC */
+    } else {				/* single channel */
+	printk ("rtd520: single channel setup\n");
+	RtdPacerStartSource (dev, 0);	/* software triggers pacer */
+	RtdAdcConversionSource (dev, 1); /* PACER triggers ADC */
+    }
+
+    RtdAdcSampleCounter (dev,		/* setup a periodic interrupt */
+			 (thisboard->fifoLen > 1024) ? 1023 : 511);
+    RtdPacerStopSource (dev, 3);	/* stop on ABOUT count down*/
+    RtdAboutStopEnable (dev, 0);	/* actually stop (see below) */
+    RtdPacerClockSource (dev, 1);	/* use INTERNAL 8Mhz clock source */
+    RtdAdcSampleCounterSource (dev, 1);	/* count samples, not scans */
+
+    /* BUG!!! these look like enumerated values, but they are bit fields */
+
+    /* First, setup when to stop */
+    switch(cmd->stop_src){
+    case TRIG_COUNT:			/* stop after N scans */
+	if ((cmd->chanlist_len <= 1) 	/* no scanning to do */
+	    && (cmd->stop_arg <= 1)) {
+	    justPoll = 1;
+	    RtdAdcConversionSource (dev, 0); /* SOFTWARE trigger */
+	} else {
+	    int	n = cmd->stop_arg * cmd->chanlist_len;
+	    /* load about counter (16bit) with number of SAMPLES */
+	    devpriv->aiCount = n;
+	    if (n <= 0x10000) {
+		/* Note: stop on underflow.  Load with N-1 */
+		printk ("rtd520: loading %d into about\n", n - 1);
+		devpriv->aboutWrap = 0;
+		RtdAboutCounter (dev, n - 1);
+	    } else {			/* multiple counter wraps */
+		int	mm, dd;
+
+		/* interrupt on ABOUT wrap, until last wrap */
+		mm = n & 0xffff;
+		dd = n / mm;		/* effective divisor */
+		while (mm < 0xfff) {	/* make sure we have time to arm */
+		    dd++;
+		    mm = (n + dd-1) / dd; /* round up, if needed */
+		}
+		/* TODO fix round error */
+		printk ("rtd520: Warning! count value %d > 65536 using %d\n",
+			n, mm);
+		devpriv->aboutWrap = mm;
+		RtdAboutCounter (dev, mm-1);
+		RtdAboutStopEnable (dev, 1); /* just interrupt */
+	    }
+	}
+
+	break;
+
+    case TRIG_NONE:			/* stop when cancel is called */
+	RtdPacerStopSource (dev, 0);	/* stop on SOFTWARE stop */
+	break;
+
+    default:
+	printk ("rtd520: Warning! ignoring stop_src mode %d\n",
+		cmd->stop_src);
+    }
+
+
+    /* Scan timing */
+    switch (cmd->scan_begin_src) {
+    case TRIG_TIMER:			/* periodic scanning */
+	timer=rtd_ns_to_timer(&cmd->scan_begin_arg,TRIG_ROUND_NEAREST);
+	/* set PACER clock */
+	printk ("rtd520: loading %d into pacer for %dns\n",
+		timer, cmd->scan_begin_arg);
+	RtdPacerCounter (dev, timer);
+
+	break;
+
+    case TRIG_EXT:
+	RtdPacerStartSource (dev, 1);	/* EXTERNALy trigger pacer */
+	break;
+
+    default:
+	printk ("rtd520: Warning! ignoring scan_begin_src mode %d\n",
+		cmd->scan_begin_src);
+    }
+
+    /* Sample timing within a scan */
+    switch(cmd->convert_src){
+    case TRIG_TIMER:			/* periodic */
+	timer=rtd_ns_to_timer(&cmd->convert_arg,TRIG_ROUND_NEAREST);
+	/* setup BURST clock */
+	printk ("rtd520: loading %d into burst\n", timer);
+	RtdBurstCounter (dev, timer);
+
+	break;
+
+    case TRIG_EXT:			/* external */
+	RtdBurstStartSource (dev, 2);	/* EXTERNALy trigger burst */
+	break;
+
+    default:
+	printk ("rtd520: Warning! ignoring convert_src mode %d\n",
+		cmd->convert_src);
+    }
+
+
+    /* end configuration */
+
+    /* start_src is ASSUMED to be TRIG_NOW */
+    /* initial settling */
+    udelay (RTD_SETTLE_DELAY);
+					/* clear any old data */
+    RtdClearAdcFifo (dev);
+
+    /* see if we can do a simple polled input */
+    if (justPoll) {
+	int stat = RtdFifoStatus (dev);		/* DEBUG */
+	s16 d;
+	int ii;
+	
+					/* DEBUG */
+	if (stat & FS_ADC_EMPTY) {		/* 1 -> not empty */
+	    printk ("rtd520: ai_cmd Warning: fifo didn't seem to clear! FifoStatus=0x%x\n",
+		    stat);
+	} else {
+	    printk ("rtd520: ai_cmd: polling for sample.\n");
+	}
+
+	/* trigger conversion */
+	RtdAdcStart (dev);
+
+	udelay ((2*RTD_MAX_SPEED + RTD_MAX_SPEED - 1)/1000);
+	/* right now, this means just 1 sample. emulate ai_rinsn */
+	for (ii = 0; ii < RTD_ADC_TIMEOUT; ++ii) {
+	    /* by delaying here, we try to reduce system electrical noise */
+	    udelay (1);
+	    stat = RtdFifoStatus (dev);
+	    if (stat & FS_ADC_EMPTY)	/* 1 -> not empty */
+		break;
+	}
+	if (ii >= RTD_ADC_TIMEOUT) {
+	    printk ("rtd520: ai_cmd Error: Never got data in FIFO! FifoStatus=0x%x\n",
+		    stat);
+	    return -ETIMEDOUT;
+	}
+
+	/* read data */
+	d = RtdAdcFifoGet (dev);	/* get 2s comp value */
+	/*printk ("rtd520: Got 0x%x after %d usec\n", d, ii+1);*/
+	d = d >> 3;		/* low 3 bits are marker lines */
+
+					/* write into buffer */
+	*((sampl_t *)((void *)s->async->data + s->async->buf_int_ptr))
+	    = d + 2048;			/* convert to comedi unsigned data */
+	s->async->buf_int_count += sizeof(sampl_t);
+	s->async->buf_int_ptr += sizeof(sampl_t);
+	comedi_done (dev, s);
+    } else {
+	/* interrupt setup */
+	if (! dev->irq) {
+	    printk ("rtd520: ERROR! No interrupt available!\n");
+	    return -ENXIO;
+	}
+	
+	printk("rtd520: using interrupts. (%ld ints, %ld extra ai)\n(int status 0x%x, overrun status 0x%x, fifo status 0x%x)\n",
+	   devpriv->intCount, devpriv->aiExtraInt,
+	   0xffff & RtdInterruptStatus (dev),
+	       0xffff & RtdInterruptOverrunStatus (dev),
+	       0xffff & RtdFifoStatus (dev));
+
+	RtdInterruptClearMask (dev, ~0); /* clear any existing flags */
+	RtdInterruptClear (dev);
+	/*DEBUG RtdInterruptOverrunClear(dev);*/
+
+	/* TODO: allow multiple interrupt sources */
+	if (devpriv->aiCount > 512) {
+	    RtdInterruptMask (dev, IRQM_ADC_ABOUT_CNT | IRQM_ADC_SAMPLE_CNT );
+	} else {
+	    RtdInterruptMask (dev, IRQM_ADC_ABOUT_CNT);
+	}
+
+	RtdPacerStart (dev);		/* Start PACER */
+    }
+    return 0;
+}
+
+/*
+  Stop a running data aquisition.
+*/
+static int rtd_ai_cancel (
+    comedi_device *dev,
+    comedi_subdevice *s)
+{
+					/* more is probably needed here */
+    RtdPacerStop (dev);			/* Stop PACER */
+    RtdAdcConversionSource (dev, 0);	/* software trigger only */
+    return 0;
+}
+
+/*
+  Given a desired period and the clock period (both in ns),
+  return the proper counter value (divider-1).
+  Sets the original period to be the true value.
+  Note: you have to check if the value is larger than the counter range!
+*/
+static int rtd_ns_to_timer_base (
+    unsigned int *nanosec,		/* desired period (in ns) */
+    int round_mode,
+    double base)			/* clock period (in ns) */
+{
+    int divider;
+
+    switch(round_mode){
+    case TRIG_ROUND_NEAREST:
+    default:
+	divider=(*nanosec+base/2)/base;
+	break;
+    case TRIG_ROUND_DOWN:
+	divider=(*nanosec)/base;
+	break;
+    case TRIG_ROUND_UP:
+	divider=(*nanosec+base-1)/base;
+	break;
+    }
+    if (divider < 2) divider = 2;	/* min is divide by 2 */
+
+    /* Note: we don't check for max, because different timers
+       have different ranges */
+
+    *nanosec=base*divider;
+    return divider - 1;			/* countdown is divisor+1 */
+}
+
+/*
+  Given a desired period (in ns),
+  return the proper counter value (divider-1) for the internal clock.
+  Sets the original period to be the true value.
+*/
+static int rtd_ns_to_timer (
+    unsigned int *ns,
+    int round_mode)
+{
+    return rtd_ns_to_timer_base (ns, round_mode, RTD_CLOCK_BASE);
+}
+
+static int rtd_ao_winsn (
+    comedi_device *dev,
+    comedi_subdevice *s,
+    comedi_insn *insn,
+    lsampl_t *data)
+{
+    int i;
+    int chan = CR_CHAN(insn->chanspec);
+
+    /* Writing a list of values to an AO channel is probably not
+     * very useful, but that's how the interface is defined. */
+    for(i=0;i<insn->n;i++){
+	/* a typical programming sequence */
+	//outw(data[i],dev->iobase + RTD_DA0 + chan);
+	devpriv->ao_readback[chan] = data[i];
+    }
+
+    /* return the number of samples read/written */
+    return 1;
+}
+
+/* AO subdevices should have a read insn as well as a write insn.
+ * Usually this means copying a value stored in devpriv. */
+static int rtd_ao_rinsn (
+    comedi_device *dev,
+    comedi_subdevice *s,
+    comedi_insn *insn,
+    lsampl_t *data)
+{
+    int i;
+    int chan = CR_CHAN(insn->chanspec);
+
+    for(i=0;i<insn->n;i++)
+	data[i] = devpriv->ao_readback[chan];
+
+    return i;
+}
+
+/* DIO devices are slightly special.  Although it is possible to
+ * implement the insn_read/insn_write interface, it is much more
+ * useful to applications if you implement the insn_bits interface.
+ * This allows packed reading/writing of the DIO channels.  The
+ * comedi core can convert between insn_bits and insn_read/write */
+static int rtd_dio_insn_bits (
+    comedi_device *dev,
+    comedi_subdevice *s,
+    comedi_insn *insn,
+    lsampl_t *data)
+{
+    if (insn->n!=2) return -EINVAL;
+
+    /* The insn data is a mask in data[0] and the new data
+     * in data[1], each channel cooresponding to a bit. */
+    if (data[0]) {
+	s->state &= ~data[0];
+	s->state |= data[0]&data[1];
+	/* Write out the new digital output lines */
+	//outw(s->state,dev->iobase + RTD_DIO);
+    }
+    /* on return, data[1] contains the value of the digital
+     * input lines. */
+    //data[1]=inw(dev->iobase + RTD_DIO);
+
+    return 2;
+}
+
+static int rtd_dio_insn_config (
+    comedi_device *dev,
+    comedi_subdevice *s,
+    comedi_insn *insn,
+    lsampl_t *data)
+{
+    int chan=CR_CHAN(insn->chanspec);
+
+    if (insn->n!=1) return -EINVAL;
+
+    /* The input or output configuration of each digital line is
+     * configured by a special insn_config instruction.  chanspec
+     * contains the channel to be changed, and data[0] contains the 
+     * value COMEDI_INPUT or COMEDI_OUTPUT. */
+	
+    if (data[0]==COMEDI_OUTPUT) {
+	s->io_bits |= 1<<chan;
+    } else {
+	s->io_bits &= ~(1<<chan);
+    }
+    //outw(s->io_bits,dev->iobase + RTD_DIO_CONFIG);
+ 
+    return 1;
+}
+
+
+/*
+ * A convenient macro that defines init_module() and cleanup_module(),
+ * as necessary.
+ */
+COMEDI_INITCLEANUP(rtd520Driver);
+
diff --git a/comedi/drivers/rtd520.h b/comedi/drivers/rtd520.h
new file mode 100644
index 00000000..1dea123b
--- /dev/null
+++ b/comedi/drivers/rtd520.h
@@ -0,0 +1,455 @@
+/*
+    comedi/drivers/rtd520.h
+    Comedi driver defines for Real Time Devices (RTD) PCI4520/DM7520
+    
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 2001 David A. Schleef <ds@schleef.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+    Created by Dan Christian, NASA Ames Research Center.
+    See board notes in rtd520.c
+*/
+
+/*
+  LAS0 Runtime Area
+  Local Address Space 0 Offset		Read Function	Write Function   
+*/
+#define LAS0_SPARE_00    0x0000         // -                               -
+#define LAS0_SPARE_04    0x0004         // -                               -
+#define LAS0_USER_IO     0x0008         // Read User Inputs                Write User Outputs
+#define LAS0_SPARE_0C    0x000C         // -                               -
+#define LAS0_ADC         0x0010         // Read FIFO Status                Software A/D Start
+#define LAS0_DAC1        0x0014         // -                               Software D/A1 Update
+#define LAS0_DAC2        0x0018         // -                               Software D/A2 Update
+#define LAS0_SPARE_1C    0x001C         // -                               -
+#define LAS0_SPARE_20    0x0020         // -                               -
+#define LAS0_DAC         0x0024         // -                               Software Simultaneous D/A1 and D/A2 Update
+#define LAS0_PACER       0x0028         // Software Pacer Start            Software Pacer Stop
+#define LAS0_TIMER       0x002C         // Read Timer Counters Status      HDIN Software Trigger
+#define LAS0_IT          0x0030         // Read Interrupt Status           Write Interrupt Enable Mask Register
+#define LAS0_CLEAR       0x0034         // Clear ITs set by Clear Mask     Set Interrupt Clear Mask
+#define LAS0_OVERRUN     0x0038         // Read pending interrupts         Clear Overrun Register
+#define LAS0_SPARE_3C    0x003C         // -                               -
+
+/*
+  LAS0 Runtime Area Timer/Counter,Dig.IO   
+  Name			Local Address			Function
+*/
+#define LAS0_PCLK        0x0040         // Pacer Clock value (24bit)             Pacer Clock load (24bit)
+#define LAS0_BCLK        0x0044         // Burst Clock value (10bit)             Burst Clock load (10bit)
+#define LAS0_ADC_SCNT    0x0048         // A/D Sample counter value (10bit)      A/D Sample counter load (10bit)
+#define LAS0_DAC1_UCNT   0x004C         // D/A1 Update counter value (10 bit)    D/A1 Update counter load (10bit)
+#define LAS0_DAC2_UCNT   0x0050         // D/A2 Update counter value (10 bit)    D/A2 Update counter load (10bit)
+#define LAS0_DCNT        0x0054         // Delay counter value (16 bit)          Delay counter load (16bit)
+#define LAS0_ACNT        0x0058         // About counter value (16 bit)          About counter load (16bit)
+#define LAS0_DAC_CLK     0x005C         // DAC clock value (16bit)               DAC clock load (16bit)
+#define LAS0_UTC0        0x0060         // 8254 TC Counter 0 User TC 0 value     Load count in TC Counter 0
+#define LAS0_UTC1        0x0064         // 8254 TC Counter 1 User TC 1 value     Load count in TC Counter 1
+#define LAS0_UTC2        0x0068         // 8254 TC Counter 2 User TC 2 value     Load count in TC Counter 2
+#define LAS0_UTC_CTRL    0x006C         // 8254 TC Control Word                  Program counter mode for TC
+#define LAS0_DIO0        0x0070         // Digital I/O Port 0 Read Port          Digital I/O Port 0 Write Port
+#define LAS0_DIO1        0x0074         // Digital I/O Port 1 Read Port          Digital I/O Port 1 Write Port
+#define LAS0_DIO0_CTRL   0x0078         // Clear digital IRQ status flag/read    Clear digital chip/program Port 0
+#define LAS0_DIO_STATUS  0x007C         // Read Digital I/O Status word          Program digital control register &
+
+/*
+  LAS0 Setup Area
+  Name			Local Address			Function
+*/
+#define LAS0_BOARD_RESET        0x0100         // Board reset
+#define LAS0_DMA0_SRC           0x0104         // DMA 0 Sources select
+#define LAS0_DMA1_SRC           0x0108         // DMA 1 Sources select
+#define LAS0_ADC_CONVERSION     0x010C         // A/D Conversion Signal select
+#define LAS0_BURST_START        0x0110         // Burst Clock Start Trigger select
+#define LAS0_PACER_START        0x0114         // Pacer Clock Start Trigger select
+#define LAS0_PACER_STOP         0x0118         // Pacer Clock Stop Trigger select
+#define LAS0_ACNT_STOP_ENABLE   0x011C         // About Counter Stop Enable
+#define LAS0_PACER_REPEAT       0x0120         // Pacer Start Trigger Mode select
+#define LAS0_DIN_START          0x0124         // High Speed Digital Input Sampling Signal select
+#define LAS0_DIN_FIFO_CLEAR     0x0128         // Digital Input FIFO Clear
+#define LAS0_ADC_FIFO_CLEAR     0x012C         // A/D FIFO Clear
+#define LAS0_CGT_WRITE          0x0130         // Channel Gain Table Write
+#define LAS0_CGL_WRITE          0x0134         // Channel Gain Latch Write
+#define LAS0_CG_DATA            0x0138         // Digital Table Write
+#define LAS0_CGT_ENABLE		0x013C         // Channel Gain Table Enable
+#define LAS0_CG_ENABLE          0x0140         // Digital Table Enable
+#define LAS0_CGT_PAUSE          0x0144         // Table Pause Enable
+#define LAS0_CGT_RESET          0x0148         // Reset Channel Gain Table
+#define LAS0_CGT_CLEAR          0x014C         // Clear Channel Gain Table
+#define LAS0_DAC1_CTRL          0x0150         // D/A1 output type/range
+#define LAS0_DAC1_SRC           0x0154         // D/A1 update source
+#define LAS0_DAC1_CYCLE         0x0158         // D/A1 cycle mode
+#define LAS0_DAC1_RESET         0x015C         // D/A1 FIFO reset
+#define LAS0_DAC1_FIFO_CLEAR    0x0160         // D/A1 FIFO clear
+#define LAS0_DAC2_CTRL          0x0164         // D/A2 output type/range
+#define LAS0_DAC2_SRC           0x0168         // D/A2 update source
+#define LAS0_DAC2_CYCLE         0x016C         // D/A2 cycle mode
+#define LAS0_DAC2_RESET         0x0170         // D/A2 FIFO reset
+#define LAS0_DAC2_FIFO_CLEAR    0x0174         // D/A2 FIFO clear
+#define LAS0_ADC_SCNT_SRC       0x0178         // A/D Sample Counter Source select
+#define LAS0_PACER_SELECT       0x0180         // Pacer Clock select
+#define LAS0_SBUS0_SRC          0x0184         // SyncBus 0 Source select
+#define LAS0_SBUS0_ENABLE       0x0188         // SyncBus 0 enable
+#define LAS0_SBUS1_SRC          0x018C         // SyncBus 1 Source select
+#define LAS0_SBUS1_ENABLE       0x0190         // SyncBus 1 enable
+#define LAS0_SBUS2_SRC          0x0198         // SyncBus 2 Source select
+#define LAS0_SBUS2_ENABLE       0x019C         // SyncBus 2 enable
+#define LAS0_ETRG_POLARITY      0x01A4         // External Trigger polarity select
+#define LAS0_EINT_POLARITY      0x01A8         // External Interrupt polarity select
+#define LAS0_UTC0_CLOCK         0x01AC         // UTC0 Clock select
+#define LAS0_UTC0_GATE          0x01B0         // UTC0 Gate select
+#define LAS0_UTC1_CLOCK         0x01B4         // UTC1 Clock select
+#define LAS0_UTC1_GATE          0x01B8         // UTC1 Gate select
+#define LAS0_UTC2_CLOCK         0x01BC         // UTC2 Clock select
+#define LAS0_UTC2_GATE          0x01C0         // UTC2 Gate select
+#define LAS0_UOUT0_SELECT       0x01C4         // User Output 0 source select
+#define LAS0_UOUT1_SELECT       0x01C8         // User Output 1 source select
+#define LAS0_DMA0_RESET         0x01CC         // DMA0 Request state machine reset
+#define LAS0_DMA1_RESET         0x01D0         // DMA1 Request state machine reset
+
+/*
+  LAS1
+  Name			Local Address			Function
+*/
+#define LAS1_ADC_FIFO            0x0000    // Read A/D FIFO (16bit) -
+#define LAS1_HDIO_FIFO           0x0004    // Read High Speed Digital Input FIFO (16bit) -
+#define LAS1_DAC1_FIFO           0x0008    // - Write D/A1 FIFO (16bit)
+#define LAS1_DAC2_FIFO           0x000C    // - Write D/A2 FIFO (16bit)
+
+/*
+  LCFG: PLX 9080 local config & runtime registers
+  Name			Local Address			Function
+*/
+#define LCFG_ITCSR              0x0068    // INTCSR, Interrupt Control/Status Register
+#define LCFG_DMAMODE0           0x0080    // DMA Channel 0 Mode Register
+#define LCFG_DMAPADR0           0x0084    // DMA Channel 0 PCI Address Register
+#define LCFG_DMALADR0           0x0088    // DMA Channel 0 Local Address Register
+#define LCFG_DMASIZ0            0x008C    // DMA Channel 0 Transfer Size (Bytes) Register
+#define LCFG_DMADPR0            0x0090    // DMA Channel 0 Descriptor Pointer Register
+#define LCFG_DMAMODE1           0x0094    // DMA Channel 1 Mode Register
+#define LCFG_DMAPADR1           0x0098    // DMA Channel 1 PCI Address Register
+#define LCFG_DMALADR1           0x009C    // DMA Channel 1 Local Address Register
+#define LCFG_DMASIZ1            0x00A0    // DMA Channel 1 Transfer Size (Bytes) Register
+#define LCFG_DMADPR1            0x00A4    // DMA Channel 1 Descriptor Pointer Register
+#define LCFG_DMACSR0            0x00A8    // DMA Channel 0 Command/Status Register
+#define LCFG_DMACSR1            0x00A9    // DMA Channel 0 Command/Status Register
+#define LCFG_DMAARB             0x00AC    // DMA Arbitration Register
+#define LCFG_DMATHR             0x00B0    // DMA Threshold Register
+
+
+
+/*======================================================================
+  Resister bit definitions
+======================================================================*/
+
+// FIFO Status Word Bits (RtdFifoStatus)
+#define FS_DAC1_EMPTY    0x0001		// D0  - DAC1 FIFO not empty
+#define FS_DAC1_HEMPTY   0x0002		// D1  - DAC1 FIFO not half empty
+#define FS_DAC1_FULL     0x0004		// D2  - DAC1 FIFO not full
+#define FS_DAC2_EMPTY    0x0010		// D4  - DAC2 FIFO not empty
+#define FS_DAC2_HEMPTY   0x0020		// D5  - DAC2 FIFO not half empty
+#define FS_DAC2_FULL     0x0040		// D6  - DAC2 FIFO not full
+#define FS_ADC_EMPTY     0x0100		// D8  - ADC FIFO not empty
+#define FS_ADC_HEMPTY    0x0200		// D9  - ADC FIFO not half empty
+#define FS_ADC_FULL      0x0400		// D10 - ADC FIFO not full
+#define FS_DIN_EMPTY     0x1000		// D12 - DIN FIFO not empty
+#define FS_DIN_HEMPTY    0x2000		// D13 - DIN FIFO not half empty
+#define FS_DIN_FULL      0x4000		// D14 - DIN FIFO not full
+
+// Timer Status Word Bits (GetTimerStatus)
+#define TS_PCLK_GATE   0x0001
+// D0 - Pacer Clock Gate [0 - gated, 1 - enabled]
+#define TS_BCLK_GATE   0x0002
+// D1 - Burst Clock Gate [0 - disabled, 1 - running]
+#define TS_DCNT_GATE   0x0004
+// D2 - Pacer Clock Delayed Start Trigger [0 - delay over, 1 - delay in
+// progress]
+#define TS_ACNT_GATE   0x0008
+// D3 - Pacer Clock About Trigger [0 - completed, 1 - in progress]
+#define TS_PCLK_RUN    0x0010
+// D4 - Pacer Clock Shutdown Flag [0 - Pacer Clock cannot be start
+// triggered only by Software Pacer Start Command, 1 - Pacer Clock can
+// be start triggered]
+
+
+// External Trigger polarity select
+// External Interrupt polarity select
+#define POL_POSITIVE         0x0	// positive edge
+#define POL_NEGATIVE         0x1	// negative edge
+
+// User Output Signal select (SetUout0Source, SetUout1Source)
+#define UOUT_ADC                0x0 // A/D Conversion Signal
+#define UOUT_DAC1               0x1 // D/A1 Update
+#define UOUT_DAC2               0x2 // D/A2 Update
+#define UOUT_SOFTWARE           0x3 // Software Programmable
+
+// Pacer clock select (SetPacerSource)
+#define PCLK_INTERNAL           1   // Internal Pacer Clock
+#define PCLK_EXTERNAL           0   // External Pacer Clock
+
+// A/D Sample Counter Sources (SetAdcntSource, SetupSampleCounter)
+#define ADC_SCNT_CGT_RESET         0x0  // needs restart with StartPacer
+#define ADC_SCNT_FIFO_WRITE        0x1
+
+// A/D Conversion Signal Select (for SetConversionSelect)
+#define ADC_START_SOFTWARE         0x0  // Software A/D Start
+#define ADC_START_PCLK             0x1  // Pacer Clock (Ext. Int. see Func.509)
+#define ADC_START_BCLK             0x2  // Burst Clock
+#define ADC_START_DIGITAL_IT       0x3  // Digital Interrupt
+#define ADC_START_DAC1_MARKER1     0x4  // D/A 1 Data Marker 1
+#define ADC_START_DAC2_MARKER1     0x5  // D/A 2 Data Marker 1
+#define ADC_START_SBUS0            0x6  // SyncBus 0
+#define ADC_START_SBUS1            0x7  // SyncBus 1
+#define ADC_START_SBUS2            0x8  // SyncBus 2
+
+// Burst Clock start trigger select (SetBurstStart)
+#define BCLK_START_SOFTWARE        0x0  // Software A/D Start (StartBurst)
+#define BCLK_START_PCLK            0x1  // Pacer Clock
+#define BCLK_START_ETRIG           0x2  // External Trigger
+#define BCLK_START_DIGITAL_IT      0x3  // Digital Interrupt
+#define BCLK_START_SBUS0           0x4  // SyncBus 0
+#define BCLK_START_SBUS1           0x5  // SyncBus 1
+#define BCLK_START_SBUS2           0x6  // SyncBus 2
+
+// Pacer Clock start trigger select (SetPacerStart)
+#define PCLK_START_SOFTWARE        0x0  // Software Pacer Start (StartPacer)
+#define PCLK_START_ETRIG           0x1  // External trigger
+#define PCLK_START_DIGITAL_IT      0x2  // Digital interrupt
+#define PCLK_START_UTC2            0x3  // User TC 2 out
+#define PCLK_START_SBUS0           0x4  // SyncBus 0
+#define PCLK_START_SBUS1           0x5  // SyncBus 1
+#define PCLK_START_SBUS2           0x6  // SyncBus 2
+#define PCLK_START_D_SOFTWARE      0x8  // Delayed Software Pacer Start
+#define PCLK_START_D_ETRIG         0x9  // Delayed external trigger
+#define PCLK_START_D_DIGITAL_IT    0xA  // Delayed digital interrupt
+#define PCLK_START_D_UTC2          0xB  // Delayed User TC 2 out
+#define PCLK_START_D_SBUS0         0xC  // Delayed SyncBus 0
+#define PCLK_START_D_SBUS1         0xD  // Delayed SyncBus 1
+#define PCLK_START_D_SBUS2         0xE  // Delayed SyncBus 2
+#define PCLK_START_ETRIG_GATED     0xF  // External Trigger Gated controlled mode
+
+// Pacer Clock Stop Trigger select (SetPacerStop)
+#define PCLK_STOP_SOFTWARE         0x0  // Software Pacer Stop (StopPacer)
+#define PCLK_STOP_ETRIG            0x1  // External Trigger
+#define PCLK_STOP_DIGITAL_IT       0x2  // Digital Interrupt
+#define PCLK_STOP_ACNT             0x3  // About Counter
+#define PCLK_STOP_UTC2             0x4  // User TC2 out
+#define PCLK_STOP_SBUS0            0x5  // SyncBus 0
+#define PCLK_STOP_SBUS1            0x6  // SyncBus 1
+#define PCLK_STOP_SBUS2            0x7  // SyncBus 2
+#define PCLK_STOP_A_SOFTWARE       0x8  // About Software Pacer Stop
+#define PCLK_STOP_A_ETRIG          0x9  // About External Trigger
+#define PCLK_STOP_A_DIGITAL_IT     0xA  // About Digital Interrupt
+#define PCLK_STOP_A_UTC2           0xC  // About User TC2 out
+#define PCLK_STOP_A_SBUS0          0xD  // About SyncBus 0
+#define PCLK_STOP_A_SBUS1          0xE  // About SyncBus 1
+#define PCLK_STOP_A_SBUS2          0xF  // About SyncBus 2
+
+// About Counter Stop Enable
+#define ACNT_STOP                  0x0  // stop enable
+#define ACNT_NO_STOP               0x1  // stop disabled
+
+// DAC update source (SetDAC1Start & SetDAC2Start)
+#define DAC_START_SOFTWARE         0x0  // Software Update
+#define DAC_START_CGT              0x1  // CGT controlled Update
+#define DAC_START_DAC_CLK          0x2  // D/A Clock
+#define DAC_START_EPCLK            0x3  // External Pacer Clock
+#define DAC_START_SBUS0            0x4  // SyncBus 0
+#define DAC_START_SBUS1            0x5  // SyncBus 1
+#define DAC_START_SBUS2            0x6  // SyncBus 2
+
+// DAC Cycle Mode (SetDAC1Cycle, SetDAC2Cycle, SetupDAC)
+#define DAC_CYCLE_SINGLE           0x0  // not cycle
+#define DAC_CYCLE_MULTI            0x1  // cycle
+
+// 8254 Operation Modes (Set8254Mode, SetupTimerCounter)
+#define M8254_EVENT_COUNTER        0    // Event Counter
+#define M8254_HW_ONE_SHOT          1    // Hardware-Retriggerable One-Shot
+#define M8254_RATE_GENERATOR       2    // Rate Generator
+#define M8254_SQUARE_WAVE          3    // Square Wave Mode
+#define M8254_SW_STROBE            4    // Software Triggered Strobe
+#define M8254_HW_STROBE            5    // Hardware Triggered Strobe (Retriggerable)
+
+// User Timer/Counter 0 Clock Select (SetUtc0Clock)
+#define CUTC0_8MHZ                 0x0  // 8MHz
+#define CUTC0_EXT_TC_CLOCK1        0x1  // Ext. TC Clock 1
+#define CUTC0_EXT_TC_CLOCK2        0x2  // Ext. TC Clock 2
+#define CUTC0_EXT_PCLK             0x3  // Ext. Pacer Clock
+
+// User Timer/Counter 1 Clock Select (SetUtc1Clock)
+#define CUTC1_8MHZ                 0x0  // 8MHz
+#define CUTC1_EXT_TC_CLOCK1        0x1  // Ext. TC Clock 1
+#define CUTC1_EXT_TC_CLOCK2        0x2  // Ext. TC Clock 2
+#define CUTC1_EXT_PCLK             0x3  // Ext. Pacer Clock
+#define CUTC1_UTC0_OUT             0x4  // User Timer/Counter 0 out
+#define CUTC1_DIN_SIGNAL           0x5  // High-Speed Digital Input   Sampling signal
+
+// User Timer/Counter 2 Clock Select (SetUtc2Clock)
+#define CUTC2_8MHZ                 0x0  // 8MHz
+#define CUTC2_EXT_TC_CLOCK1        0x1  // Ext. TC Clock 1
+#define CUTC2_EXT_TC_CLOCK2        0x2  // Ext. TC Clock 2
+#define CUTC2_EXT_PCLK             0x3  // Ext. Pacer Clock
+#define CUTC2_UTC1_OUT             0x4  // User Timer/Counter 1 out
+
+// User Timer/Counter 0 Gate Select (SetUtc0Gate)
+#define GUTC0_NOT_GATED            0x0  // Not gated
+#define GUTC0_GATED                0x1  // Gated
+#define GUTC0_EXT_TC_GATE1         0x2  // Ext. TC Gate 1
+#define GUTC0_EXT_TC_GATE2         0x3  // Ext. TC Gate 2
+
+// User Timer/Counter 1 Gate Select (SetUtc1Gate)
+#define GUTC1_NOT_GATED            0x0  // Not gated
+#define GUTC1_GATED                0x1  // Gated
+#define GUTC1_EXT_TC_GATE1         0x2  // Ext. TC Gate 1
+#define GUTC1_EXT_TC_GATE2         0x3  // Ext. TC Gate 2
+#define GUTC1_UTC0_OUT             0x4  // User Timer/Counter 0 out
+
+// User Timer/Counter 2 Gate Select (SetUtc2Gate)
+#define GUTC2_NOT_GATED            0x0  // Not gated
+#define GUTC2_GATED                0x1  // Gated
+#define GUTC2_EXT_TC_GATE1         0x2  // Ext. TC Gate 1
+#define GUTC2_EXT_TC_GATE2         0x3  // Ext. TC Gate 2
+#define GUTC2_UTC1_OUT             0x4  // User Timer/Counter 1 out
+
+// Interrupt Source Masks (SetITMask, ClearITMask, GetITStatus)
+#define IRQM_ADC_FIFO_WRITE        0x0001  // ADC FIFO Write
+#define IRQM_CGT_RESET             0x0002  // Reset CGT
+#define IRQM_CGT_PAUSE             0x0008  // Pause CGT
+#define IRQM_ADC_ABOUT_CNT         0x0010  // About Counter out
+#define IRQM_ADC_DELAY_CNT         0x0020  // Delay Counter out
+#define IRQM_ADC_SAMPLE_CNT	   0x0040  // ADC Sample Counter
+#define IRQM_DAC1_UCNT             0x0080  // DAC1 Update Counter
+#define IRQM_DAC2_UCNT             0x0100  // DAC2 Update Counter
+#define IRQM_UTC1                  0x0200  // User TC1 out
+#define IRQM_UTC1_INV              0x0400  // User TC1 out, inverted
+#define IRQM_UTC2                  0x0800  // User TC2 out
+#define IRQM_DIGITAL_IT            0x1000  // Digital Interrupt
+#define IRQM_EXTERNAL_IT           0x2000  // External Interrupt
+#define IRQM_ETRIG_RISING          0x4000  // External Trigger rising-edge
+#define IRQM_ETRIG_FALLING         0x8000  // External Trigger falling-edge
+
+// DMA Request Sources (LAS0)
+#define DMAS_DISABLED              0x0  // DMA Disabled
+#define DMAS_ADC_SCNT              0x1  // ADC Sample Counter
+#define DMAS_DAC1_UCNT             0x2  // D/A1 Update Counter
+#define DMAS_DAC2_UCNT             0x3  // D/A2 Update Counter
+#define DMAS_UTC1                  0x4  // User TC1 out
+#define DMAS_ADFIFO_HALF_FULL      0x8  // A/D FIFO half full
+#define DMAS_DAC1_FIFO_HALF_EMPTY  0x9  // D/A1 FIFO half empty
+#define DMAS_DAC2_FIFO_HALF_EMPTY  0xA  // D/A2 FIFO half empty
+
+//  PLX Interrupt Control/Status enable masks (LCFG)
+#define PIRQE_PCI          0x00000100	//PCI Interrupt Enable (defaulton)
+#define PIRQE_LINT         0x00000800	//Local Interrupt Enable (bit 11)
+#define PIRQE_DMA0         0x00040000	//DMA Channel 0 Interrupt Enable
+#define PIRQE_DMA1         0x00080000	//DMA Channel 1 Interrupt Enable
+//  PLX Interrupt Control/Status status masks (LCFG)
+#define PIRQS_LINT         0x00008000	//Local Interrupt Active (bit 15)
+#define PIRQS_DMA0         0x00200000	//DMA Channel 0 Interrupt Active
+#define PIRQS_DMA1         0x00400000	//DMA Channel 1 Interrupt Active
+
+// DMA Mode Register (LCFG)
+#define DMA_8BIT         0x0000    // 8 bit wide transfer
+#define DMA_16BIT        0x0001    // 16 bit wide transfer
+#define DMA_32BIT        0x0003    // 32 bit wide transfer
+#define DMA_RDYEN        0x0040    // READY enable
+#define DMA_BTERM        0x0080    // BTERM enable
+#define DMA_LBURST       0x0100    // Local Burst enable
+#define DMA_CHAIN        0x0200    // Chain mode DMA
+#define DMA_ITEN         0x0400    // DMA done IT enable
+#define DMA_LA_CONST     0x0800    // Local Address constant
+#define DMA_DEMAND       0x1000    // Demand mode DMA
+#define DMA_WRANDI       0x2000    // Write and Invalidate mode DMA
+#define DMA_CLRCNT       0x010000  // Clear transfer count
+#define DMA_IT_PCI       0x020000  // DMA IT rerouted to PCI IT
+
+//  DMA Command/Status bits (LCFG)
+#define DMA_ENABLED       0x01		// DMA Channel 0 enabled
+#define DMA_START         0x02		// DMA Channel 0 started
+#define DMA_ABORT         0x04		// Abort DMA Channel 0 transfer
+#define DMA_CLEAR_IT      0x08		// Clear DMA Channel 0 IT
+#define DMA_DONE          0x10		// DMA Channel 0 transfer done
+
+// DMA Transfer Direction
+#define DMAD_FROMCARD      1            // DMA transfer from card to memory
+#define DMAD_TOCARD        0            // DMA transfer from memory to card
+
+// Chained DMA Transfer Direction
+#define DMA_CHAINED_READ       8	// DMA transfer from memory to card
+#define DMA_CHAINED_WRITE      0	// DMA transfer from card to memory
+
+// DMA Local Addresses   (LAS1 offset)
+#define DMALADDR_ADC       0x0000	// A/D FIFO
+#define DMALADDR_HDIN      0x0004	// High Speed Digital Input FIFO
+#define DMALADDR_DAC1      0x0008	// D/A1 FIFO
+#define DMALADDR_DAC2      0x000C	// D/A2 FIFO
+
+// Port 0 compare modes (SetDIO0CompareMode)
+#define DIO_MODE_EVENT     0		// Event Mode
+#define DIO_MODE_MATCH     1		// Match Mode
+
+// Digital Table Enable (Port 1 disable)
+#define DTBL_DISABLE       0		// Enable Digital Table
+#define DTBL_ENABLE        1		// Disable Digital Table
+
+// Sampling Signal for High Speed Digital Input (SetHdinStart)
+#define HDIN_SOFTWARE      0x0		// Software Trigger
+#define HDIN_ADC           0x1		// A/D Conversion Signal
+#define HDIN_UTC0          0x2		// User TC out 0
+#define HDIN_UTC1          0x3		// User TC out 1
+#define HDIN_UTC2          0x4		// User TC out 2
+#define HDIN_EPCLK         0x5		// External Pacer Clock
+#define HDIN_ETRG          0x6		// External Trigger
+
+// Channel Gain Table / Channel Gain Latch
+#define CSC_LATCH          0		// Channel Gain Latch mode
+#define CSC_CGT            1		// Channel Gain Table mode
+
+// Channel Gain Table Pause Enable
+#define CGT_PAUSE_DISABLE  0		// Channel Gain Table Pause Disable
+#define CGT_PAUSE_ENABLE   1		// Channel Gain Table Pause Enable
+
+// DAC output type/range (p63)
+#define AOUT_UNIP5         0		// 0..+5 Volt
+#define AOUT_UNIP10        1		// 0..+10 Volt
+#define AOUT_BIP5          2		// -5..+5 Volt
+#define AOUT_BIP10         3		// -10..+10 Volt
+
+// Ghannel Gain Table field definitions (p61)
+// Gain
+#define GAIN1              0
+#define GAIN2              1
+#define GAIN4              2
+#define GAIN8              3
+#define GAIN16             4
+#define GAIN32             5
+#define GAIN64             6
+#define GAIN128            7
+
+// Input range/polarity
+#define AIN_BIP5           0		// -5..+5 Volt
+#define AIN_BIP10          1		// -10..+10 Volt
+#define AIN_UNIP10         2		// 0..+10 Volt
+
+// non referenced single ended select bit
+#define NRSE_AGND          0		// AGND referenced SE input
+#define NRSE_AINS          1		// AIN SENSE referenced SE input
+
+// single ended vs differential
+#define GND_SE		0		// Single-Ended
+#define GND_DIFF	1		// Differential
-- 
2.26.2