]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
staging: comedi: rtd520 complete the refactor to remove all forward declarations
authorH Hartley Sweeten <hartleys@visionengravers.com>
Thu, 24 May 2012 01:31:48 +0000 (18:31 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 5 Jun 2012 03:38:09 +0000 (20:38 -0700)
Complete the refactor of the rtd520 driver to remove all the
forward declarations.

Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Ian Abbott <abbotti@mev.co.uk>
Cc: Mori Hess <fmhess@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/comedi/drivers/rtd520.c

index d3e2786f07cbe15ea85118b4e3a5b886a5d614ed..84b28b6b99d66f44ea893628ea17ae2e14074a66 100644 (file)
@@ -702,1616 +702,1586 @@ struct rtdPrivate {
 #define RtdDma1Status(dev) \
        readb(devpriv->lcfg+LCFG_DMACSR1)
 
-static int rtd_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
-                       struct comedi_insn *insn, unsigned int *data);
-static int rtd_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
-                       struct comedi_insn *insn, unsigned int *data);
-static int rtd_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
-                       struct comedi_insn *insn, unsigned int *data);
-static int rtd_dio_insn_bits(struct comedi_device *dev,
-                            struct comedi_subdevice *s,
-                            struct comedi_insn *insn, unsigned int *data);
-static int rtd_dio_insn_config(struct comedi_device *dev,
-                              struct comedi_subdevice *s,
-                              struct comedi_insn *insn, unsigned int *data);
-static int rtd_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
-                         struct comedi_cmd *cmd);
-static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
-static int rtd_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
 /*
- * static int rtd_ai_poll(struct comedi_device *dev,
- *                       struct comedi_subdevice *s);
- */
-static int rtd_ns_to_timer(unsigned int *ns, int roundMode);
-static irqreturn_t rtd_interrupt(int irq, void *d);
-static int rtd520_probe_fifo_depth(struct comedi_device *dev);
+  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, int 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 */
+}
 
 /*
- * 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(struct comedi_device *dev, struct comedi_devconfig *it)
-{                              /* board name and options flags */
-       struct comedi_subdevice *s;
-       struct pci_dev *pcidev;
-       int ret;
-       resource_size_t physLas0;       /* configuration */
-       resource_size_t physLas1;       /* data area */
-       resource_size_t physLcfg;       /* PLX9080 */
-#ifdef USE_DMA
-       int index;
-#endif
+  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);
+}
 
-       printk(KERN_INFO "comedi%d: rtd520 attaching.\n", dev->minor);
+/*
+  Convert a single comedi channel-gain entry to a RTD520 table entry
+*/
+static unsigned short rtdConvertChanGain(struct comedi_device *dev,
+                                        unsigned int comediChan, int chanIndex)
+{                              /* index in channel list */
+       unsigned int chan, range, aref;
+       unsigned short r = 0;
 
-#if defined(CONFIG_COMEDI_DEBUG) && defined(USE_DMA)
-       /* You can set this a load time: modprobe comedi comedi_debug=1 */
-       if (0 == comedi_debug)  /* force DMA debug printks */
-               comedi_debug = 1;
-#endif
+       chan = CR_CHAN(comediChan);
+       range = CR_RANGE(comediChan);
+       aref = CR_AREF(comediChan);
 
-       /*
-        * Allocate the private structure area.  alloc_private() is a
-        * convenient macro defined in comedidev.h.
-        */
-       if (alloc_private(dev, sizeof(struct rtdPrivate)) < 0)
-               return -ENOMEM;
+       r |= chan & 0xf;
 
-       /*
-        * Probe the device to determine what device in the series it is.
-        */
-       for (pcidev = pci_get_device(PCI_VENDOR_ID_RTD, PCI_ANY_ID, NULL);
-            pcidev != NULL;
-            pcidev = pci_get_device(PCI_VENDOR_ID_RTD, PCI_ANY_ID, pcidev)) {
-               int i;
+       /* Note: we also setup the channel list bipolar flag array */
+       if (range < thisboard->range10Start) {  /* first batch are +-5 */
+               r |= 0x000;     /* +-5 range */
+               r |= (range & 0x7) << 4;        /* gain */
+               CHAN_ARRAY_SET(devpriv->chanBipolar, chanIndex);
+       } else if (range < thisboard->rangeUniStart) {  /* second batch are +-10 */
+               r |= 0x100;     /* +-10 range */
+               /* gain */
+               r |= ((range - thisboard->range10Start) & 0x7) << 4;
+               CHAN_ARRAY_SET(devpriv->chanBipolar, chanIndex);
+       } else {                /* last batch is +10 */
+               r |= 0x200;     /* +10 range */
+               /* gain */
+               r |= ((range - thisboard->rangeUniStart) & 0x7) << 4;
+               CHAN_ARRAY_CLEAR(devpriv->chanBipolar, chanIndex);
+       }
 
-               if (it->options[0] || it->options[1]) {
-                       if (pcidev->bus->number != it->options[0]
-                           || PCI_SLOT(pcidev->devfn) != it->options[1]) {
-                               continue;
-                       }
-               }
-               for (i = 0; i < ARRAY_SIZE(rtd520Boards); ++i) {
-                       if (pcidev->device == rtd520Boards[i].device_id) {
-                               dev->board_ptr = &rtd520Boards[i];
-                               break;
-                       }
+       switch (aref) {
+       case AREF_GROUND:       /* on-board ground */
+               break;
+
+       case AREF_COMMON:
+               r |= 0x80;      /* ref external analog common */
+               break;
+
+       case AREF_DIFF:
+               r |= 0x400;     /* differential inputs */
+               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(struct 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(dev, list[ii],
+                                                               ii));
                }
-               if (dev->board_ptr)
-                       break;  /* found one */
+       } else {                /* just use the channel gain latch */
+               RtdEnableCGT(dev, 0);   /* disable table, enable latch */
+               RtdWriteCGLatch(dev, rtdConvertChanGain(dev, list[0], 0));
        }
-       if (!pcidev) {
-               if (it->options[0] && it->options[1]) {
-                       printk(KERN_INFO "No RTD card at bus=%d slot=%d.\n",
-                              it->options[0], it->options[1]);
-               } else {
-                       printk(KERN_INFO "No RTD card found.\n");
+}
+
+/* determine fifo size by doing adc conversions until the fifo half
+empty status flag clears */
+static int rtd520_probe_fifo_depth(struct comedi_device *dev)
+{
+       unsigned int chanspec = CR_PACK(0, 0, AREF_GROUND);
+       unsigned i;
+       static const unsigned limit = 0x2000;
+       unsigned fifo_size = 0;
+
+       RtdAdcClearFifo(dev);
+       rtd_load_channelgain_list(dev, 1, &chanspec);
+       RtdAdcConversionSource(dev, 0); /* software */
+       /* convert  samples */
+       for (i = 0; i < limit; ++i) {
+               unsigned fifo_status;
+               /* trigger conversion */
+               RtdAdcStart(dev);
+               udelay(1);
+               fifo_status = RtdFifoStatus(dev);
+               if ((fifo_status & FS_ADC_HEMPTY) == 0) {
+                       fifo_size = 2 * i;
+                       break;
                }
+       }
+       if (i == limit) {
+               printk(KERN_INFO "\ncomedi: %s: failed to probe fifo size.\n",
+                      DRV_NAME);
                return -EIO;
        }
-       devpriv->pci_dev = pcidev;
-       dev->board_name = thisboard->name;
-
-       ret = comedi_pci_enable(pcidev, DRV_NAME);
-       if (ret < 0) {
-               printk(KERN_INFO "Failed to enable PCI device and request regions.\n");
-               return ret;
+       RtdAdcClearFifo(dev);
+       if (fifo_size != 0x400 && fifo_size != 0x2000) {
+               printk
+                   (KERN_INFO "\ncomedi: %s: unexpected fifo size of %i, expected 1024 or 8192.\n",
+                    DRV_NAME, fifo_size);
+               return -EIO;
        }
-       devpriv->got_regions = 1;
-
-       /*
-        * Initialize base addresses
-        */
-       /* Get the physical address from PCI config */
-       physLas0 = pci_resource_start(devpriv->pci_dev, LAS0_PCIINDEX);
-       physLas1 = pci_resource_start(devpriv->pci_dev, LAS1_PCIINDEX);
-       physLcfg = pci_resource_start(devpriv->pci_dev, LCFG_PCIINDEX);
-       /* Now have the kernel map this into memory */
-       /* ASSUME page aligned */
-       devpriv->las0 = ioremap_nocache(physLas0, LAS0_PCISIZE);
-       devpriv->las1 = ioremap_nocache(physLas1, LAS1_PCISIZE);
-       devpriv->lcfg = ioremap_nocache(physLcfg, LCFG_PCISIZE);
+       return fifo_size;
+}
 
-       if (!devpriv->las0 || !devpriv->las1 || !devpriv->lcfg)
-               return -ENOMEM;
+/*
+  "instructions" read/write data in "one-shot" or "software-triggered"
+  mode (simplest case).
+  This doesn't use interrupts.
 
+  Note, we don't do any settling delays.  Use a instruction list to
+  select, delay, then read.
+ */
+static int rtd_ai_rinsn(struct comedi_device *dev,
+                       struct comedi_subdevice *s, struct comedi_insn *insn,
+                       unsigned int *data)
+{
+       int n, ii;
+       int stat;
 
-       DPRINTK("%s: LAS0=%llx, LAS1=%llx, CFG=%llx.\n", dev->board_name,
-               (unsigned long long)physLas0, (unsigned long long)physLas1,
-               (unsigned long long)physLcfg);
-       {                       /* The RTD driver does this */
-               unsigned char pci_latency;
-               u16 revision;
-               /*uint32_t epld_version; */
+       /* clear any old fifo data */
+       RtdAdcClearFifo(dev);
 
-               pci_read_config_word(devpriv->pci_dev, PCI_REVISION_ID,
-                                    &revision);
-               DPRINTK("%s: PCI revision %d.\n", dev->board_name, revision);
+       /* write channel to multiplexer and clear channel gain table */
+       rtd_load_channelgain_list(dev, 1, &insn->chanspec);
 
-               pci_read_config_byte(devpriv->pci_dev,
-                                    PCI_LATENCY_TIMER, &pci_latency);
-               if (pci_latency < 32) {
-                       printk(KERN_INFO "%s: PCI latency changed from %d to %d\n",
-                              dev->board_name, pci_latency, 32);
-                       pci_write_config_byte(devpriv->pci_dev,
-                                             PCI_LATENCY_TIMER, 32);
-               } else {
-                       DPRINTK("rtd520: PCI latency = %d\n", pci_latency);
-               }
+       /* set conversion source */
+       RtdAdcConversionSource(dev, 0); /* software */
 
-               /*
-                * Undocumented EPLD version (doesn't match RTD driver results)
-                */
-               /*DPRINTK ("rtd520: Reading epld from %p\n",
-                  devpriv->las0+0);
-                  epld_version = readl (devpriv->las0+0);
-                  if ((epld_version & 0xF0) >> 4 == 0x0F) {
-                  DPRINTK("rtd520: pre-v8 EPLD. (%x)\n", epld_version);
-                  } else {
-                  DPRINTK("rtd520: EPLD version %x.\n", epld_version >> 4);
-                  } */
+       /* convert n samples */
+       for (n = 0; n < insn->n; n++) {
+               s16 d;
+               /* trigger conversion */
+               RtdAdcStart(dev);
+
+               for (ii = 0; ii < RTD_ADC_TIMEOUT; ++ii) {
+                       stat = RtdFifoStatus(dev);
+                       if (stat & FS_ADC_NOT_EMPTY)    /* 1 -> not empty */
+                               break;
+                       WAIT_QUIETLY;
+               }
+               if (ii >= RTD_ADC_TIMEOUT) {
+                       DPRINTK
+                           ("rtd520: Error: ADC never finished! FifoStatus=0x%x\n",
+                            stat ^ 0x6666);
+                       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 */
+               if (CHAN_ARRAY_TEST(devpriv->chanBipolar, 0))
+                       /* convert to comedi unsigned data */
+                       data[n] = d + 2048;
+               else
+                       data[n] = d;
        }
 
-       /* Show board configuration */
-       printk(KERN_INFO "%s:", dev->board_name);
+       /* return the number of samples read/written */
+       return n;
+}
 
-       /*
-        * Allocate the subdevice structures.  alloc_subdevice() is a
-        * convenient macro defined in comedidev.h.
-        */
-       if (alloc_subdevices(dev, 4) < 0)
-               return -ENOMEM;
+/*
+  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 int ai_read_n(struct comedi_device *dev, struct comedi_subdevice *s,
+                    int count)
+{
+       int ii;
 
-       s = dev->subdevices + 0;
-       dev->read_subdev = s;
-       /* analog input subdevice */
-       s->type = COMEDI_SUBD_AI;
-       s->subdev_flags =
-           SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF | SDF_CMD_READ;
-       s->n_chan = thisboard->aiChans;
-       s->maxdata = (1 << thisboard->aiBits) - 1;
-       if (thisboard->aiMaxGain <= 32)
-               s->range_table = &rtd_ai_7520_range;
-       else
-               s->range_table = &rtd_ai_4520_range;
+       for (ii = 0; ii < count; ii++) {
+               short sample;
+               s16 d;
 
-       s->len_chanlist = RTD_MAX_CHANLIST;     /* devpriv->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->poll = rtd_ai_poll; *//* not ready yet */
+               if (0 == devpriv->aiCount) {    /* done */
+                       d = RtdAdcFifoGet(dev); /* Read N and discard */
+                       continue;
+               }
+#if 0
+               if (0 == (RtdFifoStatus(dev) & FS_ADC_NOT_EMPTY)) {     /* DEBUG */
+                       DPRINTK("comedi: READ OOPS on %d of %d\n", ii + 1,
+                               count);
+                       break;
+               }
+#endif
+               d = RtdAdcFifoGet(dev); /* get 2s comp value */
 
-       s = dev->subdevices + 1;
-       /* analog output subdevice */
-       s->type = COMEDI_SUBD_AO;
-       s->subdev_flags = SDF_WRITABLE;
-       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;
+               d = d >> 3;     /* low 3 bits are marker lines */
+               if (CHAN_ARRAY_TEST(devpriv->chanBipolar, s->async->cur_chan)) {
+                       /* convert to comedi unsigned data */
+                       sample = d + 2048;
+               } else
+                       sample = d;
 
-       s = dev->subdevices + 2;
-       /* digital i/o subdevice */
-       s->type = COMEDI_SUBD_DIO;
-       s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
-       /* we only support port 0 right now.  Ignoring port 1 and user IO */
-       s->n_chan = 8;
-       s->maxdata = 1;
-       s->range_table = &range_digital;
-       s->insn_bits = rtd_dio_insn_bits;
-       s->insn_config = rtd_dio_insn_config;
+               if (!comedi_buf_put(s->async, sample))
+                       return -1;
 
-       /* timer/counter subdevices (not currently supported) */
-       s = dev->subdevices + 3;
-       s->type = COMEDI_SUBD_COUNTER;
-       s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
-       s->n_chan = 3;
-       s->maxdata = 0xffff;
+               if (devpriv->aiCount > 0)       /* < 0, means read forever */
+                       devpriv->aiCount--;
+       }
+       return 0;
+}
 
-       /* initialize board, per RTD spec */
-       /* also, initialize shadow registers */
-       RtdResetBoard(dev);
-       udelay(100);            /* needed? */
-       RtdPlxInterruptWrite(dev, 0);
-       RtdInterruptMask(dev, 0);       /* and sets shadow */
-       RtdInterruptClearMask(dev, ~0); /* and sets shadow */
-       RtdInterruptClear(dev); /* clears bits set by mask */
-       RtdInterruptOverrunClear(dev);
-       RtdClearCGT(dev);
-       RtdAdcClearFifo(dev);
-       RtdDacClearFifo(dev, 0);
-       RtdDacClearFifo(dev, 1);
-       /* clear digital IO fifo */
-       RtdDioStatusWrite(dev, 0);      /* safe state, set shadow */
-       RtdUtcCtrlPut(dev, 0, 0x30);    /* safe state, set shadow */
-       RtdUtcCtrlPut(dev, 1, 0x30);    /* safe state, set shadow */
-       RtdUtcCtrlPut(dev, 2, 0x30);    /* safe state, set shadow */
-       RtdUtcCtrlPut(dev, 3, 0);       /* safe state, set shadow */
-       /* TODO: set user out source ??? */
+/*
+  unknown amout of data is waiting in fifo.
+*/
+static int ai_read_dregs(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+       while (RtdFifoStatus(dev) & FS_ADC_NOT_EMPTY) { /* 1 -> not empty */
+               short sample;
+               s16 d = RtdAdcFifoGet(dev);     /* get 2s comp value */
 
-       /* check if our interrupt is available and get it */
-       ret = request_irq(devpriv->pci_dev->irq, rtd_interrupt,
-                         IRQF_SHARED, DRV_NAME, dev);
+               if (0 == devpriv->aiCount) {    /* done */
+                       continue;       /* read rest */
+               }
 
-       if (ret < 0) {
-               printk("Could not get interrupt! (%u)\n",
-                      devpriv->pci_dev->irq);
-               return ret;
-       }
-       dev->irq = devpriv->pci_dev->irq;
-       printk(KERN_INFO "( irq=%u )", dev->irq);
+               d = d >> 3;     /* low 3 bits are marker lines */
+               if (CHAN_ARRAY_TEST(devpriv->chanBipolar, s->async->cur_chan)) {
+                       /* convert to comedi unsigned data */
+                       sample = d + 2048;
+               } else
+                       sample = d;
 
-       ret = rtd520_probe_fifo_depth(dev);
-       if (ret < 0)
-               return ret;
+               if (!comedi_buf_put(s->async, sample))
+                       return -1;
 
-       devpriv->fifoLen = ret;
-       printk("( fifoLen=%d )", devpriv->fifoLen);
+               if (devpriv->aiCount > 0)       /* < 0, means read forever */
+                       devpriv->aiCount--;
+       }
+       return 0;
+}
 
 #ifdef USE_DMA
-       if (dev->irq > 0) {
-               printk("( DMA buff=%d )\n", DMA_CHAIN_COUNT);
-               /*
-                * The PLX9080 has 2 DMA controllers, but there could be
-                * 4 sources: ADC, digital, DAC1, and DAC2.  Since only the
-                * ADC supports cmd mode right now, this isn't an issue (yet)
-                */
-               devpriv->dma0Offset = 0;
+/*
+  Terminate a DMA transfer and wait for everything to quiet down
+*/
+void abort_dma(struct comedi_device *dev, unsigned int channel)
+{                              /* DMA channel 0, 1 */
+       unsigned long dma_cs_addr;      /* the control/status register */
+       uint8_t status;
+       unsigned int ii;
+       /* unsigned long flags; */
 
-               for (index = 0; index < DMA_CHAIN_COUNT; index++) {
-                       devpriv->dma0Buff[index] =
-                           pci_alloc_consistent(devpriv->pci_dev,
-                                                sizeof(u16) *
-                                                devpriv->fifoLen / 2,
-                                                &devpriv->
-                                                dma0BuffPhysAddr[index]);
-                       if (devpriv->dma0Buff[index] == NULL) {
-                               ret = -ENOMEM;
-                               goto rtd_attach_die_error;
-                       }
-                       /*DPRINTK ("buff[%d] @ %p virtual, %x PCI\n",
-                          index,
-                          devpriv->dma0Buff[index],
-                          devpriv->dma0BuffPhysAddr[index]); */
-               }
+       dma_cs_addr = (unsigned long)devpriv->lcfg
+           + ((channel == 0) ? LCFG_DMACSR0 : LCFG_DMACSR1);
 
-               /*
-                * setup DMA descriptor ring (use cpu_to_le32 for byte
-                * ordering?)
-                */
-               devpriv->dma0Chain =
-                   pci_alloc_consistent(devpriv->pci_dev,
-                                        sizeof(struct plx_dma_desc) *
-                                        DMA_CHAIN_COUNT,
-                                        &devpriv->dma0ChainPhysAddr);
-               for (index = 0; index < DMA_CHAIN_COUNT; index++) {
-                       devpriv->dma0Chain[index].pci_start_addr =
-                           devpriv->dma0BuffPhysAddr[index];
-                       devpriv->dma0Chain[index].local_start_addr =
-                           DMALADDR_ADC;
-                       devpriv->dma0Chain[index].transfer_size =
-                           sizeof(u16) * devpriv->fifoLen / 2;
-                       devpriv->dma0Chain[index].next =
-                           (devpriv->dma0ChainPhysAddr + ((index +
-                                                           1) %
-                                                          (DMA_CHAIN_COUNT))
-                            * sizeof(devpriv->dma0Chain[0]))
-                           | DMA_TRANSFER_BITS;
-                       /*DPRINTK ("ring[%d] @%lx PCI: %x, local: %x, N: 0x%x, next: %x\n",
-                          index,
-                          ((long)devpriv->dma0ChainPhysAddr
-                          + (index * sizeof(devpriv->dma0Chain[0]))),
-                          devpriv->dma0Chain[index].pci_start_addr,
-                          devpriv->dma0Chain[index].local_start_addr,
-                          devpriv->dma0Chain[index].transfer_size,
-                          devpriv->dma0Chain[index].next); */
-               }
+       /*  spinlock for plx dma control/status reg */
+       /* spin_lock_irqsave( &dev->spinlock, flags ); */
 
-               if (devpriv->dma0Chain == NULL) {
-                       ret = -ENOMEM;
-                       goto rtd_attach_die_error;
-               }
+       /*  abort dma transfer if necessary */
+       status = readb(dma_cs_addr);
+       if ((status & PLX_DMA_EN_BIT) == 0) {   /* not enabled (Error?) */
+               DPRINTK("rtd520: AbortDma on non-active channel %d (0x%x)\n",
+                       channel, status);
+               goto abortDmaExit;
+       }
 
-               RtdDma0Mode(dev, DMA_MODE_BITS);
-               /* set DMA trigger source */
-               RtdDma0Source(dev, DMAS_ADFIFO_HALF_FULL);
-       } else {
-               printk(KERN_INFO "( no IRQ->no DMA )");
+       /* wait to make sure done bit is zero (needed?) */
+       for (ii = 0; (status & PLX_DMA_DONE_BIT) && ii < RTD_DMA_TIMEOUT; ii++) {
+               WAIT_QUIETLY;
+               status = readb(dma_cs_addr);
+       }
+       if (status & PLX_DMA_DONE_BIT) {
+               printk("rtd520: Timeout waiting for dma %i done clear\n",
+                      channel);
+               goto abortDmaExit;
        }
-#endif /* USE_DMA */
 
-       if (dev->irq) {         /* enable plx9080 interrupts */
-               RtdPlxInterruptWrite(dev, ICS_PIE | ICS_PLIE);
+       /* disable channel (required) */
+       writeb(0, dma_cs_addr);
+       udelay(1);              /* needed?? */
+       /* set abort bit for channel */
+       writeb(PLX_DMA_ABORT_BIT, dma_cs_addr);
+
+       /*  wait for dma done bit to be set */
+       status = readb(dma_cs_addr);
+       for (ii = 0;
+            (status & PLX_DMA_DONE_BIT) == 0 && ii < RTD_DMA_TIMEOUT; ii++) {
+               status = readb(dma_cs_addr);
+               WAIT_QUIETLY;
+       }
+       if ((status & PLX_DMA_DONE_BIT) == 0) {
+               printk("rtd520: Timeout waiting for dma %i done set\n",
+                      channel);
        }
 
-       printk("\ncomedi%d: rtd520 driver attached.\n", dev->minor);
+abortDmaExit:
+       /* spin_unlock_irqrestore( &dev->spinlock, flags ); */
+}
 
-       return 1;
+/*
+  Process what is in the DMA transfer buffer and pass to comedi
+  Note: this is not re-entrant
+*/
+static int ai_process_dma(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+       int ii, n;
+       s16 *dp;
 
-#if 0
-       /* hit an error, clean up memory and return ret */
-/* rtd_attach_die_error: */
-#ifdef USE_DMA
-       for (index = 0; index < DMA_CHAIN_COUNT; index++) {
-               if (NULL != devpriv->dma0Buff[index]) { /* free buffer memory */
-                       pci_free_consistent(devpriv->pci_dev,
-                                           sizeof(u16) * devpriv->fifoLen / 2,
-                                           devpriv->dma0Buff[index],
-                                           devpriv->dma0BuffPhysAddr[index]);
-                       devpriv->dma0Buff[index] = NULL;
+       if (devpriv->aiCount == 0)      /* transfer already complete */
+               return 0;
+
+       dp = devpriv->dma0Buff[devpriv->dma0Offset];
+       for (ii = 0; ii < devpriv->fifoLen / 2;) {      /* convert samples */
+               short sample;
+
+               if (CHAN_ARRAY_TEST(devpriv->chanBipolar, s->async->cur_chan)) {
+                       sample = (*dp >> 3) + 2048;     /* convert to comedi unsigned data */
+               else
+                       sample = *dp >> 3;      /* low 3 bits are marker lines */
+
+               *dp++ = sample; /* put processed value back */
+
+               if (++s->async->cur_chan >= s->async->cmd.chanlist_len)
+                       s->async->cur_chan = 0;
+
+               ++ii;           /* number ready to transfer */
+               if (devpriv->aiCount > 0) {     /* < 0, means read forever */
+                       if (--devpriv->aiCount == 0) {  /* done */
+                               /*DPRINTK ("rtd520: Final %d samples\n", ii); */
+                               break;
+                       }
                }
        }
-       if (NULL != devpriv->dma0Chain) {
-               pci_free_consistent(devpriv->pci_dev,
-                                   sizeof(struct plx_dma_desc)
-                                   * DMA_CHAIN_COUNT,
-                                   devpriv->dma0Chain,
-                                   devpriv->dma0ChainPhysAddr);
-               devpriv->dma0Chain = NULL;
-       }
-#endif /* USE_DMA */
-       /* subdevices and priv are freed by the core */
-       if (dev->irq) {
-               /* disable interrupt controller */
-               RtdPlxInterruptWrite(dev, RtdPlxInterruptRead(dev)
-                                    & ~(ICS_PLIE | ICS_DMA0_E | ICS_DMA1_E));
-               free_irq(dev->irq, dev);
+
+       /* now pass the whole array to the comedi buffer */
+       dp = devpriv->dma0Buff[devpriv->dma0Offset];
+       n = comedi_buf_write_alloc(s->async, ii * sizeof(s16));
+       if (n < (ii * sizeof(s16))) {   /* any residual is an error */
+               DPRINTK("rtd520:ai_process_dma buffer overflow %d samples!\n",
+                       ii - (n / sizeof(s16)));
+               s->async->events |= COMEDI_CB_ERROR;
+               return -1;
        }
+       comedi_buf_memcpy_to(s->async, 0, dp, n);
+       comedi_buf_write_free(s->async, n);
 
-       /* release all regions that were allocated */
-       if (devpriv->las0)
-               iounmap(devpriv->las0);
+       /*
+        * always at least 1 scan -- 1/2 FIFO is larger than our max scan list
+        */
+       s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
 
-       if (devpriv->las1)
-               iounmap(devpriv->las1);
+       if (++devpriv->dma0Offset >= DMA_CHAIN_COUNT) { /* next buffer */
+               devpriv->dma0Offset = 0;
+       }
+       return 0;
+}
+#endif /* USE_DMA */
 
-       if (devpriv->lcfg)
-               iounmap(devpriv->lcfg);
+/*
+  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 irqreturn_t rtd_interrupt(int irq,      /* interrupt number (ignored) */
+                                void *d)
+{                              /* our data *//* cpu context (ignored) */
+       struct comedi_device *dev = d;  /* must be called "dev" for devpriv */
+       u16 status;
+       u16 fifoStatus;
+       struct comedi_subdevice *s = dev->subdevices + 0;       /* analog in subdevice */
 
-       if (devpriv->pci_dev)
-               pci_dev_put(devpriv->pci_dev);
+       if (!dev->attached)
+               return IRQ_NONE;
 
-       return ret;
-#endif
-}
+       devpriv->intCount++;    /* DEBUG statistics */
 
-static void rtd_detach(struct comedi_device *dev)
-{
+       fifoStatus = RtdFifoStatus(dev);
+       /* check for FIFO full, this automatically halts the ADC! */
+       if (!(fifoStatus & FS_ADC_NOT_FULL)) {  /* 0 -> full */
+               DPRINTK("rtd520: FIFO full! fifo_status=0x%x\n", (fifoStatus ^ 0x6666) & 0x7777);       /* should be all 0s */
+               goto abortTransfer;
+       }
 #ifdef USE_DMA
-       int index;
-#endif
+       if (devpriv->flags & DMA0_ACTIVE) {     /* Check DMA */
+               u32 istatus = RtdPlxInterruptRead(dev);
 
-       if (devpriv) {
-               /* Shut down any board ops by resetting it */
-#ifdef USE_DMA
-               if (devpriv->lcfg) {
-                       RtdDma0Control(dev, 0); /* disable DMA */
-                       RtdDma1Control(dev, 0); /* disable DMA */
-                       RtdPlxInterruptWrite(dev, ICS_PIE | ICS_PLIE);
-               }
-#endif /* USE_DMA */
-               if (devpriv->las0) {
-                       RtdResetBoard(dev);
-                       RtdInterruptMask(dev, 0);
-                       RtdInterruptClearMask(dev, ~0);
-                       RtdInterruptClear(dev); /* clears bits set by mask */
-               }
-#ifdef USE_DMA
-               /* release DMA */
-               for (index = 0; index < DMA_CHAIN_COUNT; index++) {
-                       if (NULL != devpriv->dma0Buff[index]) {
-                               pci_free_consistent(devpriv->pci_dev,
-                                                   sizeof(u16) *
-                                                   devpriv->fifoLen / 2,
-                                                   devpriv->dma0Buff[index],
-                                                   devpriv->
-                                                   dma0BuffPhysAddr[index]);
-                               devpriv->dma0Buff[index] = NULL;
+               if (istatus & ICS_DMA0_A) {
+                       if (ai_process_dma(dev, s) < 0) {
+                               DPRINTK
+                                   ("rtd520: comedi read buffer overflow (DMA) with %ld to go!\n",
+                                    devpriv->aiCount);
+                               RtdDma0Control(dev,
+                                              (devpriv->dma0Control &
+                                               ~PLX_DMA_START_BIT)
+                                              | PLX_CLEAR_DMA_INTR_BIT);
+                               goto abortTransfer;
                        }
+
+                       /*DPRINTK ("rtd520: DMA transfer: %ld to go, istatus %x\n",
+                          devpriv->aiCount, istatus); */
+                       RtdDma0Control(dev,
+                                      (devpriv->
+                                       dma0Control & ~PLX_DMA_START_BIT)
+                                      | PLX_CLEAR_DMA_INTR_BIT);
+                       if (0 == devpriv->aiCount) {    /* counted down */
+                               DPRINTK("rtd520: Samples Done (DMA).\n");
+                               goto transferDone;
+                       }
+                       comedi_event(dev, s);
+               } else {
+                       /*DPRINTK ("rtd520: No DMA ready: istatus %x\n", istatus); */
                }
-               if (NULL != devpriv->dma0Chain) {
-                       pci_free_consistent(devpriv->pci_dev,
-                                           sizeof(struct plx_dma_desc) *
-                                           DMA_CHAIN_COUNT, devpriv->dma0Chain,
-                                           devpriv->dma0ChainPhysAddr);
-                       devpriv->dma0Chain = NULL;
-               }
+       }
+       /* Fall through and check for other interrupt sources */
 #endif /* USE_DMA */
-               if (dev->irq) {
-                       RtdPlxInterruptWrite(dev, RtdPlxInterruptRead(dev)
-                                            & ~(ICS_PLIE | ICS_DMA0_E |
-                                                ICS_DMA1_E));
-                       free_irq(dev->irq, dev);
-               }
-               if (devpriv->las0)
-                       iounmap(devpriv->las0);
-               if (devpriv->las1)
-                       iounmap(devpriv->las1);
-               if (devpriv->lcfg)
-                       iounmap(devpriv->lcfg);
-               if (devpriv->pci_dev) {
-                       if (devpriv->got_regions)
-                               comedi_pci_disable(devpriv->pci_dev);
-                       pci_dev_put(devpriv->pci_dev);
+
+       status = RtdInterruptStatus(dev);
+       /* if interrupt was not caused by our board, or handled above */
+       if (0 == status)
+               return IRQ_HANDLED;
+
+       if (status & IRQM_ADC_ABOUT_CNT) {      /* sample count -> read FIFO */
+               /* since the priority interrupt controller may have queued a sample
+                  counter interrupt, even though we have already finished,
+                  we must handle the possibility that there is no data here */
+               if (!(fifoStatus & FS_ADC_HEMPTY)) {    /* 0 -> 1/2 full */
+                       /*DPRINTK("rtd520: Sample int, reading 1/2FIFO.  fifo_status 0x%x\n",
+                          (fifoStatus ^ 0x6666) & 0x7777); */
+                       if (ai_read_n(dev, s, devpriv->fifoLen / 2) < 0) {
+                               DPRINTK
+                                   ("rtd520: comedi read buffer overflow (1/2FIFO) with %ld to go!\n",
+                                    devpriv->aiCount);
+                               goto abortTransfer;
+                       }
+                       if (0 == devpriv->aiCount) {    /* counted down */
+                               DPRINTK("rtd520: Samples Done (1/2). fifo_status was 0x%x\n", (fifoStatus ^ 0x6666) & 0x7777);  /* should be all 0s */
+                               goto transferDone;
+                       }
+                       comedi_event(dev, s);
+               } else if (devpriv->transCount > 0) {   /* read often */
+                       /*DPRINTK("rtd520: Sample int, reading %d  fifo_status 0x%x\n",
+                          devpriv->transCount, (fifoStatus ^ 0x6666) & 0x7777); */
+                       if (fifoStatus & FS_ADC_NOT_EMPTY) {    /* 1 -> not empty */
+                               if (ai_read_n(dev, s, devpriv->transCount) < 0) {
+                                       DPRINTK
+                                           ("rtd520: comedi read buffer overflow (N) with %ld to go!\n",
+                                            devpriv->aiCount);
+                                       goto abortTransfer;
+                               }
+                               if (0 == devpriv->aiCount) {    /* counted down */
+                                       DPRINTK
+                                           ("rtd520: Samples Done (N). fifo_status was 0x%x\n",
+                                            (fifoStatus ^ 0x6666) & 0x7777);
+                                       goto transferDone;
+                               }
+                               comedi_event(dev, s);
+                       }
+               } else {        /* wait for 1/2 FIFO (old) */
+                       DPRINTK
+                           ("rtd520: Sample int.  Wait for 1/2. fifo_status 0x%x\n",
+                            (fifoStatus ^ 0x6666) & 0x7777);
                }
+       } else {
+               DPRINTK("rtd520: unknown interrupt source!\n");
        }
-}
 
-/*
-  Convert a single comedi channel-gain entry to a RTD520 table entry
-*/
-static unsigned short rtdConvertChanGain(struct comedi_device *dev,
-                                        unsigned int comediChan, int chanIndex)
-{                              /* index in channel list */
-       unsigned int chan, range, aref;
-       unsigned short r = 0;
+       if (0xffff & RtdInterruptOverrunStatus(dev)) {  /* interrupt overrun */
+               DPRINTK
+                   ("rtd520: Interrupt overrun with %ld to go! over_status=0x%x\n",
+                    devpriv->aiCount, 0xffff & RtdInterruptOverrunStatus(dev));
+               goto abortTransfer;
+       }
 
-       chan = CR_CHAN(comediChan);
-       range = CR_RANGE(comediChan);
-       aref = CR_AREF(comediChan);
+       /* clear the interrupt */
+       RtdInterruptClearMask(dev, status);
+       RtdInterruptClear(dev);
+       return IRQ_HANDLED;
 
-       r |= chan & 0xf;
+abortTransfer:
+       RtdAdcClearFifo(dev);   /* clears full flag */
+       s->async->events |= COMEDI_CB_ERROR;
+       devpriv->aiCount = 0;   /* stop and don't transfer any more */
+       /* fall into transferDone */
 
-       /* Note: we also setup the channel list bipolar flag array */
-       if (range < thisboard->range10Start) {  /* first batch are +-5 */
-               r |= 0x000;     /* +-5 range */
-               r |= (range & 0x7) << 4;        /* gain */
-               CHAN_ARRAY_SET(devpriv->chanBipolar, chanIndex);
-       } else if (range < thisboard->rangeUniStart) {  /* second batch are +-10 */
-               r |= 0x100;     /* +-10 range */
-               /* gain */
-               r |= ((range - thisboard->range10Start) & 0x7) << 4;
-               CHAN_ARRAY_SET(devpriv->chanBipolar, chanIndex);
-       } else {                /* last batch is +10 */
-               r |= 0x200;     /* +10 range */
-               /* gain */
-               r |= ((range - thisboard->rangeUniStart) & 0x7) << 4;
-               CHAN_ARRAY_CLEAR(devpriv->chanBipolar, chanIndex);
+transferDone:
+       RtdPacerStopSource(dev, 0);     /* stop on SOFTWARE stop */
+       RtdPacerStop(dev);      /* Stop PACER */
+       RtdAdcConversionSource(dev, 0); /* software trigger only */
+       RtdInterruptMask(dev, 0);       /* mask out SAMPLE */
+#ifdef USE_DMA
+       if (devpriv->flags & DMA0_ACTIVE) {
+               RtdPlxInterruptWrite(dev,       /* disable any more interrupts */
+                                    RtdPlxInterruptRead(dev) & ~ICS_DMA0_E);
+               abort_dma(dev, 0);
+               devpriv->flags &= ~DMA0_ACTIVE;
+               /* if Using DMA, then we should have read everything by now */
+               if (devpriv->aiCount > 0) {
+                       DPRINTK("rtd520: Lost DMA data! %ld remain\n",
+                               devpriv->aiCount);
+               }
        }
+#endif /* USE_DMA */
 
-       switch (aref) {
-       case AREF_GROUND:       /* on-board ground */
-               break;
+       if (devpriv->aiCount > 0) {     /* there shouldn't be anything left */
+               fifoStatus = RtdFifoStatus(dev);
+               DPRINTK("rtd520: Finishing up. %ld remain, fifoStat=%x\n", devpriv->aiCount, (fifoStatus ^ 0x6666) & 0x7777);   /* should read all 0s */
+               ai_read_dregs(dev, s);  /* read anything left in FIFO */
+       }
 
-       case AREF_COMMON:
-               r |= 0x80;      /* ref external analog common */
-               break;
+       s->async->events |= COMEDI_CB_EOA;      /* signal end to comedi */
+       comedi_event(dev, s);
 
-       case AREF_DIFF:
-               r |= 0x400;     /* differential inputs */
-               break;
+       /* clear the interrupt */
+       status = RtdInterruptStatus(dev);
+       RtdInterruptClearMask(dev, status);
+       RtdInterruptClear(dev);
 
-       case AREF_OTHER:        /* ??? */
-               break;
-       }
-       /*printk ("chan=%d r=%d a=%d -> 0x%x\n",
-          chan, range, aref, r); */
-       return r;
+       fifoStatus = RtdFifoStatus(dev);        /* DEBUG */
+       DPRINTK
+           ("rtd520: Acquisition complete. %ld ints, intStat=%x, overStat=%x\n",
+            devpriv->intCount, status,
+            0xffff & RtdInterruptOverrunStatus(dev));
+
+       return IRQ_HANDLED;
 }
 
+#if 0
 /*
-  Setup the channel-gain table from a comedi list
+  return the number of samples available
 */
-static void rtd_load_channelgain_list(struct comedi_device *dev,
-                                     unsigned int n_chan, unsigned int *list)
+static int rtd_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
 {
-       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(dev, list[ii],
-                                                               ii));
-               }
-       } else {                /* just use the channel gain latch */
-               RtdEnableCGT(dev, 0);   /* disable table, enable latch */
-               RtdWriteCGLatch(dev, rtdConvertChanGain(dev, list[0], 0));
-       }
+       /* TODO: This needs to mask interrupts, read_dregs, and then re-enable */
+       /* Not sure what to do if DMA is active */
+       return s->async->buf_write_count - s->async->buf_read_count;
 }
+#endif
 
-/* determine fifo size by doing adc conversions until the fifo half
-empty status flag clears */
-static int rtd520_probe_fifo_depth(struct comedi_device *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(struct comedi_device *dev,
+                         struct comedi_subdevice *s, struct comedi_cmd *cmd)
 {
-       unsigned int chanspec = CR_PACK(0, 0, AREF_GROUND);
-       unsigned i;
-       static const unsigned limit = 0x2000;
-       unsigned fifo_size = 0;
+       int err = 0;
+       int tmp;
 
-       RtdAdcClearFifo(dev);
-       rtd_load_channelgain_list(dev, 1, &chanspec);
-       RtdAdcConversionSource(dev, 0); /* software */
-       /* convert  samples */
-       for (i = 0; i < limit; ++i) {
-               unsigned fifo_status;
-               /* trigger conversion */
-               RtdAdcStart(dev);
-               udelay(1);
-               fifo_status = RtdFifoStatus(dev);
-               if ((fifo_status & FS_ADC_HEMPTY) == 0) {
-                       fifo_size = 2 * i;
-                       break;
-               }
-       }
-       if (i == limit) {
-               printk(KERN_INFO "\ncomedi: %s: failed to probe fifo size.\n",
-                      DRV_NAME);
-               return -EIO;
-       }
-       RtdAdcClearFifo(dev);
-       if (fifo_size != 0x400 && fifo_size != 0x2000) {
-               printk
-                   (KERN_INFO "\ncomedi: %s: unexpected fifo size of %i, expected 1024 or 8192.\n",
-                    DRV_NAME, fifo_size);
-               return -EIO;
-       }
-       return fifo_size;
-}
+       /* step 1: make sure trigger sources are trivially valid */
 
-/*
-  "instructions" read/write data in "one-shot" or "software-triggered"
-  mode (simplest case).
-  This doesn't use interrupts.
+       tmp = cmd->start_src;
+       cmd->start_src &= TRIG_NOW;
+       if (!cmd->start_src || tmp != cmd->start_src)
+               err++;
 
-  Note, we don't do any settling delays.  Use a instruction list to
-  select, delay, then read.
- */
-static int rtd_ai_rinsn(struct comedi_device *dev,
-                       struct comedi_subdevice *s, struct comedi_insn *insn,
-                       unsigned int *data)
-{
-       int n, ii;
-       int stat;
+       tmp = cmd->scan_begin_src;
+       cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
+       if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+               err++;
 
-       /* clear any old fifo data */
-       RtdAdcClearFifo(dev);
 
-       /* write channel to multiplexer and clear channel gain table */
-       rtd_load_channelgain_list(dev, 1, &insn->chanspec);
+       tmp = cmd->convert_src;
+       cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
+       if (!cmd->convert_src || tmp != cmd->convert_src)
+               err++;
 
-       /* set conversion source */
-       RtdAdcConversionSource(dev, 0); /* software */
 
-       /* convert n samples */
-       for (n = 0; n < insn->n; n++) {
-               s16 d;
-               /* trigger conversion */
-               RtdAdcStart(dev);
+       tmp = cmd->scan_end_src;
+       cmd->scan_end_src &= TRIG_COUNT;
+       if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+               err++;
 
-               for (ii = 0; ii < RTD_ADC_TIMEOUT; ++ii) {
-                       stat = RtdFifoStatus(dev);
-                       if (stat & FS_ADC_NOT_EMPTY)    /* 1 -> not empty */
-                               break;
-                       WAIT_QUIETLY;
-               }
-               if (ii >= RTD_ADC_TIMEOUT) {
-                       DPRINTK
-                           ("rtd520: Error: ADC never finished! FifoStatus=0x%x\n",
-                            stat ^ 0x6666);
-                       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 */
-               if (CHAN_ARRAY_TEST(devpriv->chanBipolar, 0))
-                       /* convert to comedi unsigned data */
-                       data[n] = d + 2048;
-               else
-                       data[n] = d;
+       tmp = cmd->stop_src;
+       cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+       if (!cmd->stop_src || tmp != cmd->stop_src)
+               err++;
+
+
+       if (err)
+               return 1;
+
+       /* step 2: make sure trigger sources are unique
+          and mutually compatible */
+       /* note that mutual compatibility 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++;
 
-       /* return the number of samples read/written */
-       return n;
-}
+       if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+               err++;
 
-/*
-  Get what we know is there.... Fast!
-  This uses 1/2 the bus cycles of read_dregs (below).
+       if (err)
+               return 2;
 
-  The manual claims that we can do a lword read, but it doesn't work here.
-*/
-static int ai_read_n(struct comedi_device *dev, struct comedi_subdevice *s,
-                    int count)
-{
-       int ii;
+       /* step 3: make sure arguments are trivially compatible */
 
-       for (ii = 0; ii < count; ii++) {
-               short sample;
-               s16 d;
+       if (cmd->start_arg != 0) {
+               cmd->start_arg = 0;
+               err++;
+       }
 
-               if (0 == devpriv->aiCount) {    /* done */
-                       d = RtdAdcFifoGet(dev); /* Read N and discard */
-                       continue;
+       if (cmd->scan_begin_src == TRIG_TIMER) {
+               /* Note: these are time periods, not actual rates */
+               if (1 == cmd->chanlist_len) {   /* no scanning */
+                       if (cmd->scan_begin_arg < RTD_MAX_SPEED_1) {
+                               cmd->scan_begin_arg = RTD_MAX_SPEED_1;
+                               rtd_ns_to_timer(&cmd->scan_begin_arg,
+                                               TRIG_ROUND_UP);
+                               err++;
+                       }
+                       if (cmd->scan_begin_arg > RTD_MIN_SPEED_1) {
+                               cmd->scan_begin_arg = RTD_MIN_SPEED_1;
+                               rtd_ns_to_timer(&cmd->scan_begin_arg,
+                                               TRIG_ROUND_DOWN);
+                               err++;
+                       }
+               } else {
+                       if (cmd->scan_begin_arg < RTD_MAX_SPEED) {
+                               cmd->scan_begin_arg = RTD_MAX_SPEED;
+                               rtd_ns_to_timer(&cmd->scan_begin_arg,
+                                               TRIG_ROUND_UP);
+                               err++;
+                       }
+                       if (cmd->scan_begin_arg > RTD_MIN_SPEED) {
+                               cmd->scan_begin_arg = RTD_MIN_SPEED;
+                               rtd_ns_to_timer(&cmd->scan_begin_arg,
+                                               TRIG_ROUND_DOWN);
+                               err++;
+                       }
                }
-#if 0
-               if (0 == (RtdFifoStatus(dev) & FS_ADC_NOT_EMPTY)) {     /* DEBUG */
-                       DPRINTK("comedi: READ OOPS on %d of %d\n", ii + 1,
-                               count);
-                       break;
+       } 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;
+                       err++;
+               }
+       }
+       if (cmd->convert_src == TRIG_TIMER) {
+               if (1 == cmd->chanlist_len) {   /* no scanning */
+                       if (cmd->convert_arg < RTD_MAX_SPEED_1) {
+                               cmd->convert_arg = RTD_MAX_SPEED_1;
+                               rtd_ns_to_timer(&cmd->convert_arg,
+                                               TRIG_ROUND_UP);
+                               err++;
+                       }
+                       if (cmd->convert_arg > RTD_MIN_SPEED_1) {
+                               cmd->convert_arg = RTD_MIN_SPEED_1;
+                               rtd_ns_to_timer(&cmd->convert_arg,
+                                               TRIG_ROUND_DOWN);
+                               err++;
+                       }
+               } else {
+                       if (cmd->convert_arg < RTD_MAX_SPEED) {
+                               cmd->convert_arg = RTD_MAX_SPEED;
+                               rtd_ns_to_timer(&cmd->convert_arg,
+                                               TRIG_ROUND_UP);
+                               err++;
+                       }
+                       if (cmd->convert_arg > RTD_MIN_SPEED) {
+                               cmd->convert_arg = RTD_MIN_SPEED;
+                               rtd_ns_to_timer(&cmd->convert_arg,
+                                               TRIG_ROUND_DOWN);
+                               err++;
+                       }
+               }
+       } else {
+               /* external trigger */
+               /* see above */
+               if (cmd->convert_arg > 9) {
+                       cmd->convert_arg = 9;
+                       err++;
                }
+       }
+
+#if 0
+       if (cmd->scan_end_arg != cmd->chanlist_len) {
+               cmd->scan_end_arg = cmd->chanlist_len;
+               err++;
+       }
 #endif
-               d = RtdAdcFifoGet(dev); /* get 2s comp value */
+       if (cmd->stop_src == TRIG_COUNT) {
+               /* TODO check for rounding error due to counter wrap */
 
-               d = d >> 3;     /* low 3 bits are marker lines */
-               if (CHAN_ARRAY_TEST(devpriv->chanBipolar, s->async->cur_chan)) {
-                       /* convert to comedi unsigned data */
-                       sample = d + 2048;
-               } else
-                       sample = d;
+       } else {
+               /* TRIG_NONE */
+               if (cmd->stop_arg != 0) {
+                       cmd->stop_arg = 0;
+                       err++;
+               }
+       }
 
-               if (!comedi_buf_put(s->async, sample))
-                       return -1;
+       if (err)
+               return 3;
 
-               if (devpriv->aiCount > 0)       /* < 0, means read forever */
-                       devpriv->aiCount--;
+
+       /* step 4: fix up any arguments */
+
+       if (cmd->chanlist_len > RTD_MAX_CHANLIST) {
+               cmd->chanlist_len = RTD_MAX_CHANLIST;
+               err++;
        }
-       return 0;
-}
+       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++;
 
-/*
-  unknown amout of data is waiting in fifo.
-*/
-static int ai_read_dregs(struct comedi_device *dev, struct comedi_subdevice *s)
-{
-       while (RtdFifoStatus(dev) & FS_ADC_NOT_EMPTY) { /* 1 -> not empty */
-               short sample;
-               s16 d = RtdAdcFifoGet(dev);     /* get 2s comp value */
+       }
+       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 (0 == devpriv->aiCount) {    /* done */
-                       continue;       /* read rest */
+               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++;
                }
+       }
 
-               d = d >> 3;     /* low 3 bits are marker lines */
-               if (CHAN_ARRAY_TEST(devpriv->chanBipolar, s->async->cur_chan)) {
-                       /* convert to comedi unsigned data */
-                       sample = d + 2048;
-               } else
-                       sample = d;
-
-               if (!comedi_buf_put(s->async, sample))
-                       return -1;
+       if (err)
+               return 4;
 
-               if (devpriv->aiCount > 0)       /* < 0, means read forever */
-                       devpriv->aiCount--;
-       }
        return 0;
 }
 
-#ifdef USE_DMA
 /*
-  Terminate a DMA transfer and wait for everything to quiet down
+  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.
 */
-void abort_dma(struct comedi_device *dev, unsigned int channel)
-{                              /* DMA channel 0, 1 */
-       unsigned long dma_cs_addr;      /* the control/status register */
-       uint8_t status;
-       unsigned int ii;
-       /* unsigned long flags; */
-
-       dma_cs_addr = (unsigned long)devpriv->lcfg
-           + ((channel == 0) ? LCFG_DMACSR0 : LCFG_DMACSR1);
-
-       /*  spinlock for plx dma control/status reg */
-       /* spin_lock_irqsave( &dev->spinlock, flags ); */
+static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+       struct comedi_cmd *cmd = &s->async->cmd;
+       int timer;
 
-       /*  abort dma transfer if necessary */
-       status = readb(dma_cs_addr);
-       if ((status & PLX_DMA_EN_BIT) == 0) {   /* not enabled (Error?) */
-               DPRINTK("rtd520: AbortDma on non-active channel %d (0x%x)\n",
-                       channel, status);
-               goto abortDmaExit;
+       /* stop anything currently running */
+       RtdPacerStopSource(dev, 0);     /* stop on SOFTWARE stop */
+       RtdPacerStop(dev);      /* make sure PACER is stopped */
+       RtdAdcConversionSource(dev, 0); /* software trigger only */
+       RtdInterruptMask(dev, 0);
+#ifdef USE_DMA
+       if (devpriv->flags & DMA0_ACTIVE) {     /* cancel anything running */
+               RtdPlxInterruptWrite(dev,       /* disable any more interrupts */
+                                    RtdPlxInterruptRead(dev) & ~ICS_DMA0_E);
+               abort_dma(dev, 0);
+               devpriv->flags &= ~DMA0_ACTIVE;
+               if (RtdPlxInterruptRead(dev) & ICS_DMA0_A) {    /*clear pending int */
+                       RtdDma0Control(dev, PLX_CLEAR_DMA_INTR_BIT);
+               }
        }
+       RtdDma0Reset(dev);      /* reset onboard state */
+#endif /* USE_DMA */
+       RtdAdcClearFifo(dev);   /* clear any old data */
+       RtdInterruptOverrunClear(dev);
+       devpriv->intCount = 0;
 
-       /* wait to make sure done bit is zero (needed?) */
-       for (ii = 0; (status & PLX_DMA_DONE_BIT) && ii < RTD_DMA_TIMEOUT; ii++) {
-               WAIT_QUIETLY;
-               status = readb(dma_cs_addr);
-       }
-       if (status & PLX_DMA_DONE_BIT) {
-               printk("rtd520: Timeout waiting for dma %i done clear\n",
-                      channel);
-               goto abortDmaExit;
+       if (!dev->irq) {        /* we need interrupts for this */
+               DPRINTK("rtd520: ERROR! No interrupt available!\n");
+               return -ENXIO;
        }
 
-       /* disable channel (required) */
-       writeb(0, dma_cs_addr);
-       udelay(1);              /* needed?? */
-       /* set abort bit for channel */
-       writeb(PLX_DMA_ABORT_BIT, dma_cs_addr);
+       /* start configuration */
+       /* load channel list and reset CGT */
+       rtd_load_channelgain_list(dev, cmd->chanlist_len, cmd->chanlist);
 
-       /*  wait for dma done bit to be set */
-       status = readb(dma_cs_addr);
-       for (ii = 0;
-            (status & PLX_DMA_DONE_BIT) == 0 && ii < RTD_DMA_TIMEOUT; ii++) {
-               status = readb(dma_cs_addr);
-               WAIT_QUIETLY;
+       /* setup the common case and override if needed */
+       if (cmd->chanlist_len > 1) {
+               /*DPRINTK ("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 */
+               /*DPRINTK ("rtd520: single channel setup\n"); */
+               RtdPacerStartSource(dev, 0);    /* software triggers pacer */
+               RtdAdcConversionSource(dev, 1); /* PACER triggers ADC */
        }
-       if ((status & PLX_DMA_DONE_BIT) == 0) {
-               printk("rtd520: Timeout waiting for dma %i done set\n",
-                      channel);
+       RtdAboutCounter(dev, devpriv->fifoLen / 2 - 1); /* 1/2 FIFO */
+
+       if (TRIG_TIMER == cmd->scan_begin_src) {
+               /* scan_begin_arg is in nanoseconds */
+               /* find out how many samples to wait before transferring */
+               if (cmd->flags & TRIG_WAKE_EOS) {
+                       /* this may generate un-sustainable interrupt rates */
+                       /* the application is responsible for doing the right thing */
+                       devpriv->transCount = cmd->chanlist_len;
+                       devpriv->flags |= SEND_EOS;
+               } else {
+                       /* arrange to transfer data periodically */
+                       devpriv->transCount
+                           =
+                           (TRANS_TARGET_PERIOD * cmd->chanlist_len) /
+                           cmd->scan_begin_arg;
+                       if (devpriv->transCount < cmd->chanlist_len) {
+                               /* transfer after each scan (and avoid 0) */
+                               devpriv->transCount = cmd->chanlist_len;
+                       } else {        /* make a multiple of scan length */
+                               devpriv->transCount =
+                                   (devpriv->transCount +
+                                    cmd->chanlist_len - 1)
+                                   / cmd->chanlist_len;
+                               devpriv->transCount *= cmd->chanlist_len;
+                       }
+                       devpriv->flags |= SEND_EOS;
+               }
+               if (devpriv->transCount >= (devpriv->fifoLen / 2)) {
+                       /* out of counter range, use 1/2 fifo instead */
+                       devpriv->transCount = 0;
+                       devpriv->flags &= ~SEND_EOS;
+               } else {
+                       /* interrupt for each transfer */
+                       RtdAboutCounter(dev, devpriv->transCount - 1);
+               }
+
+               DPRINTK
+                   ("rtd520: scanLen=%d transferCount=%d fifoLen=%d\n  scanTime(ns)=%d flags=0x%x\n",
+                    cmd->chanlist_len, devpriv->transCount, devpriv->fifoLen,
+                    cmd->scan_begin_arg, devpriv->flags);
+       } else {                /* unknown timing, just use 1/2 FIFO */
+               devpriv->transCount = 0;
+               devpriv->flags &= ~SEND_EOS;
        }
+       RtdPacerClockSource(dev, 1);    /* use INTERNAL 8Mhz clock source */
+       RtdAboutStopEnable(dev, 1);     /* just interrupt, dont stop */
 
-abortDmaExit:
-       /* spin_unlock_irqrestore( &dev->spinlock, flags ); */
-}
+       /* BUG??? these look like enumerated values, but they are bit fields */
 
-/*
-  Process what is in the DMA transfer buffer and pass to comedi
-  Note: this is not re-entrant
-*/
-static int ai_process_dma(struct comedi_device *dev, struct comedi_subdevice *s)
-{
-       int ii, n;
-       s16 *dp;
+       /* First, setup when to stop */
+       switch (cmd->stop_src) {
+       case TRIG_COUNT:        /* stop after N scans */
+               devpriv->aiCount = cmd->stop_arg * cmd->chanlist_len;
+               if ((devpriv->transCount > 0)
+                   && (devpriv->transCount > devpriv->aiCount)) {
+                       devpriv->transCount = devpriv->aiCount;
+               }
+               break;
 
-       if (devpriv->aiCount == 0)      /* transfer already complete */
-               return 0;
+       case TRIG_NONE: /* stop when cancel is called */
+               devpriv->aiCount = -1;  /* read forever */
+               break;
 
-       dp = devpriv->dma0Buff[devpriv->dma0Offset];
-       for (ii = 0; ii < devpriv->fifoLen / 2;) {      /* convert samples */
-               short sample;
+       default:
+               DPRINTK("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 */
+               /*DPRINTK ("rtd520: loading %d into pacer\n", timer); */
+               RtdPacerCounter(dev, timer);
 
-               if (CHAN_ARRAY_TEST(devpriv->chanBipolar, s->async->cur_chan)) {
-                       sample = (*dp >> 3) + 2048;     /* convert to comedi unsigned data */
-               else
-                       sample = *dp >> 3;      /* low 3 bits are marker lines */
+               break;
 
-               *dp++ = sample; /* put processed value back */
+       case TRIG_EXT:
+               RtdPacerStartSource(dev, 1);    /* EXTERNALy trigger pacer */
+               break;
 
-               if (++s->async->cur_chan >= s->async->cmd.chanlist_len)
-                       s->async->cur_chan = 0;
+       default:
+               DPRINTK("rtd520: Warning! ignoring scan_begin_src mode %d\n",
+                       cmd->scan_begin_src);
+       }
 
-               ++ii;           /* number ready to transfer */
-               if (devpriv->aiCount > 0) {     /* < 0, means read forever */
-                       if (--devpriv->aiCount == 0) {  /* done */
-                               /*DPRINTK ("rtd520: Final %d samples\n", ii); */
-                               break;
-                       }
+       /* Sample timing within a scan */
+       switch (cmd->convert_src) {
+       case TRIG_TIMER:        /* periodic */
+               if (cmd->chanlist_len > 1) {    /* only needed for multi-channel */
+                       timer = rtd_ns_to_timer(&cmd->convert_arg,
+                                               TRIG_ROUND_NEAREST);
+                       /* setup BURST clock */
+                       /*DPRINTK ("rtd520: loading %d into burst\n", timer); */
+                       RtdBurstCounter(dev, timer);
                }
-       }
 
-       /* now pass the whole array to the comedi buffer */
-       dp = devpriv->dma0Buff[devpriv->dma0Offset];
-       n = comedi_buf_write_alloc(s->async, ii * sizeof(s16));
-       if (n < (ii * sizeof(s16))) {   /* any residual is an error */
-               DPRINTK("rtd520:ai_process_dma buffer overflow %d samples!\n",
-                       ii - (n / sizeof(s16)));
-               s->async->events |= COMEDI_CB_ERROR;
-               return -1;
-       }
-       comedi_buf_memcpy_to(s->async, 0, dp, n);
-       comedi_buf_write_free(s->async, n);
+               break;
 
-       /*
-        * always at least 1 scan -- 1/2 FIFO is larger than our max scan list
-        */
-       s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
+       case TRIG_EXT:          /* external */
+               RtdBurstStartSource(dev, 2);    /* EXTERNALy trigger burst */
+               break;
 
-       if (++devpriv->dma0Offset >= DMA_CHAIN_COUNT) { /* next buffer */
-               devpriv->dma0Offset = 0;
+       default:
+               DPRINTK("rtd520: Warning! ignoring convert_src mode %d\n",
+                       cmd->convert_src);
        }
-       return 0;
-}
-#endif /* USE_DMA */
-
-/*
-  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 irqreturn_t rtd_interrupt(int irq,      /* interrupt number (ignored) */
-                                void *d)
-{                              /* our data *//* cpu context (ignored) */
-       struct comedi_device *dev = d;  /* must be called "dev" for devpriv */
-       u16 status;
-       u16 fifoStatus;
-       struct comedi_subdevice *s = dev->subdevices + 0;       /* analog in subdevice */
-
-       if (!dev->attached)
-               return IRQ_NONE;
+       /* end configuration */
 
-       devpriv->intCount++;    /* DEBUG statistics */
+       /* This doesn't seem to work.  There is no way to clear an interrupt
+          that the priority controller has queued! */
+       RtdInterruptClearMask(dev, ~0); /* clear any existing flags */
+       RtdInterruptClear(dev);
 
-       fifoStatus = RtdFifoStatus(dev);
-       /* check for FIFO full, this automatically halts the ADC! */
-       if (!(fifoStatus & FS_ADC_NOT_FULL)) {  /* 0 -> full */
-               DPRINTK("rtd520: FIFO full! fifo_status=0x%x\n", (fifoStatus ^ 0x6666) & 0x7777);       /* should be all 0s */
-               goto abortTransfer;
-       }
+       /* TODO: allow multiple interrupt sources */
+       if (devpriv->transCount > 0) {  /* transfer every N samples */
+               RtdInterruptMask(dev, IRQM_ADC_ABOUT_CNT);
+               DPRINTK("rtd520: Transferring every %d\n", devpriv->transCount);
+       } else {                /* 1/2 FIFO transfers */
 #ifdef USE_DMA
-       if (devpriv->flags & DMA0_ACTIVE) {     /* Check DMA */
-               u32 istatus = RtdPlxInterruptRead(dev);
+               devpriv->flags |= DMA0_ACTIVE;
 
-               if (istatus & ICS_DMA0_A) {
-                       if (ai_process_dma(dev, s) < 0) {
-                               DPRINTK
-                                   ("rtd520: comedi read buffer overflow (DMA) with %ld to go!\n",
-                                    devpriv->aiCount);
-                               RtdDma0Control(dev,
-                                              (devpriv->dma0Control &
-                                               ~PLX_DMA_START_BIT)
-                                              | PLX_CLEAR_DMA_INTR_BIT);
-                               goto abortTransfer;
-                       }
+               /* point to first transfer in ring */
+               devpriv->dma0Offset = 0;
+               RtdDma0Mode(dev, DMA_MODE_BITS);
+               RtdDma0Next(dev,        /* point to first block */
+                           devpriv->dma0Chain[DMA_CHAIN_COUNT - 1].next);
+               RtdDma0Source(dev, DMAS_ADFIFO_HALF_FULL);      /* set DMA trigger source */
 
-                       /*DPRINTK ("rtd520: DMA transfer: %ld to go, istatus %x\n",
-                          devpriv->aiCount, istatus); */
-                       RtdDma0Control(dev,
-                                      (devpriv->
-                                       dma0Control & ~PLX_DMA_START_BIT)
-                                      | PLX_CLEAR_DMA_INTR_BIT);
-                       if (0 == devpriv->aiCount) {    /* counted down */
-                               DPRINTK("rtd520: Samples Done (DMA).\n");
-                               goto transferDone;
-                       }
-                       comedi_event(dev, s);
-               } else {
-                       /*DPRINTK ("rtd520: No DMA ready: istatus %x\n", istatus); */
-               }
-       }
-       /* Fall through and check for other interrupt sources */
+               RtdPlxInterruptWrite(dev,       /* enable interrupt */
+                                    RtdPlxInterruptRead(dev) | ICS_DMA0_E);
+               /* Must be 2 steps.  See PLX app note about "Starting a DMA transfer" */
+               RtdDma0Control(dev, PLX_DMA_EN_BIT);    /* enable DMA (clear INTR?) */
+               RtdDma0Control(dev, PLX_DMA_EN_BIT | PLX_DMA_START_BIT);        /*start DMA */
+               DPRINTK("rtd520: Using DMA0 transfers. plxInt %x RtdInt %x\n",
+                       RtdPlxInterruptRead(dev), devpriv->intMask);
+#else /* USE_DMA */
+               RtdInterruptMask(dev, IRQM_ADC_ABOUT_CNT);
+               DPRINTK("rtd520: Transferring every 1/2 FIFO\n");
 #endif /* USE_DMA */
-
-       status = RtdInterruptStatus(dev);
-       /* if interrupt was not caused by our board, or handled above */
-       if (0 == status)
-               return IRQ_HANDLED;
-
-       if (status & IRQM_ADC_ABOUT_CNT) {      /* sample count -> read FIFO */
-               /* since the priority interrupt controller may have queued a sample
-                  counter interrupt, even though we have already finished,
-                  we must handle the possibility that there is no data here */
-               if (!(fifoStatus & FS_ADC_HEMPTY)) {    /* 0 -> 1/2 full */
-                       /*DPRINTK("rtd520: Sample int, reading 1/2FIFO.  fifo_status 0x%x\n",
-                          (fifoStatus ^ 0x6666) & 0x7777); */
-                       if (ai_read_n(dev, s, devpriv->fifoLen / 2) < 0) {
-                               DPRINTK
-                                   ("rtd520: comedi read buffer overflow (1/2FIFO) with %ld to go!\n",
-                                    devpriv->aiCount);
-                               goto abortTransfer;
-                       }
-                       if (0 == devpriv->aiCount) {    /* counted down */
-                               DPRINTK("rtd520: Samples Done (1/2). fifo_status was 0x%x\n", (fifoStatus ^ 0x6666) & 0x7777);  /* should be all 0s */
-                               goto transferDone;
-                       }
-                       comedi_event(dev, s);
-               } else if (devpriv->transCount > 0) {   /* read often */
-                       /*DPRINTK("rtd520: Sample int, reading %d  fifo_status 0x%x\n",
-                          devpriv->transCount, (fifoStatus ^ 0x6666) & 0x7777); */
-                       if (fifoStatus & FS_ADC_NOT_EMPTY) {    /* 1 -> not empty */
-                               if (ai_read_n(dev, s, devpriv->transCount) < 0) {
-                                       DPRINTK
-                                           ("rtd520: comedi read buffer overflow (N) with %ld to go!\n",
-                                            devpriv->aiCount);
-                                       goto abortTransfer;
-                               }
-                               if (0 == devpriv->aiCount) {    /* counted down */
-                                       DPRINTK
-                                           ("rtd520: Samples Done (N). fifo_status was 0x%x\n",
-                                            (fifoStatus ^ 0x6666) & 0x7777);
-                                       goto transferDone;
-                               }
-                               comedi_event(dev, s);
-                       }
-               } else {        /* wait for 1/2 FIFO (old) */
-                       DPRINTK
-                           ("rtd520: Sample int.  Wait for 1/2. fifo_status 0x%x\n",
-                            (fifoStatus ^ 0x6666) & 0x7777);
-               }
-       } else {
-               DPRINTK("rtd520: unknown interrupt source!\n");
-       }
-
-       if (0xffff & RtdInterruptOverrunStatus(dev)) {  /* interrupt overrun */
-               DPRINTK
-                   ("rtd520: Interrupt overrun with %ld to go! over_status=0x%x\n",
-                    devpriv->aiCount, 0xffff & RtdInterruptOverrunStatus(dev));
-               goto abortTransfer;
        }
 
-       /* clear the interrupt */
-       RtdInterruptClearMask(dev, status);
-       RtdInterruptClear(dev);
-       return IRQ_HANDLED;
+       /* BUG: start_src is ASSUMED to be TRIG_NOW */
+       /* BUG? it seems like things are running before the "start" */
+       RtdPacerStart(dev);     /* Start PACER */
+       return 0;
+}
 
-abortTransfer:
-       RtdAdcClearFifo(dev);   /* clears full flag */
-       s->async->events |= COMEDI_CB_ERROR;
-       devpriv->aiCount = 0;   /* stop and don't transfer any more */
-       /* fall into transferDone */
+/*
+  Stop a running data acquisition.
+*/
+static int rtd_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+       u16 status;
 
-transferDone:
        RtdPacerStopSource(dev, 0);     /* stop on SOFTWARE stop */
        RtdPacerStop(dev);      /* Stop PACER */
        RtdAdcConversionSource(dev, 0); /* software trigger only */
-       RtdInterruptMask(dev, 0);       /* mask out SAMPLE */
+       RtdInterruptMask(dev, 0);
+       devpriv->aiCount = 0;   /* stop and don't transfer any more */
 #ifdef USE_DMA
        if (devpriv->flags & DMA0_ACTIVE) {
                RtdPlxInterruptWrite(dev,       /* disable any more interrupts */
                                     RtdPlxInterruptRead(dev) & ~ICS_DMA0_E);
                abort_dma(dev, 0);
                devpriv->flags &= ~DMA0_ACTIVE;
-               /* if Using DMA, then we should have read everything by now */
-               if (devpriv->aiCount > 0) {
-                       DPRINTK("rtd520: Lost DMA data! %ld remain\n",
-                               devpriv->aiCount);
-               }
        }
 #endif /* USE_DMA */
-
-       if (devpriv->aiCount > 0) {     /* there shouldn't be anything left */
-               fifoStatus = RtdFifoStatus(dev);
-               DPRINTK("rtd520: Finishing up. %ld remain, fifoStat=%x\n", devpriv->aiCount, (fifoStatus ^ 0x6666) & 0x7777);   /* should read all 0s */
-               ai_read_dregs(dev, s);  /* read anything left in FIFO */
-       }
-
-       s->async->events |= COMEDI_CB_EOA;      /* signal end to comedi */
-       comedi_event(dev, s);
-
-       /* clear the interrupt */
        status = RtdInterruptStatus(dev);
-       RtdInterruptClearMask(dev, status);
-       RtdInterruptClear(dev);
-
-       fifoStatus = RtdFifoStatus(dev);        /* DEBUG */
        DPRINTK
-           ("rtd520: Acquisition complete. %ld ints, intStat=%x, overStat=%x\n",
-            devpriv->intCount, status,
-            0xffff & RtdInterruptOverrunStatus(dev));
-
-       return IRQ_HANDLED;
-}
-
-#if 0
-/*
-  return the number of samples available
-*/
-static int rtd_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
-{
-       /* TODO: This needs to mask interrupts, read_dregs, and then re-enable */
-       /* Not sure what to do if DMA is active */
-       return s->async->buf_write_count - s->async->buf_read_count;
+           ("rtd520: Acquisition canceled. %ld ints, intStat=%x, overStat=%x\n",
+            devpriv->intCount, status,
+            0xffff & RtdInterruptOverrunStatus(dev));
+       return 0;
 }
-#endif
 
 /*
-  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.
+  Output one (or more) analog values to a single port as fast as possible.
 */
-
-static int rtd_ai_cmdtest(struct comedi_device *dev,
-                         struct comedi_subdevice *s, struct comedi_cmd *cmd)
+static int rtd_ao_winsn(struct comedi_device *dev,
+                       struct comedi_subdevice *s, struct comedi_insn *insn,
+                       unsigned int *data)
 {
-       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)
-               err++;
-
-       tmp = cmd->scan_begin_src;
-       cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
-       if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
-               err++;
-
-
-       tmp = cmd->convert_src;
-       cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
-       if (!cmd->convert_src || tmp != 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)
-               err++;
-
-
-       tmp = cmd->stop_src;
-       cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
-       if (!cmd->stop_src || tmp != cmd->stop_src)
-               err++;
+       int i;
+       int chan = CR_CHAN(insn->chanspec);
+       int range = CR_RANGE(insn->chanspec);
 
+       /* Configure the output range (table index matches the range values) */
+       RtdDacRange(dev, chan, range);
 
-       if (err)
-               return 1;
+       /* 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) {
+               int val = data[i] << 3;
+               int stat = 0;   /* initialize to avoid bogus warning */
+               int ii;
 
-       /* step 2: make sure trigger sources are unique
-          and mutually compatible */
-       /* note that mutual compatibility 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++;
+               /* VERIFY: comedi range and offset conversions */
 
-       if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
-               err++;
+               if ((range > 1) /* bipolar */
+                   && (data[i] < 2048)) {
+                       /* offset and sign extend */
+                       val = (((int)data[i]) - 2048) << 3;
+               } else {        /* unipolor */
+                       val = data[i] << 3;
+               }
 
-       if (err)
-               return 2;
+               DPRINTK
+                   ("comedi: rtd520 DAC chan=%d range=%d writing %d as 0x%x\n",
+                    chan, range, data[i], val);
 
-       /* step 3: make sure arguments are trivially compatible */
+               /* a typical programming sequence */
+               RtdDacFifoPut(dev, chan, val);  /* put the value in */
+               RtdDacUpdate(dev, chan);        /* trigger the conversion */
 
-       if (cmd->start_arg != 0) {
-               cmd->start_arg = 0;
-               err++;
-       }
+               devpriv->aoValue[chan] = data[i];       /* save for read back */
 
-       if (cmd->scan_begin_src == TRIG_TIMER) {
-               /* Note: these are time periods, not actual rates */
-               if (1 == cmd->chanlist_len) {   /* no scanning */
-                       if (cmd->scan_begin_arg < RTD_MAX_SPEED_1) {
-                               cmd->scan_begin_arg = RTD_MAX_SPEED_1;
-                               rtd_ns_to_timer(&cmd->scan_begin_arg,
-                                               TRIG_ROUND_UP);
-                               err++;
-                       }
-                       if (cmd->scan_begin_arg > RTD_MIN_SPEED_1) {
-                               cmd->scan_begin_arg = RTD_MIN_SPEED_1;
-                               rtd_ns_to_timer(&cmd->scan_begin_arg,
-                                               TRIG_ROUND_DOWN);
-                               err++;
-                       }
-               } else {
-                       if (cmd->scan_begin_arg < RTD_MAX_SPEED) {
-                               cmd->scan_begin_arg = RTD_MAX_SPEED;
-                               rtd_ns_to_timer(&cmd->scan_begin_arg,
-                                               TRIG_ROUND_UP);
-                               err++;
-                       }
-                       if (cmd->scan_begin_arg > RTD_MIN_SPEED) {
-                               cmd->scan_begin_arg = RTD_MIN_SPEED;
-                               rtd_ns_to_timer(&cmd->scan_begin_arg,
-                                               TRIG_ROUND_DOWN);
-                               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;
-                       err++;
-               }
-       }
-       if (cmd->convert_src == TRIG_TIMER) {
-               if (1 == cmd->chanlist_len) {   /* no scanning */
-                       if (cmd->convert_arg < RTD_MAX_SPEED_1) {
-                               cmd->convert_arg = RTD_MAX_SPEED_1;
-                               rtd_ns_to_timer(&cmd->convert_arg,
-                                               TRIG_ROUND_UP);
-                               err++;
-                       }
-                       if (cmd->convert_arg > RTD_MIN_SPEED_1) {
-                               cmd->convert_arg = RTD_MIN_SPEED_1;
-                               rtd_ns_to_timer(&cmd->convert_arg,
-                                               TRIG_ROUND_DOWN);
-                               err++;
-                       }
-               } else {
-                       if (cmd->convert_arg < RTD_MAX_SPEED) {
-                               cmd->convert_arg = RTD_MAX_SPEED;
-                               rtd_ns_to_timer(&cmd->convert_arg,
-                                               TRIG_ROUND_UP);
-                               err++;
-                       }
-                       if (cmd->convert_arg > RTD_MIN_SPEED) {
-                               cmd->convert_arg = RTD_MIN_SPEED;
-                               rtd_ns_to_timer(&cmd->convert_arg,
-                                               TRIG_ROUND_DOWN);
-                               err++;
-                       }
+               for (ii = 0; ii < RTD_DAC_TIMEOUT; ++ii) {
+                       stat = RtdFifoStatus(dev);
+                       /* 1 -> not empty */
+                       if (stat & ((0 == chan) ? FS_DAC1_NOT_EMPTY :
+                                   FS_DAC2_NOT_EMPTY))
+                               break;
+                       WAIT_QUIETLY;
                }
-       } else {
-               /* external trigger */
-               /* see above */
-               if (cmd->convert_arg > 9) {
-                       cmd->convert_arg = 9;
-                       err++;
+               if (ii >= RTD_DAC_TIMEOUT) {
+                       DPRINTK
+                           ("rtd520: Error: DAC never finished! FifoStatus=0x%x\n",
+                            stat ^ 0x6666);
+                       return -ETIMEDOUT;
                }
        }
 
-#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 */
+       /* return the number of samples read/written */
+       return i;
+}
 
-       } else {
-               /* TRIG_NONE */
-               if (cmd->stop_arg != 0) {
-                       cmd->stop_arg = 0;
-                       err++;
-               }
-       }
+/* 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(struct comedi_device *dev,
+                       struct comedi_subdevice *s, struct comedi_insn *insn,
+                       unsigned int *data)
+{
+       int i;
+       int chan = CR_CHAN(insn->chanspec);
 
-       if (err)
-               return 3;
+       for (i = 0; i < insn->n; i++)
+               data[i] = devpriv->aoValue[chan];
 
 
-       /* step 4: fix up any arguments */
+       return i;
+}
 
-       if (cmd->chanlist_len > RTD_MAX_CHANLIST) {
-               cmd->chanlist_len = RTD_MAX_CHANLIST;
-               err++;
-       }
-       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++;
+/*
+   Write a masked set of bits and the read back the port.
+   We track what the bits should be (i.e. we don't read the port first).
 
-       }
-       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++;
+   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(struct comedi_device *dev,
+                            struct comedi_subdevice *s,
+                            struct comedi_insn *insn, unsigned int *data)
+{
+       if (insn->n != 2)
+               return -EINVAL;
 
-               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++;
-               }
+       /* 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 */
+               RtdDio0Write(dev, s->state);
        }
+       /* on return, data[1] contains the value of the digital
+        * input lines. */
+       data[1] = RtdDio0Read(dev);
 
-       if (err)
-               return 4;
+       /*DPRINTK("rtd520:port_0 wrote: 0x%x read: 0x%x\n", s->state, data[1]); */
 
-       return 0;
+       return 2;
 }
 
 /*
-  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.
+  Configure one bit on a IO port as Input or Output (hence the name :-).
 */
-static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+static int rtd_dio_insn_config(struct comedi_device *dev,
+                              struct comedi_subdevice *s,
+                              struct comedi_insn *insn, unsigned int *data)
 {
-       struct comedi_cmd *cmd = &s->async->cmd;
-       int timer;
-
-       /* stop anything currently running */
-       RtdPacerStopSource(dev, 0);     /* stop on SOFTWARE stop */
-       RtdPacerStop(dev);      /* make sure PACER is stopped */
-       RtdAdcConversionSource(dev, 0); /* software trigger only */
-       RtdInterruptMask(dev, 0);
-#ifdef USE_DMA
-       if (devpriv->flags & DMA0_ACTIVE) {     /* cancel anything running */
-               RtdPlxInterruptWrite(dev,       /* disable any more interrupts */
-                                    RtdPlxInterruptRead(dev) & ~ICS_DMA0_E);
-               abort_dma(dev, 0);
-               devpriv->flags &= ~DMA0_ACTIVE;
-               if (RtdPlxInterruptRead(dev) & ICS_DMA0_A) {    /*clear pending int */
-                       RtdDma0Control(dev, PLX_CLEAR_DMA_INTR_BIT);
-               }
-       }
-       RtdDma0Reset(dev);      /* reset onboard state */
-#endif /* USE_DMA */
-       RtdAdcClearFifo(dev);   /* clear any old data */
-       RtdInterruptOverrunClear(dev);
-       devpriv->intCount = 0;
-
-       if (!dev->irq) {        /* we need interrupts for this */
-               DPRINTK("rtd520: ERROR! No interrupt available!\n");
-               return -ENXIO;
-       }
-
-       /* start configuration */
-       /* load channel list and reset CGT */
-       rtd_load_channelgain_list(dev, cmd->chanlist_len, cmd->chanlist);
+       int chan = CR_CHAN(insn->chanspec);
 
-       /* setup the common case and override if needed */
-       if (cmd->chanlist_len > 1) {
-               /*DPRINTK ("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 */
-               /*DPRINTK ("rtd520: single channel setup\n"); */
-               RtdPacerStartSource(dev, 0);    /* software triggers pacer */
-               RtdAdcConversionSource(dev, 1); /* PACER triggers ADC */
+       /* 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. */
+       switch (data[0]) {
+       case INSN_CONFIG_DIO_OUTPUT:
+               s->io_bits |= 1 << chan;        /* 1 means Out */
+               break;
+       case INSN_CONFIG_DIO_INPUT:
+               s->io_bits &= ~(1 << chan);
+               break;
+       case INSN_CONFIG_DIO_QUERY:
+               data[1] =
+                   (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
+               return insn->n;
+               break;
+       default:
+               return -EINVAL;
        }
-       RtdAboutCounter(dev, devpriv->fifoLen / 2 - 1); /* 1/2 FIFO */
 
-       if (TRIG_TIMER == cmd->scan_begin_src) {
-               /* scan_begin_arg is in nanoseconds */
-               /* find out how many samples to wait before transferring */
-               if (cmd->flags & TRIG_WAKE_EOS) {
-                       /* this may generate un-sustainable interrupt rates */
-                       /* the application is responsible for doing the right thing */
-                       devpriv->transCount = cmd->chanlist_len;
-                       devpriv->flags |= SEND_EOS;
-               } else {
-                       /* arrange to transfer data periodically */
-                       devpriv->transCount
-                           =
-                           (TRANS_TARGET_PERIOD * cmd->chanlist_len) /
-                           cmd->scan_begin_arg;
-                       if (devpriv->transCount < cmd->chanlist_len) {
-                               /* transfer after each scan (and avoid 0) */
-                               devpriv->transCount = cmd->chanlist_len;
-                       } else {        /* make a multiple of scan length */
-                               devpriv->transCount =
-                                   (devpriv->transCount +
-                                    cmd->chanlist_len - 1)
-                                   / cmd->chanlist_len;
-                               devpriv->transCount *= cmd->chanlist_len;
-                       }
-                       devpriv->flags |= SEND_EOS;
-               }
-               if (devpriv->transCount >= (devpriv->fifoLen / 2)) {
-                       /* out of counter range, use 1/2 fifo instead */
-                       devpriv->transCount = 0;
-                       devpriv->flags &= ~SEND_EOS;
-               } else {
-                       /* interrupt for each transfer */
-                       RtdAboutCounter(dev, devpriv->transCount - 1);
-               }
+       DPRINTK("rtd520: port_0_direction=0x%x (1 means out)\n", s->io_bits);
+       /* TODO support digital match interrupts and strobes */
+       RtdDioStatusWrite(dev, 0x01);   /* make Dio0Ctrl point to direction */
+       RtdDio0CtrlWrite(dev, s->io_bits);      /* set direction 1 means Out */
+       RtdDioStatusWrite(dev, 0);      /* make Dio0Ctrl clear interrupts */
 
-               DPRINTK
-                   ("rtd520: scanLen=%d transferCount=%d fifoLen=%d\n  scanTime(ns)=%d flags=0x%x\n",
-                    cmd->chanlist_len, devpriv->transCount, devpriv->fifoLen,
-                    cmd->scan_begin_arg, devpriv->flags);
-       } else {                /* unknown timing, just use 1/2 FIFO */
-               devpriv->transCount = 0;
-               devpriv->flags &= ~SEND_EOS;
-       }
-       RtdPacerClockSource(dev, 1);    /* use INTERNAL 8Mhz clock source */
-       RtdAboutStopEnable(dev, 1);     /* just interrupt, dont stop */
+       /* port1 can only be all input or all output */
 
-       /* BUG??? these look like enumerated values, but they are bit fields */
+       /* there are also 2 user input lines and 2 user output lines */
 
-       /* First, setup when to stop */
-       switch (cmd->stop_src) {
-       case TRIG_COUNT:        /* stop after N scans */
-               devpriv->aiCount = cmd->stop_arg * cmd->chanlist_len;
-               if ((devpriv->transCount > 0)
-                   && (devpriv->transCount > devpriv->aiCount)) {
-                       devpriv->transCount = devpriv->aiCount;
-               }
-               break;
+       return 1;
+}
 
-       case TRIG_NONE: /* stop when cancel is called */
-               devpriv->aiCount = -1;  /* read forever */
-               break;
+static int rtd_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{                              /* board name and options flags */
+       struct comedi_subdevice *s;
+       struct pci_dev *pcidev;
+       int ret;
+       resource_size_t physLas0;       /* configuration */
+       resource_size_t physLas1;       /* data area */
+       resource_size_t physLcfg;       /* PLX9080 */
+#ifdef USE_DMA
+       int index;
+#endif
 
-       default:
-               DPRINTK("rtd520: Warning! ignoring stop_src mode %d\n",
-                       cmd->stop_src);
-       }
+       printk(KERN_INFO "comedi%d: rtd520 attaching.\n", dev->minor);
 
-       /* 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 */
-               /*DPRINTK ("rtd520: loading %d into pacer\n", timer); */
-               RtdPacerCounter(dev, timer);
+#if defined(CONFIG_COMEDI_DEBUG) && defined(USE_DMA)
+       /* You can set this a load time: modprobe comedi comedi_debug=1 */
+       if (0 == comedi_debug)  /* force DMA debug printks */
+               comedi_debug = 1;
+#endif
 
-               break;
+       /*
+        * Allocate the private structure area.  alloc_private() is a
+        * convenient macro defined in comedidev.h.
+        */
+       if (alloc_private(dev, sizeof(struct rtdPrivate)) < 0)
+               return -ENOMEM;
 
-       case TRIG_EXT:
-               RtdPacerStartSource(dev, 1);    /* EXTERNALy trigger pacer */
-               break;
+       /*
+        * Probe the device to determine what device in the series it is.
+        */
+       for (pcidev = pci_get_device(PCI_VENDOR_ID_RTD, PCI_ANY_ID, NULL);
+            pcidev != NULL;
+            pcidev = pci_get_device(PCI_VENDOR_ID_RTD, PCI_ANY_ID, pcidev)) {
+               int i;
 
-       default:
-               DPRINTK("rtd520: Warning! ignoring scan_begin_src mode %d\n",
-                       cmd->scan_begin_src);
+               if (it->options[0] || it->options[1]) {
+                       if (pcidev->bus->number != it->options[0]
+                           || PCI_SLOT(pcidev->devfn) != it->options[1]) {
+                               continue;
+                       }
+               }
+               for (i = 0; i < ARRAY_SIZE(rtd520Boards); ++i) {
+                       if (pcidev->device == rtd520Boards[i].device_id) {
+                               dev->board_ptr = &rtd520Boards[i];
+                               break;
+                       }
+               }
+               if (dev->board_ptr)
+                       break;  /* found one */
        }
-
-       /* Sample timing within a scan */
-       switch (cmd->convert_src) {
-       case TRIG_TIMER:        /* periodic */
-               if (cmd->chanlist_len > 1) {    /* only needed for multi-channel */
-                       timer = rtd_ns_to_timer(&cmd->convert_arg,
-                                               TRIG_ROUND_NEAREST);
-                       /* setup BURST clock */
-                       /*DPRINTK ("rtd520: loading %d into burst\n", timer); */
-                       RtdBurstCounter(dev, timer);
+       if (!pcidev) {
+               if (it->options[0] && it->options[1]) {
+                       printk(KERN_INFO "No RTD card at bus=%d slot=%d.\n",
+                              it->options[0], it->options[1]);
+               } else {
+                       printk(KERN_INFO "No RTD card found.\n");
                }
+               return -EIO;
+       }
+       devpriv->pci_dev = pcidev;
+       dev->board_name = thisboard->name;
 
-               break;
-
-       case TRIG_EXT:          /* external */
-               RtdBurstStartSource(dev, 2);    /* EXTERNALy trigger burst */
-               break;
-
-       default:
-               DPRINTK("rtd520: Warning! ignoring convert_src mode %d\n",
-                       cmd->convert_src);
+       ret = comedi_pci_enable(pcidev, DRV_NAME);
+       if (ret < 0) {
+               printk(KERN_INFO "Failed to enable PCI device and request regions.\n");
+               return ret;
        }
-       /* end configuration */
+       devpriv->got_regions = 1;
 
-       /* This doesn't seem to work.  There is no way to clear an interrupt
-          that the priority controller has queued! */
-       RtdInterruptClearMask(dev, ~0); /* clear any existing flags */
-       RtdInterruptClear(dev);
+       /*
+        * Initialize base addresses
+        */
+       /* Get the physical address from PCI config */
+       physLas0 = pci_resource_start(devpriv->pci_dev, LAS0_PCIINDEX);
+       physLas1 = pci_resource_start(devpriv->pci_dev, LAS1_PCIINDEX);
+       physLcfg = pci_resource_start(devpriv->pci_dev, LCFG_PCIINDEX);
+       /* Now have the kernel map this into memory */
+       /* ASSUME page aligned */
+       devpriv->las0 = ioremap_nocache(physLas0, LAS0_PCISIZE);
+       devpriv->las1 = ioremap_nocache(physLas1, LAS1_PCISIZE);
+       devpriv->lcfg = ioremap_nocache(physLcfg, LCFG_PCISIZE);
 
-       /* TODO: allow multiple interrupt sources */
-       if (devpriv->transCount > 0) {  /* transfer every N samples */
-               RtdInterruptMask(dev, IRQM_ADC_ABOUT_CNT);
-               DPRINTK("rtd520: Transferring every %d\n", devpriv->transCount);
-       } else {                /* 1/2 FIFO transfers */
-#ifdef USE_DMA
-               devpriv->flags |= DMA0_ACTIVE;
+       if (!devpriv->las0 || !devpriv->las1 || !devpriv->lcfg)
+               return -ENOMEM;
 
-               /* point to first transfer in ring */
-               devpriv->dma0Offset = 0;
-               RtdDma0Mode(dev, DMA_MODE_BITS);
-               RtdDma0Next(dev,        /* point to first block */
-                           devpriv->dma0Chain[DMA_CHAIN_COUNT - 1].next);
-               RtdDma0Source(dev, DMAS_ADFIFO_HALF_FULL);      /* set DMA trigger source */
 
-               RtdPlxInterruptWrite(dev,       /* enable interrupt */
-                                    RtdPlxInterruptRead(dev) | ICS_DMA0_E);
-               /* Must be 2 steps.  See PLX app note about "Starting a DMA transfer" */
-               RtdDma0Control(dev, PLX_DMA_EN_BIT);    /* enable DMA (clear INTR?) */
-               RtdDma0Control(dev, PLX_DMA_EN_BIT | PLX_DMA_START_BIT);        /*start DMA */
-               DPRINTK("rtd520: Using DMA0 transfers. plxInt %x RtdInt %x\n",
-                       RtdPlxInterruptRead(dev), devpriv->intMask);
-#else /* USE_DMA */
-               RtdInterruptMask(dev, IRQM_ADC_ABOUT_CNT);
-               DPRINTK("rtd520: Transferring every 1/2 FIFO\n");
-#endif /* USE_DMA */
-       }
+       DPRINTK("%s: LAS0=%llx, LAS1=%llx, CFG=%llx.\n", dev->board_name,
+               (unsigned long long)physLas0, (unsigned long long)physLas1,
+               (unsigned long long)physLcfg);
+       {                       /* The RTD driver does this */
+               unsigned char pci_latency;
+               u16 revision;
+               /*uint32_t epld_version; */
 
-       /* BUG: start_src is ASSUMED to be TRIG_NOW */
-       /* BUG? it seems like things are running before the "start" */
-       RtdPacerStart(dev);     /* Start PACER */
-       return 0;
-}
+               pci_read_config_word(devpriv->pci_dev, PCI_REVISION_ID,
+                                    &revision);
+               DPRINTK("%s: PCI revision %d.\n", dev->board_name, revision);
 
-/*
-  Stop a running data acquisition.
-*/
-static int rtd_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
-{
-       u16 status;
+               pci_read_config_byte(devpriv->pci_dev,
+                                    PCI_LATENCY_TIMER, &pci_latency);
+               if (pci_latency < 32) {
+                       printk(KERN_INFO "%s: PCI latency changed from %d to %d\n",
+                              dev->board_name, pci_latency, 32);
+                       pci_write_config_byte(devpriv->pci_dev,
+                                             PCI_LATENCY_TIMER, 32);
+               } else {
+                       DPRINTK("rtd520: PCI latency = %d\n", pci_latency);
+               }
 
-       RtdPacerStopSource(dev, 0);     /* stop on SOFTWARE stop */
-       RtdPacerStop(dev);      /* Stop PACER */
-       RtdAdcConversionSource(dev, 0); /* software trigger only */
-       RtdInterruptMask(dev, 0);
-       devpriv->aiCount = 0;   /* stop and don't transfer any more */
-#ifdef USE_DMA
-       if (devpriv->flags & DMA0_ACTIVE) {
-               RtdPlxInterruptWrite(dev,       /* disable any more interrupts */
-                                    RtdPlxInterruptRead(dev) & ~ICS_DMA0_E);
-               abort_dma(dev, 0);
-               devpriv->flags &= ~DMA0_ACTIVE;
+               /*
+                * Undocumented EPLD version (doesn't match RTD driver results)
+                */
+               /*DPRINTK ("rtd520: Reading epld from %p\n",
+                  devpriv->las0+0);
+                  epld_version = readl (devpriv->las0+0);
+                  if ((epld_version & 0xF0) >> 4 == 0x0F) {
+                  DPRINTK("rtd520: pre-v8 EPLD. (%x)\n", epld_version);
+                  } else {
+                  DPRINTK("rtd520: EPLD version %x.\n", epld_version >> 4);
+                  } */
        }
-#endif /* USE_DMA */
-       status = RtdInterruptStatus(dev);
-       DPRINTK
-           ("rtd520: Acquisition canceled. %ld ints, intStat=%x, overStat=%x\n",
-            devpriv->intCount, status,
-            0xffff & RtdInterruptOverrunStatus(dev));
-       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, int base)
-{                              /* clock period (in ns) */
-       int divider;
+       /* Show board configuration */
+       printk(KERN_INFO "%s:", dev->board_name);
 
-       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 */
+       /*
+        * Allocate the subdevice structures.  alloc_subdevice() is a
+        * convenient macro defined in comedidev.h.
+        */
+       if (alloc_subdevices(dev, 4) < 0)
+               return -ENOMEM;
 
-       /* Note: we don't check for max, because different timers
-          have different ranges */
 
-       *nanosec = base * divider;
-       return divider - 1;     /* countdown is divisor+1 */
-}
+       s = dev->subdevices + 0;
+       dev->read_subdev = s;
+       /* analog input subdevice */
+       s->type = COMEDI_SUBD_AI;
+       s->subdev_flags =
+           SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF | SDF_CMD_READ;
+       s->n_chan = thisboard->aiChans;
+       s->maxdata = (1 << thisboard->aiBits) - 1;
+       if (thisboard->aiMaxGain <= 32)
+               s->range_table = &rtd_ai_7520_range;
+       else
+               s->range_table = &rtd_ai_4520_range;
 
-/*
-  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);
-}
+       s->len_chanlist = RTD_MAX_CHANLIST;     /* devpriv->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->poll = rtd_ai_poll; *//* not ready yet */
 
-/*
-  Output one (or more) analog values to a single port as fast as possible.
-*/
-static int rtd_ao_winsn(struct comedi_device *dev,
-                       struct comedi_subdevice *s, struct comedi_insn *insn,
-                       unsigned int *data)
-{
-       int i;
-       int chan = CR_CHAN(insn->chanspec);
-       int range = CR_RANGE(insn->chanspec);
+       s = dev->subdevices + 1;
+       /* analog output subdevice */
+       s->type = COMEDI_SUBD_AO;
+       s->subdev_flags = SDF_WRITABLE;
+       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;
 
-       /* Configure the output range (table index matches the range values) */
-       RtdDacRange(dev, chan, range);
+       s = dev->subdevices + 2;
+       /* digital i/o subdevice */
+       s->type = COMEDI_SUBD_DIO;
+       s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+       /* we only support port 0 right now.  Ignoring port 1 and user IO */
+       s->n_chan = 8;
+       s->maxdata = 1;
+       s->range_table = &range_digital;
+       s->insn_bits = rtd_dio_insn_bits;
+       s->insn_config = rtd_dio_insn_config;
 
-       /* 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) {
-               int val = data[i] << 3;
-               int stat = 0;   /* initialize to avoid bogus warning */
-               int ii;
+       /* timer/counter subdevices (not currently supported) */
+       s = dev->subdevices + 3;
+       s->type = COMEDI_SUBD_COUNTER;
+       s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+       s->n_chan = 3;
+       s->maxdata = 0xffff;
 
-               /* VERIFY: comedi range and offset conversions */
+       /* initialize board, per RTD spec */
+       /* also, initialize shadow registers */
+       RtdResetBoard(dev);
+       udelay(100);            /* needed? */
+       RtdPlxInterruptWrite(dev, 0);
+       RtdInterruptMask(dev, 0);       /* and sets shadow */
+       RtdInterruptClearMask(dev, ~0); /* and sets shadow */
+       RtdInterruptClear(dev); /* clears bits set by mask */
+       RtdInterruptOverrunClear(dev);
+       RtdClearCGT(dev);
+       RtdAdcClearFifo(dev);
+       RtdDacClearFifo(dev, 0);
+       RtdDacClearFifo(dev, 1);
+       /* clear digital IO fifo */
+       RtdDioStatusWrite(dev, 0);      /* safe state, set shadow */
+       RtdUtcCtrlPut(dev, 0, 0x30);    /* safe state, set shadow */
+       RtdUtcCtrlPut(dev, 1, 0x30);    /* safe state, set shadow */
+       RtdUtcCtrlPut(dev, 2, 0x30);    /* safe state, set shadow */
+       RtdUtcCtrlPut(dev, 3, 0);       /* safe state, set shadow */
+       /* TODO: set user out source ??? */
 
-               if ((range > 1) /* bipolar */
-                   && (data[i] < 2048)) {
-                       /* offset and sign extend */
-                       val = (((int)data[i]) - 2048) << 3;
-               } else {        /* unipolor */
-                       val = data[i] << 3;
-               }
+       /* check if our interrupt is available and get it */
+       ret = request_irq(devpriv->pci_dev->irq, rtd_interrupt,
+                         IRQF_SHARED, DRV_NAME, dev);
 
-               DPRINTK
-                   ("comedi: rtd520 DAC chan=%d range=%d writing %d as 0x%x\n",
-                    chan, range, data[i], val);
+       if (ret < 0) {
+               printk("Could not get interrupt! (%u)\n",
+                      devpriv->pci_dev->irq);
+               return ret;
+       }
+       dev->irq = devpriv->pci_dev->irq;
+       printk(KERN_INFO "( irq=%u )", dev->irq);
 
-               /* a typical programming sequence */
-               RtdDacFifoPut(dev, chan, val);  /* put the value in */
-               RtdDacUpdate(dev, chan);        /* trigger the conversion */
+       ret = rtd520_probe_fifo_depth(dev);
+       if (ret < 0)
+               return ret;
 
-               devpriv->aoValue[chan] = data[i];       /* save for read back */
+       devpriv->fifoLen = ret;
+       printk("( fifoLen=%d )", devpriv->fifoLen);
+
+#ifdef USE_DMA
+       if (dev->irq > 0) {
+               printk("( DMA buff=%d )\n", DMA_CHAIN_COUNT);
+               /*
+                * The PLX9080 has 2 DMA controllers, but there could be
+                * 4 sources: ADC, digital, DAC1, and DAC2.  Since only the
+                * ADC supports cmd mode right now, this isn't an issue (yet)
+                */
+               devpriv->dma0Offset = 0;
 
-               for (ii = 0; ii < RTD_DAC_TIMEOUT; ++ii) {
-                       stat = RtdFifoStatus(dev);
-                       /* 1 -> not empty */
-                       if (stat & ((0 == chan) ? FS_DAC1_NOT_EMPTY :
-                                   FS_DAC2_NOT_EMPTY))
-                               break;
-                       WAIT_QUIETLY;
+               for (index = 0; index < DMA_CHAIN_COUNT; index++) {
+                       devpriv->dma0Buff[index] =
+                           pci_alloc_consistent(devpriv->pci_dev,
+                                                sizeof(u16) *
+                                                devpriv->fifoLen / 2,
+                                                &devpriv->
+                                                dma0BuffPhysAddr[index]);
+                       if (devpriv->dma0Buff[index] == NULL) {
+                               ret = -ENOMEM;
+                               goto rtd_attach_die_error;
+                       }
+                       /*DPRINTK ("buff[%d] @ %p virtual, %x PCI\n",
+                          index,
+                          devpriv->dma0Buff[index],
+                          devpriv->dma0BuffPhysAddr[index]); */
                }
-               if (ii >= RTD_DAC_TIMEOUT) {
-                       DPRINTK
-                           ("rtd520: Error: DAC never finished! FifoStatus=0x%x\n",
-                            stat ^ 0x6666);
-                       return -ETIMEDOUT;
+
+               /*
+                * setup DMA descriptor ring (use cpu_to_le32 for byte
+                * ordering?)
+                */
+               devpriv->dma0Chain =
+                   pci_alloc_consistent(devpriv->pci_dev,
+                                        sizeof(struct plx_dma_desc) *
+                                        DMA_CHAIN_COUNT,
+                                        &devpriv->dma0ChainPhysAddr);
+               for (index = 0; index < DMA_CHAIN_COUNT; index++) {
+                       devpriv->dma0Chain[index].pci_start_addr =
+                           devpriv->dma0BuffPhysAddr[index];
+                       devpriv->dma0Chain[index].local_start_addr =
+                           DMALADDR_ADC;
+                       devpriv->dma0Chain[index].transfer_size =
+                           sizeof(u16) * devpriv->fifoLen / 2;
+                       devpriv->dma0Chain[index].next =
+                           (devpriv->dma0ChainPhysAddr + ((index +
+                                                           1) %
+                                                          (DMA_CHAIN_COUNT))
+                            * sizeof(devpriv->dma0Chain[0]))
+                           | DMA_TRANSFER_BITS;
+                       /*DPRINTK ("ring[%d] @%lx PCI: %x, local: %x, N: 0x%x, next: %x\n",
+                          index,
+                          ((long)devpriv->dma0ChainPhysAddr
+                          + (index * sizeof(devpriv->dma0Chain[0]))),
+                          devpriv->dma0Chain[index].pci_start_addr,
+                          devpriv->dma0Chain[index].local_start_addr,
+                          devpriv->dma0Chain[index].transfer_size,
+                          devpriv->dma0Chain[index].next); */
                }
-       }
 
-       /* return the number of samples read/written */
-       return i;
-}
+               if (devpriv->dma0Chain == NULL) {
+                       ret = -ENOMEM;
+                       goto rtd_attach_die_error;
+               }
 
-/* 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(struct comedi_device *dev,
-                       struct comedi_subdevice *s, struct comedi_insn *insn,
-                       unsigned int *data)
-{
-       int i;
-       int chan = CR_CHAN(insn->chanspec);
+               RtdDma0Mode(dev, DMA_MODE_BITS);
+               /* set DMA trigger source */
+               RtdDma0Source(dev, DMAS_ADFIFO_HALF_FULL);
+       } else {
+               printk(KERN_INFO "( no IRQ->no DMA )");
+       }
+#endif /* USE_DMA */
 
-       for (i = 0; i < insn->n; i++)
-               data[i] = devpriv->aoValue[chan];
+       if (dev->irq) {         /* enable plx9080 interrupts */
+               RtdPlxInterruptWrite(dev, ICS_PIE | ICS_PLIE);
+       }
 
+       printk("\ncomedi%d: rtd520 driver attached.\n", dev->minor);
 
-       return i;
-}
+       return 1;
 
-/*
-   Write a masked set of bits and the read back the port.
-   We track what the bits should be (i.e. we don't read the port first).
+#if 0
+       /* hit an error, clean up memory and return ret */
+/* rtd_attach_die_error: */
+#ifdef USE_DMA
+       for (index = 0; index < DMA_CHAIN_COUNT; index++) {
+               if (NULL != devpriv->dma0Buff[index]) { /* free buffer memory */
+                       pci_free_consistent(devpriv->pci_dev,
+                                           sizeof(u16) * devpriv->fifoLen / 2,
+                                           devpriv->dma0Buff[index],
+                                           devpriv->dma0BuffPhysAddr[index]);
+                       devpriv->dma0Buff[index] = NULL;
+               }
+       }
+       if (NULL != devpriv->dma0Chain) {
+               pci_free_consistent(devpriv->pci_dev,
+                                   sizeof(struct plx_dma_desc)
+                                   * DMA_CHAIN_COUNT,
+                                   devpriv->dma0Chain,
+                                   devpriv->dma0ChainPhysAddr);
+               devpriv->dma0Chain = NULL;
+       }
+#endif /* USE_DMA */
+       /* subdevices and priv are freed by the core */
+       if (dev->irq) {
+               /* disable interrupt controller */
+               RtdPlxInterruptWrite(dev, RtdPlxInterruptRead(dev)
+                                    & ~(ICS_PLIE | ICS_DMA0_E | ICS_DMA1_E));
+               free_irq(dev->irq, dev);
+       }
 
-   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(struct comedi_device *dev,
-                            struct comedi_subdevice *s,
-                            struct comedi_insn *insn, unsigned int *data)
-{
-       if (insn->n != 2)
-               return -EINVAL;
+       /* release all regions that were allocated */
+       if (devpriv->las0)
+               iounmap(devpriv->las0);
 
-       /* 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];
+       if (devpriv->las1)
+               iounmap(devpriv->las1);
 
-               /* Write out the new digital output lines */
-               RtdDio0Write(dev, s->state);
-       }
-       /* on return, data[1] contains the value of the digital
-        * input lines. */
-       data[1] = RtdDio0Read(dev);
+       if (devpriv->lcfg)
+               iounmap(devpriv->lcfg);
 
-       /*DPRINTK("rtd520:port_0 wrote: 0x%x read: 0x%x\n", s->state, data[1]); */
+       if (devpriv->pci_dev)
+               pci_dev_put(devpriv->pci_dev);
 
-       return 2;
+       return ret;
+#endif
 }
 
-/*
-  Configure one bit on a IO port as Input or Output (hence the name :-).
-*/
-static int rtd_dio_insn_config(struct comedi_device *dev,
-                              struct comedi_subdevice *s,
-                              struct comedi_insn *insn, unsigned int *data)
+static void rtd_detach(struct comedi_device *dev)
 {
-       int chan = CR_CHAN(insn->chanspec);
+#ifdef USE_DMA
+       int index;
+#endif
 
-       /* 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. */
-       switch (data[0]) {
-       case INSN_CONFIG_DIO_OUTPUT:
-               s->io_bits |= 1 << chan;        /* 1 means Out */
-               break;
-       case INSN_CONFIG_DIO_INPUT:
-               s->io_bits &= ~(1 << chan);
-               break;
-       case INSN_CONFIG_DIO_QUERY:
-               data[1] =
-                   (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
-               return insn->n;
-               break;
-       default:
-               return -EINVAL;
+       if (devpriv) {
+               /* Shut down any board ops by resetting it */
+#ifdef USE_DMA
+               if (devpriv->lcfg) {
+                       RtdDma0Control(dev, 0); /* disable DMA */
+                       RtdDma1Control(dev, 0); /* disable DMA */
+                       RtdPlxInterruptWrite(dev, ICS_PIE | ICS_PLIE);
+               }
+#endif /* USE_DMA */
+               if (devpriv->las0) {
+                       RtdResetBoard(dev);
+                       RtdInterruptMask(dev, 0);
+                       RtdInterruptClearMask(dev, ~0);
+                       RtdInterruptClear(dev); /* clears bits set by mask */
+               }
+#ifdef USE_DMA
+               /* release DMA */
+               for (index = 0; index < DMA_CHAIN_COUNT; index++) {
+                       if (NULL != devpriv->dma0Buff[index]) {
+                               pci_free_consistent(devpriv->pci_dev,
+                                                   sizeof(u16) *
+                                                   devpriv->fifoLen / 2,
+                                                   devpriv->dma0Buff[index],
+                                                   devpriv->
+                                                   dma0BuffPhysAddr[index]);
+                               devpriv->dma0Buff[index] = NULL;
+                       }
+               }
+               if (NULL != devpriv->dma0Chain) {
+                       pci_free_consistent(devpriv->pci_dev,
+                                           sizeof(struct plx_dma_desc) *
+                                           DMA_CHAIN_COUNT, devpriv->dma0Chain,
+                                           devpriv->dma0ChainPhysAddr);
+                       devpriv->dma0Chain = NULL;
+               }
+#endif /* USE_DMA */
+               if (dev->irq) {
+                       RtdPlxInterruptWrite(dev, RtdPlxInterruptRead(dev)
+                                            & ~(ICS_PLIE | ICS_DMA0_E |
+                                                ICS_DMA1_E));
+                       free_irq(dev->irq, dev);
+               }
+               if (devpriv->las0)
+                       iounmap(devpriv->las0);
+               if (devpriv->las1)
+                       iounmap(devpriv->las1);
+               if (devpriv->lcfg)
+                       iounmap(devpriv->lcfg);
+               if (devpriv->pci_dev) {
+                       if (devpriv->got_regions)
+                               comedi_pci_disable(devpriv->pci_dev);
+                       pci_dev_put(devpriv->pci_dev);
+               }
        }
-
-       DPRINTK("rtd520: port_0_direction=0x%x (1 means out)\n", s->io_bits);
-       /* TODO support digital match interrupts and strobes */
-       RtdDioStatusWrite(dev, 0x01);   /* make Dio0Ctrl point to direction */
-       RtdDio0CtrlWrite(dev, s->io_bits);      /* set direction 1 means Out */
-       RtdDioStatusWrite(dev, 0);      /* make Dio0Ctrl clear interrupts */
-
-       /* port1 can only be all input or all output */
-
-       /* there are also 2 user input lines and 2 user output lines */
-
-       return 1;
 }
 
 static struct comedi_driver rtd520_driver = {