analog input commands may possibly work now
authorFrank Mori Hess <fmhess@speakeasy.net>
Fri, 14 Sep 2001 18:39:02 +0000 (18:39 +0000)
committerFrank Mori Hess <fmhess@speakeasy.net>
Fri, 14 Sep 2001 18:39:02 +0000 (18:39 +0000)
comedi/drivers/cb_pcidas64.c
comedi/drivers/plx9080.h

index c12d5e7f417c7edd30b834277ba7bd99052e241f..28af037993f622c99ee9a866d90ee052cf0a5f03 100644 (file)
-/*
-    cb_pcidas64.c
-    This is a driver for the ComputerBoards/MeasurementComputing PCI-DAS
-    64xxx cards.
-
-    Author:  Frank Mori Hess <fmhess@uiuc.edu>
-
-    Options:
-    [0] - PCI bus number
-    [1] - PCI slot number
-
-    Copyright (C) 2001 Frank Mori Hess <fmhess@uiuc.edu>
-
-    COMEDI - Linux Control and Measurement Device Interface
-    Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-************************************************************************
-*/
-/*
-Driver: cb_pcidas64.o
-Description: Driver for the ComputerBoards/MeasurementComputing
-  PCI-DAS64xxx series with the PLX 9080 PCI controller.
-Author: Frank Mori Hess <fmhess@uiuc.edu>
-Status: Experimental
-Updated: 2001-8-27
-
-Configuration options:
-  [0] - PCI bus of device (optional)
-  [1] - PCI slot of device (optional)
-
-Basic insn support should work, but untested as far as I know.
-  Feel free to send and success/failure reports to author.  No
-  command support yet.
-*/
-/*
-STATUS:
-       insn supported
-
-TODO:
-       command support
-       calibration subdevice
-       user counter subdevice
-       there are a number of boards this driver will support when they are
-               fully released, but does not since yet since the pci device id numbers
-               are not yet available.
-       add plx9080 stuff to make interrupts and dma work
-       need to take care to prevent ai and ao from affecting each others register bits
-       support prescaled 100khz clock for slow pacing
-*/
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/malloc.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/timex.h>
-#include <linux/timer.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <asm/io.h>
-#include <linux/comedidev.h>
-#include "8253.h"
-#include "8255.h"
-#include "plx9080.h"
-
-#define PCIDAS64_DEBUG // enable debugging code
-//#undef PCIDAS64_DEBUG        // disable debugging code
-
-// PCI vendor number of ComputerBoards/MeasurementComputing
-#define PCI_VENDOR_ID_CB       0x1307
-#define TIMER_BASE 25  // 40MHz master clock
-#define PRESCALED_TIMER_BASE   10000   // 100kHz 'prescaled' clock for slow aquisition, maybe I'll support this someday
-
-/* PCI-DAS64xxx base addresses */
-
-// indices of base address regions
-#define PLX9080_BADRINDEX 0
-#define MAIN_BADRINDEX 2
-#define DIO_COUNTER_BADRINDEX 3
-
-#define PLX9080_IOSIZE 0xec
-#define MAIN_IOSIZE 0x302
-#define DIO_COUNTER_IOSIZE 0x29
-
-// devpriv->main_iobase registers
-// write-only
-#define INTR_ENABLE_REG        0x0     // interrupt enable register
-#define    EN_ADC_OVERRUN_BIT  0x8000  // enable adc overrun status bit
-#define    EN_DAC_UNDERRUN_BIT 0x4000  // enable dac underrun status bit
-#define    EN_ADC_DONE_INTR_BIT        0x8     // enable adc aquisition done interrupt
-#define    EN_ADC_INTR_SRC_BIT 0x4     // enable adc interrupt source
-#define    ADC_INTR_SRC_MASK   0x3     // bits that set adc interrupt source
-#define    ADC_INTR_QFULL_BITS 0x0     // interrupt fifo quater full
-#define    ADC_INTR_EOC_BITS   0x1     // interrupt end of conversion
-#define    ADC_INTR_EOSCAN_BITS        0x2     // interrupt end of scan
-#define    ADC_INTR_EOSEQ_BITS 0x3     // interrupt end of sequence (probably wont use this it's pretty fancy)
-#define HW_CONFIG_REG  0x2     // hardware config register
-#define    HW_CONFIG_DUMMY_BITS        0x2400  // bits that don't do anything yet but are given default values
-#define    HW_CONFIG_DUMMY_BITS_6402   0x0400  // dummy bits in 6402 manual are slightly different, probably doesn't matter
-#define    EXT_QUEUE   0x200   // use external channel/gain queue (more versatile than internal queue)
-#define FIFO_SIZE_REG  0x4     // allows adjustment of fifo sizes, we will always use maximum
-#define    FIFO_SIZE_DUMMY_BITS        0xf038  // bits that don't do anything yet but are given default values
-#define    ADC_FIFO_SIZE_MASK  0x7     // bits that set adc fifo size
-#define    ADC_FIFO_8K_BITS    0x0     // 8 kilosample adc fifo
-#define    DAC_FIFO_SIZE_MASK  0xf00   // bits that set dac fifo size
-#define    DAC_FIFO_16K_BITS 0x0
-#define ADC_CONTROL0_REG       0x10    // adc control register 0
-#define    TRIG1_FALLING_BIT   0x20    // trig 1 uses falling edge
-#define    ADC_EXT_CONV_FALLING_BIT    0x800   // external pacing uses falling edge
-#define    ADC_ENABLE_BIT      0x8000  // master adc enable
-#define ADC_CONTROL1_REG       0x12    // adc control register 1
-#define    SW_NOGATE_BIT       0x40    // disables software gate of adc
-#define    ADC_MODE_BITS(x)    (((x) & 0xf) << 12)
-#define ADC_SAMPLE_INTERVAL_LOWER_REG  0x16    // lower 16 bits of sample interval counter
-#define ADC_SAMPLE_INTERVAL_UPPER_REG  0x18    // upper 8 bits of sample interval counter
-#define ADC_DELAY_INTERVAL_LOWER_REG   0x1a    // lower 16 bits of delay interval counter
-#define ADC_DELAY_INTERVAL_UPPER_REG   0x1c    // upper 8 bits of delay interval counter
-#define ADC_COUNT_LOWER_REG    0x1e    // lower 16 bits of hardware conversion/scan counter
-#define ADC_COUNT_UPPER_REG    0x20    // upper 8 bits of hardware conversion/scan counter
-#define ADC_START_REG  0x22    // software trigger to start aquisition
-#define ADC_CONVERT_REG        0x24    // initiates single conversion
-#define ADC_QUEUE_CLEAR_REG    0x26    // clears adc queue
-#define ADC_QUEUE_LOAD_REG     0x28    // loads adc queue
-#define    CHAN_BITS(x)        ((x) & 0x3f)
-#define    GAIN_BITS(x)        (((x) & 0x3) << 8)      // translates range index to gain bits
-#define    UNIP_BIT(x) (((x) & 0x4) << 11)     // translates range index to unipolar/bipolar bit
-#define    SE_BIT      0x1000  // single-ended/ differential bit
-#define    QUEUE_EOSEQ_BIT     0x4000  // queue end of sequence
-#define    QUEUE_EOSCAN_BIT    0x8000  // queue end of scan
-#define ADC_BUFFER_CLEAR_REG   0x2a
-#define ADC_QUEUE_HIGH_REG     0x2c    // high channel for internal queue, use CHAN_BITS() macro above
-#define DAC_CONTROL0_REG       0x50    // dac control register 0
-#define    DAC_ENABLE_BIT      0x8000  // dac controller enable bit
-#define DAC_CONTROL1_REG       0x52    // dac control register 0
-#define    DAC_RANGE_BITS(channel, range)      (((range) & 0x3) << (2 * ((channel) & 0x1)))
-#define    DAC_OUTPUT_ENABLE_BIT       0x80    // dac output enable bit
-#define DAC_BUFFER_CLEAR_REG 0x66      // clear dac buffer
-#define DAC_CONVERT_REG(channel)       ((0x70) + (2 * ((channel) & 0x1)))
-// read-only
-#define HW_STATUS_REG  0x0
-#define   ADC_BUSY_BIT 0x8
-#define   HW_REVISION(x)       (((x) >> 12) & 0xf)
-#define PIPE1_READ_REG 0x4
-// read-write
-#define ADC_QUEUE_FIFO_REG     0x100   // external channel/gain queue, uses same bits as ADC_QUEUE_LOAD_REG
-
-// devpriv->dio_counter_iobase registers
-#define DIO_8255_OFFSET        0x0
-#define DO_REG 0x20
-#define DI_REG 0x28
-
-// analog input ranges for most boards
-static comedi_lrange ai_ranges =
-{
-       8,
-       {
-               BIP_RANGE(10),
-               BIP_RANGE(5),
-               BIP_RANGE(2.5),
-               BIP_RANGE(1.25),
-               UNI_RANGE(10),
-               UNI_RANGE(5),
-               UNI_RANGE(2.5),
-               UNI_RANGE(1.25)
-       }
-};
-
-// analog output ranges
-static comedi_lrange ao_ranges =
-{
-       4,
-       {
-               BIP_RANGE(5),
-               BIP_RANGE(10),
-               UNI_RANGE(5),
-               UNI_RANGE(10),
-       }
-};
-
-typedef struct pcidas64_board_struct
-{
-       char *name;
-       int device_id;  // pci device id
-       int ai_se_chans;        // number of ai inputs in single-ended mode
-       int ai_bits;    // analog input resolution
-       int ai_speed;   // fastest conversion period in ns
-       int ao_nchan;   // number of analog out channels
-       int ao_scan_speed;      // analog output speed (for a scan, not conversion)
-} pcidas64_board;
-
-static pcidas64_board pcidas64_boards[] =
-{
-       {
-               name:           "pci-das6402/16",
-               device_id:      0x1d,
-               ai_se_chans:    64,
-               ai_bits:        16,
-               ai_speed:       5000,
-               ao_nchan: 2,
-               ao_scan_speed:  10000,
-       },
-       {
-               name:           "pci-das6402/12",       // XXX check
-               device_id:      0x1e,
-               ai_se_chans:    64,
-               ai_bits:        12,
-               ai_speed:       5000,
-               ao_nchan: 2,
-               ao_scan_speed:  10000,
-       },
-       {
-               name:           "pci-das64/m1/16",
-               device_id:      0x35,
-               ai_se_chans:    64,
-               ai_bits:        16,
-               ai_speed:       1000,
-               ao_nchan: 2,
-               ao_scan_speed:  10000,
-       },
-       {
-               name:           "pci-das64/m2/16",
-               device_id:      0x36,
-               ai_se_chans:    64,
-               ai_bits:        16,
-               ai_speed:       500,
-               ao_nchan: 2,
-               ao_scan_speed:  10000,
-       },
-       {
-               name:           "pci-das64/m3/16",
-               device_id:      0x37,
-               ai_se_chans:    64,
-               ai_bits:        16,
-               ai_speed:       333,
-               ao_nchan: 2,
-               ao_scan_speed:  10000,
-       },
-
-#if 0
-       {
-               name:           "pci-das6402/16/jr",
-               device_id:      0 // XXX,
-               ai_se_chans:    64,
-               ai_bits:        16,
-               ai_speed:       5000,
-               ao_nchan: 0,
-               ao_scan_speed:  10000,
-       },
-       {
-               name:           "pci-das64/m1/16/jr",
-               device_id:      0 // XXX,
-               ai_se_chans:    64,
-               ai_bits:        16,
-               ai_speed:       1000,
-               ao_nchan: 0,
-               ao_scan_speed:  10000,
-       },
-       {
-               name:           "pci-das64/m2/16/jr",
-               device_id:      0 // XXX,
-               ai_se_chans:    64,
-               ai_bits:        16,
-               ai_speed:       500,
-               ao_nchan: 0,
-               ao_scan_speed:  10000,
-       },
-       {
-               name:           "pci-das64/m3/16/jr",
-               device_id:      0 // XXX,
-               ai_se_chans:    64,
-               ai_bits:        16,
-               ai_speed:       333,
-               ao_nchan: 0,
-               ao_scan_speed:  10000,
-       },
-       {
-               name:           "pci-das64/m1/14",
-               device_id:      0,      // XXX
-               ai_se_chans:    64,
-               ai_bits:        14,
-               ai_speed:       1000,
-               ao_nchan: 2,
-               ao_scan_speed:  10000,
-       },
-       {
-               name:           "pci-das64/m2/14",
-               device_id:      0,      // XXX
-               ai_se_chans:    64,
-               ai_bits:        14,
-               ai_speed:       500,
-               ao_nchan: 2,
-               ao_scan_speed:  10000,
-       },
-       {
-               name:           "pci-das64/m3/14",
-               device_id:      0,      // XXX
-               ai_se_chans:    64,
-               ai_bits:        14,
-               ai_speed:       333,
-               ao_nchan: 2,
-               ao_scan_speed:  10000,
-       },
-#endif
-
-};
-// Number of boards in cb_pcidas_boards
-#define N_BOARDS       (sizeof(pcidas64_boards) / sizeof(pcidas64_board))
-
-static struct pci_device_id pcidas64_pci_table[] __devinitdata = {
-       { PCI_VENDOR_ID_CB, 0x001d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
-       { PCI_VENDOR_ID_CB, 0x001e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
-       { PCI_VENDOR_ID_CB, 0x0035, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
-       { PCI_VENDOR_ID_CB, 0x0036, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
-       { PCI_VENDOR_ID_CB, 0x0037, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
-       { 0 }
-};
-MODULE_DEVICE_TABLE(pci, pcidas64_pci_table);
-
-/*
- * Useful for shorthand access to the particular board structure
- */
-#define thisboard ((pcidas64_board *)dev->board_ptr)
-
-/* this structure is for data unique to this hardware driver.  If
-   several hardware drivers keep similar information in this structure,
-   feel free to suggest moving the variable to the comedi_device struct.  */
-typedef struct
-{
-       // base addresses (physical)
-       unsigned long plx9080_phys_iobase;
-       unsigned long main_phys_iobase;
-       unsigned long dio_counter_phys_iobase;
-       // base addresses (ioremapped)
-       unsigned long plx9080_iobase;
-       unsigned long main_iobase;
-       unsigned long dio_counter_iobase;
-       // divisor of master clock for analog input pacing
-       unsigned int ai_divisor;
-       volatile unsigned int ai_count; // number of analog input samples remaining
-       // divisors of master clock for analog output pacing
-       unsigned int ao_divisor;
-       volatile unsigned int ao_count; // number of analog output samples remaining
-       unsigned int ao_value[2];       // remember what the analog outputs are set to, to allow readback
-       unsigned int hw_revision;       // stc chip hardware revision number
-       unsigned int do_bits;   // remember digital ouput levels
-} pcidas64_private;
-
-/*
- * most drivers define the following macro to make it easy to
- * access the private structure.
- */
-#define devpriv ((pcidas64_private *)dev->private)
-
-/*
- * The comedi_driver structure tells the Comedi core module
- * which functions to call to configure/deconfigure (attach/detach)
- * the board, and also about the kernel module that contains
- * the device code.
- */
-static int attach(comedi_device *dev,comedi_devconfig *it);
-static int detach(comedi_device *dev);
-comedi_driver driver_cb_pcidas={
-       driver_name:    "cb_pcidas64",
-       module:         THIS_MODULE,
-       attach:         attach,
-       detach:         detach,
-};
-
-static int ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
-static int ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
-static int ao_readback_insn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
-static int ai_cmd(comedi_device *dev,comedi_subdevice *s);
-static int ai_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd);
-//static int ao_cmd(comedi_device *dev,comedi_subdevice *s);
-//static int ao_inttrig(comedi_device *dev, comedi_subdevice *subdev, unsigned int trig_num);
-//static int ao_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd);
-static void handle_interrupt(int irq, void *d, struct pt_regs *regs);
-static int ai_cancel(comedi_device *dev, comedi_subdevice *s);
-//static int ao_cancel(comedi_device *dev, comedi_subdevice *s);
-static int dio_callback(int dir, int port, int data, void *arg);
-static int di_rbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
-static int do_wbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
-static void check_adc_timing(comedi_cmd *cmd);
-static unsigned int get_divisor(unsigned int ns, unsigned int flags);
-
-/*
- * A convenient macro that defines init_module() and cleanup_module(),
- * as necessary.
- */
-COMEDI_INITCLEANUP(driver_cb_pcidas);
-
-/*
- * Attach is called by the Comedi core to configure the driver
- * for a particular board.
- */
-static int attach(comedi_device *dev, comedi_devconfig *it)
-{
-       comedi_subdevice *s;
-       struct pci_dev* pcidev;
-       int index;
-       // base addresses
-       unsigned long plx9080_iobase;
-       unsigned long main_iobase;
-       unsigned long dio_counter_iobase;
-
-       printk("comedi%d: cb_pcidas64\n",dev->minor);
-
-/*
- * Allocate the private structure area.
- */
-       if(alloc_private(dev,sizeof(pcidas64_private)) < 0)
-               return -ENOMEM;
-
-/*
- * Probe the device to determine what device in the series it is.
- */
-
-       pci_for_each_dev(pcidev)
-       {
-               // is it not a computer boards card?
-               if(pcidev->vendor != PCI_VENDOR_ID_CB)
-                       continue;
-#ifdef PCIDAS64_DEBUG
-               printk(" found computer boards device id 0x%x on bus %i slot %i\n",
-                       pcidev->device, pcidev->bus->number, PCI_SLOT(pcidev->devfn));
-#endif
-               // loop through cards supported by this driver
-               for(index = 0; index < N_BOARDS; index++)
-               {
-                       if(pcidas64_boards[index].device_id != pcidev->device)
-                               continue;
-                       // was a particular bus/slot requested?
-                       if(it->options[0] || it->options[1])
-                       {
-                               // are we on the wrong bus/slot?
-                               if(pcidev->bus->number != it->options[0] ||
-                                  PCI_SLOT(pcidev->devfn) != it->options[1])
-                               {
-                                       continue;
-                               }
-                       }
-                       dev->board_ptr = pcidas64_boards + index;
-                       goto found;
-               }
-       }
-
-       printk("No supported ComputerBoards/MeasurementComputing card found\n");
-       return -EIO;
-
-found:
-
-       printk("Found %s on bus %i, slot %i\n", pcidas64_boards[index].name,
-               pcidev->bus->number, PCI_SLOT(pcidev->devfn));
-
-       //Initialize dev->board_name
-       dev->board_name = thisboard->name;
-
-       /* Initialize devpriv->control_status and devpriv->adc_fifo to point to
-        * their base address.
-        */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
-       plx9080_iobase =
-               pcidev->base_address[PLX9080_BADRINDEX] &
-               PCI_BASE_ADDRESS_MEM_MASK;
-       main_iobase =
-               pcidev->base_address[MAIN_BADRINDEX] &
-               PCI_BASE_ADDRESS_MEM_MASK;
-       dio_counter_iobase =
-               pcidev->base_address[DIO_COUNTER_BADRINDEX] &
-               PCI_BASE_ADDRESS_MEM_MASK;
-#else
-       if(pci_enable_device(pcidev))
-               return -EIO;
-       pci_set_master(pcidev);
-       plx9080_iobase =
-               pcidev->resource[PLX9080_BADRINDEX].start &
-               PCI_BASE_ADDRESS_MEM_MASK;
-       main_iobase =
-               pcidev->resource[MAIN_BADRINDEX].start &
-               PCI_BASE_ADDRESS_MEM_MASK;
-       dio_counter_iobase =
-               pcidev->resource[DIO_COUNTER_BADRINDEX].start &
-               PCI_BASE_ADDRESS_MEM_MASK;
-
-       if(check_mem_region(plx9080_iobase, PLX9080_IOSIZE))
-       {
-               /* Couldn't allocate io space */
-               printk(KERN_WARNING "couldn't allocate IO space\n");
-               return -EIO;
-       }
-       if(check_mem_region(main_iobase, MAIN_IOSIZE))
-       {
-               /* Couldn't allocate io space */
-               printk(KERN_WARNING "couldn't allocate IO space\n");
-               return -EIO;
-       }
-       if(check_mem_region(dio_counter_iobase, DIO_COUNTER_IOSIZE))
-       {
-               /* Couldn't allocate io space */
-               printk(KERN_WARNING "couldn't allocate IO space\n");
-               return -EIO;
-       }
-
-       request_mem_region(plx9080_iobase, PLX9080_IOSIZE, "cb_pcidas64");
-       devpriv->plx9080_phys_iobase = dio_counter_iobase;
-       request_mem_region(main_iobase, MAIN_IOSIZE, "cb_pcidas64");
-       devpriv->main_phys_iobase = dio_counter_iobase;
-       request_mem_region(dio_counter_iobase, DIO_COUNTER_IOSIZE, "cb_pcidas64");
-       devpriv->dio_counter_phys_iobase = dio_counter_iobase;
-
-#endif
-
-       // remap, won't work with 2.0 kernels but who cares
-       devpriv->plx9080_iobase = (unsigned long)ioremap(plx9080_iobase, PLX9080_IOSIZE);
-       devpriv->main_iobase = (unsigned long)ioremap(main_iobase, MAIN_IOSIZE);
-       devpriv->dio_counter_iobase = (unsigned long)ioremap(dio_counter_iobase, DIO_COUNTER_IOSIZE);
-
-       devpriv->hw_revision = HW_REVISION(readw(devpriv->main_iobase + HW_STATUS_REG));
-
-       // get irq
-/*     if(comedi_request_irq(pcidev->irq, handle_interrupt, SA_SHIRQ, "cb_pcidas64", dev ))
-       {
-               printk(" unable to allocate irq %d\n", pcidev->irq);
-               return -EINVAL;
-       }
-       dev->irq = pcidev->irq;
-*/
-#ifdef PCIDAS64_DEBUG
-
-printk(" plx9080 phys io addr 0x%lx\n", devpriv->plx9080_phys_iobase);
-printk(" main phys io addr 0x%lx\n", devpriv->main_phys_iobase);
-printk(" diocounter phys io addr 0x%lx\n", devpriv->dio_counter_phys_iobase);
-printk(" irq %i\n", dev->irq);
-
-printk(" plx9080 virt io addr 0x%lx\n", devpriv->plx9080_iobase);
-printk(" main virt io addr 0x%lx\n", devpriv->main_iobase);
-printk(" diocounter virt io addr 0x%lx\n", devpriv->dio_counter_iobase);
-printk(" irq %i\n", dev->irq);
-
-printk(" stc hardware revision %i\n", devpriv->hw_revision);
-
-// plx9080 dump
-printk(" plx interrupt status 0x%x\n", readl(devpriv->plx9080_iobase + PLX_INTRCS_REG));
-printk(" plx id bits 0x%x\n", readl(devpriv->plx9080_iobase + PLX_ID_REG));
-printk(" plx hardware revision 0x%x\n", readl(devpriv->plx9080_iobase + PLX_REVISION_REG));
-printk(" plx dma channel 0 mode 0x%x\n", readl(devpriv->plx9080_iobase + PLX_DMA0_MODE_REG));
-printk(" plx dma channel 0 pci address 0x%x\n", readl(devpriv->plx9080_iobase + PLX_DMA0_PCI_ADDRESS_REG));
-printk(" plx dma channel 0 local address 0x%x\n", readl(devpriv->plx9080_iobase + PLX_DMA0_LOCAL_ADDRESS_REG));
-printk(" plx dma channel 0 transfer size 0x%x\n", readl(devpriv->plx9080_iobase + PLX_DMA0_TRANSFER_SIZE_REG));
-printk(" plx dma channel 0 descriptor 0x%x\n", readl(devpriv->plx9080_iobase + PLX_DMA0_DESCRIPTOR_REG));
-printk(" plx dma channel 0 command status 0x%x\n", readl(devpriv->plx9080_iobase + PLX_DMA0_CS_REG));
-printk(" plx dma channel 0 threshold 0x%x\n", readl(devpriv->plx9080_iobase + PLX_DMA0_THRESHOLD_REG));
-
-#endif
-
-
-/*
- * Allocate the subdevice structures.
- */
-       dev->n_subdevices = 7;
-       if(alloc_subdevices(dev)<0)
-               return -ENOMEM;
-
-       s = dev->subdevices + 0;
-       /* analog input subdevice */
-       dev->read_subdev = s;
-       s->type = COMEDI_SUBD_AI;
-       s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF;
-       /* XXX Number of inputs in differential mode is ignored */
-       s->n_chan = thisboard->ai_se_chans;
-       s->len_chanlist = 8092;
-       s->maxdata = (1 << thisboard->ai_bits) - 1;
-       s->range_table = &ai_ranges;
-       s->insn_read = ai_rinsn;
-       //s->do_cmd = ai_cmd;
-       //s->do_cmdtest = ai_cmdtest;
-       //s->cancel = ai_cancel;
-
-       /* analog output subdevice */
-       s = dev->subdevices + 1;
-       if(thisboard->ao_nchan)
-       {
-       //      dev->write_subdev = s;
-               s->type = COMEDI_SUBD_AO;
-               s->subdev_flags = SDF_READABLE | SDF_WRITEABLE | SDF_GROUND;
-               s->n_chan = thisboard->ao_nchan;
-               // analog out resolution is the same as analog input resolution, so use ai_bits
-               s->maxdata = (1 << thisboard->ai_bits) - 1;
-               s->range_table = &ao_ranges;
-               s->insn_read = ao_readback_insn;
-               s->insn_write = ao_winsn;
-       //      s->do_cmdtest = ao_cmdtest;
-       //      s->do_cmd = ao_cmd;
-       //      s->len_chanlist = thisboard->ao_nchan;
-       //      s->cancel = ao_cancel;
-       } else
-       {
-               s->type = COMEDI_SUBD_UNUSED;
-       }
-
-       // digital input
-       s = dev->subdevices + 2;
-       s->type=COMEDI_SUBD_DI;
-       s->subdev_flags = SDF_READABLE;
-       s->n_chan = 4;
-       s->maxdata = 1;
-       s->range_table = &range_digital;
-       s->insn_bits = di_rbits;
-
-       // digital output
-       s = dev->subdevices + 3;
-       s->type=COMEDI_SUBD_DO;
-       s->subdev_flags = SDF_WRITEABLE | SDF_READABLE;
-       s->n_chan = 4;
-       s->maxdata = 1;
-       s->range_table = &range_digital;
-       s->insn_bits = do_wbits;
-
-       /* 8255 */
-       s = dev->subdevices + 4;
-       subdev_8255_init(dev, s, dio_callback,
-               (void*) (devpriv->dio_counter_iobase + DIO_8255_OFFSET));
-
-       // user counter subd XXX
-       s = dev->subdevices + 5;
-       s->type = COMEDI_SUBD_UNUSED;
-
-       // calibration subd XXX
-       s = dev->subdevices + 6;
-       s->type = COMEDI_SUBD_UNUSED; 
-
-       return 0;
-}
-
-
-/*
- * _detach is called to deconfigure a device.  It should deallocate
- * resources.
- * This function is also called when _attach() fails, so it should be
- * careful not to release resources that were not necessarily
- * allocated by _attach().  dev->private and dev->subdevices are
- * deallocated automatically by the core.
- */
-static int detach(comedi_device *dev)
-{
-       printk("comedi%d: cb_pcidas: remove\n",dev->minor);
-
-       if(devpriv)
-       {
-               if(devpriv->plx9080_iobase)
-                       iounmap((void*)devpriv->plx9080_iobase);
-               if(devpriv->main_iobase)
-                       iounmap((void*)devpriv->main_iobase);
-               if(devpriv->dio_counter_iobase)
-                       iounmap((void*)devpriv->dio_counter_iobase);
-               if(devpriv->plx9080_phys_iobase)
-                       release_mem_region(devpriv->plx9080_iobase, PLX9080_IOSIZE);
-               if(devpriv->main_iobase)
-                       release_mem_region(devpriv->main_phys_iobase, MAIN_IOSIZE);
-               if(devpriv->dio_counter_iobase)
-                       release_mem_region(devpriv->dio_counter_phys_iobase, DIO_COUNTER_IOSIZE);
-       }
-       if(dev->irq)
-               comedi_free_irq(dev->irq, dev);
-       if(dev->subdevices)
-               subdev_8255_cleanup(dev,dev->subdevices + 4);
-
-       return 0;
-}
-
-static int ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
-{
-       unsigned int bits, n, i;
-       const int timeout = 1000;
-
-       // disable card's interrupt sources
-       writew(0, devpriv->main_iobase + INTR_ENABLE_REG);
-
-       /* disable pacing, triggering, etc */
-       writew(ADC_ENABLE_BIT, devpriv->main_iobase + ADC_CONTROL0_REG);
-       writew(0, devpriv->main_iobase + ADC_CONTROL1_REG);
-
-       // use internal queue
-       writew(HW_CONFIG_DUMMY_BITS, devpriv->main_iobase + HW_CONFIG_REG);
-
-       // load internal queue
-       bits = 0;
-       // set channel
-       bits |= CHAN_BITS(CR_CHAN(insn->chanspec));
-       // set gain
-       bits |= GAIN_BITS(CR_RANGE(insn->chanspec));
-       // set unipolar / bipolar
-       bits |= UNIP_BIT(CR_RANGE(insn->chanspec));
-       // set single-ended / differential
-       if(CR_AREF(insn->chanspec) != AREF_DIFF)
-               bits |= SE_BIT;
-       // set stop channel
-       writew(CHAN_BITS(CR_CHAN(insn->chanspec)), devpriv->main_iobase + ADC_QUEUE_HIGH_REG);
-       // set start channel, and rest of settings
-       writew(bits, devpriv->main_iobase + ADC_QUEUE_LOAD_REG);
-
-       // clear adc buffer
-       writew(0, devpriv->main_iobase + ADC_BUFFER_CLEAR_REG);
-
-       for(n = 0; n < insn->n; n++)
-       {
-               /* trigger conversion */
-               writew(0, devpriv->main_iobase + ADC_CONVERT_REG);
-
-               // wait for data
-               for(i = 0; i < timeout; i++)
-               {
-                       if(!(readw(devpriv->main_iobase + HW_STATUS_REG) & ADC_BUSY_BIT))
-                               break;
-               }
-               if(i == timeout)
-               {
-                       comedi_error(dev, " analog input read insn timed out");
-                       return -ETIME;
-               }
-               data[n] = readw(devpriv->main_iobase + PIPE1_READ_REG);
-       }
-
-       return n;
-}
-
-static int ai_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd)
-{      int err = 0;
-       int tmp;
-       unsigned int tmp_arg, tmp_arg2;
-       int i;
-       int aref;
-
-       /* step 1: make sure trigger sources are trivially valid */
-
-       tmp = cmd->start_src;
-       cmd->start_src &= TRIG_NOW | TRIG_EXT;
-       if(!cmd->start_src || tmp != cmd->start_src) err++;
-
-       tmp = cmd->scan_begin_src;
-       cmd->scan_begin_src &= TRIG_TIMER | TRIG_FOLLOW;
-       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_EXT | 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 */
-
-       // uniqueness check
-       if(cmd->start_src != TRIG_NOW &&
-               cmd->start_src != TRIG_EXT) err++;
-       if(cmd->scan_begin_src != TRIG_TIMER &&
-               cmd->scan_begin_src != TRIG_FOLLOW) err++;
-       if(cmd->convert_src != TRIG_TIMER &&
-               cmd->convert_src != TRIG_EXT) err++;
-       if(cmd->stop_src != TRIG_COUNT &&
-               cmd->stop_src != TRIG_NONE &&
-               cmd->stop_src != TRIG_EXT) err++;
-
-       // compatibility check
-       if(cmd->convert_src == TRIG_EXT &&
-               cmd->scan_begin_src == TRIG_TIMER)\r
+/*\r
+    cb_pcidas64.c\r
+    This is a driver for the ComputerBoards/MeasurementComputing PCI-DAS\r
+    64xxx cards.\r
+\r
+    Author:  Frank Mori Hess <fmhess@uiuc.edu>\r
+\r
+    Options:\r
+    [0] - PCI bus number\r
+    [1] - PCI slot number\r
+\r
+    Copyright (C) 2001 Frank Mori Hess <fmhess@uiuc.edu>\r
+\r
+    COMEDI - Linux Control and Measurement Device Interface\r
+    Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>\r
+\r
+    This program is free software; you can redistribute it and/or modify\r
+    it under the terms of the GNU General Public License as published by\r
+    the Free Software Foundation; either version 2 of the License, or\r
+    (at your option) any later version.\r
+\r
+    This program is distributed in the hope that it will be useful,\r
+    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+    GNU General Public License for more details.\r
+\r
+    You should have received a copy of the GNU General Public License\r
+    along with this program; if not, write to the Free Software\r
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
+\r
+************************************************************************/\r
+\r
+/*\r
+\r
+Driver: cb_pcidas64.o\r
+Description: Driver for the ComputerBoards/MeasurementComputing\r
+   PCI-DAS64xxx series with the PLX 9080 PCI controller.\r
+Author: Frank Mori Hess <fmhess@uiuc.edu>\r
+Status: Experimental\r
+Updated: 2001-8-27\r
+Configuration options:\r
+   [0] - PCI bus of device (optional)\r
+   [1] - PCI slot of device (optional)\r
+\r
+Basic insn support should work, but untested as far as I know.\r
+Has command support for analog input, which may also work.  Still\r
+needs dma support to be added to support very fast aquisition rates.\r
+This driver is in need of stout-hearted testers who aren't afraid to\r
+crash their computers in the name of progress.\r
+Feel free to send and success/failure reports to author.\r
+\r
+*/\r
+\r
+/*\r
+\r
+TODO:\r
+       command support for ao\r
+       pci-dma support\r
+       calibration subdevice\r
+       user counter subdevice\r
+       there are a number of boards this driver will support when they are\r
+               fully released, but does not since yet since the pci device id numbers\r
+               are not yet available.\r
+       need to take care to prevent ai and ao from affecting each others register bits\r
+       support prescaled 100khz clock for slow pacing\r
+*/\r
+\r
+#include <linux/kernel.h>\r
+#include <linux/module.h>\r
+#include <linux/sched.h>\r
+#include <linux/mm.h>\r
+#include <linux/malloc.h>\r
+#include <linux/errno.h>\r
+#include <linux/ioport.h>\r
+#include <linux/delay.h>\r
+#include <linux/interrupt.h>\r
+#include <linux/timex.h>\r
+#include <linux/timer.h>\r
+#include <linux/pci.h>\r
+#include <linux/init.h>\r
+#include <asm/io.h>\r
+#include <linux/comedidev.h>\r
+#include "8253.h"\r
+#include "8255.h"\r
+#include "plx9080.h"\r
+\r
+#define PCIDAS64_DEBUG // enable debugging code\r
+//#undef PCIDAS64_DEBUG        // disable debugging code\r
+\r
+// PCI vendor number of ComputerBoards/MeasurementComputing\r
+#define PCI_VENDOR_ID_CB       0x1307\r
+#define TIMER_BASE 25  // 40MHz master clock\r
+#define PRESCALED_TIMER_BASE   10000   // 100kHz 'prescaled' clock for slow aquisition, maybe I'll support this someday\r
+#define QUARTER_AI_FIFO_SIZE 2048      // 1/4 analog input fifo size\r
+\r
+/* PCI-DAS64xxx base addresses */\r
+\r
+// indices of base address regions\r
+#define PLX9080_BADRINDEX 0\r
+#define MAIN_BADRINDEX 2\r
+#define DIO_COUNTER_BADRINDEX 3\r
+\r
+#define PLX9080_IOSIZE 0xec\r
+#define MAIN_IOSIZE 0x302\r
+#define DIO_COUNTER_IOSIZE 0x29\r
+\r
+// devpriv->main_iobase registers\r
+// write-only\r
+#define INTR_ENABLE_REG        0x0     // interrupt enable register\r
+#define    EN_ADC_OVERRUN_BIT  0x8000  // enable adc overrun status bit\r
+#define    EN_DAC_UNDERRUN_BIT 0x4000  // enable dac underrun status bit\r
+#define    EN_ADC_DONE_INTR_BIT        0x8     // enable adc aquisition done interrupt\r
+#define    EN_ADC_INTR_SRC_BIT 0x4     // enable adc interrupt source\r
+#define    ADC_INTR_SRC_MASK   0x3     // bits that set adc interrupt source\r
+#define    ADC_INTR_QFULL_BITS 0x0     // interrupt fifo quater full\r
+#define    ADC_INTR_EOC_BITS   0x1     // interrupt end of conversion\r
+#define    ADC_INTR_EOSCAN_BITS        0x2     // interrupt end of scan\r
+#define    ADC_INTR_EOSEQ_BITS 0x3     // interrupt end of sequence (probably wont use this it's pretty fancy)\r
+#define HW_CONFIG_REG  0x2     // hardware config register\r
+#define    HW_CONFIG_DUMMY_BITS        0x2400  // bits that don't do anything yet but are given default values\r
+#define    HW_CONFIG_DUMMY_BITS_6402   0x0400  // dummy bits in 6402 manual are slightly different, probably doesn't matter\r
+#define    EXT_QUEUE   0x200   // use external channel/gain queue (more versatile than internal queue)\r
+#define FIFO_SIZE_REG  0x4     // allows adjustment of fifo sizes, we will always use maximum\r
+#define    FIFO_SIZE_DUMMY_BITS        0xf038  // bits that don't do anything yet but are given default values\r
+#define    ADC_FIFO_SIZE_MASK  0x7     // bits that set adc fifo size\r
+#define    ADC_FIFO_8K_BITS    0x0     // 8 kilosample adc fifo\r
+#define    DAC_FIFO_SIZE_MASK  0xf00   // bits that set dac fifo size\r
+#define    DAC_FIFO_16K_BITS 0x0\r
+#define ADC_CONTROL0_REG       0x10    // adc control register 0\r
+#define    TRIG1_FALLING_BIT   0x20    // trig 1 uses falling edge\r
+#define    ADC_EXT_CONV_FALLING_BIT    0x800   // external pacing uses falling edge\r
+#define    ADC_ENABLE_BIT      0x8000  // master adc enable\r
+#define ADC_CONTROL1_REG       0x12    // adc control register 1\r
+#define    ADC_CONTROL1_DUMMY_BITS     0x1     // dummy bits for adc control register 1\r
+#define    SW_NOGATE_BIT       0x40    // disables software gate of adc\r
+#define    ADC_MODE_BITS(x)    (((x) & 0xf) << 12)\r
+#define ADC_SAMPLE_INTERVAL_LOWER_REG  0x16    // lower 16 bits of sample interval counter\r
+#define ADC_SAMPLE_INTERVAL_UPPER_REG  0x18    // upper 8 bits of sample interval counter\r
+#define ADC_DELAY_INTERVAL_LOWER_REG   0x1a    // lower 16 bits of delay interval counter\r
+#define ADC_DELAY_INTERVAL_UPPER_REG   0x1c    // upper 8 bits of delay interval counter\r
+#define ADC_COUNT_LOWER_REG    0x1e    // lower 16 bits of hardware conversion/scan counter\r
+#define ADC_COUNT_UPPER_REG    0x20    // upper 8 bits of hardware conversion/scan counter\r
+#define ADC_START_REG  0x22    // software trigger to start aquisition\r
+#define ADC_CONVERT_REG        0x24    // initiates single conversion\r
+#define ADC_QUEUE_CLEAR_REG    0x26    // clears adc queue\r
+#define ADC_QUEUE_LOAD_REG     0x28    // loads adc queue\r
+#define    CHAN_BITS(x)        ((x) & 0x3f)\r
+#define    GAIN_BITS(x)        (((x) & 0x3) << 8)      // translates range index to gain bits\r
+#define    UNIP_BIT(x) (((x) & 0x4) << 11)     // translates range index to unipolar/bipolar bit\r
+#define    SE_BIT      0x1000  // single-ended/ differential bit\r
+#define    QUEUE_EOSEQ_BIT     0x4000  // queue end of sequence\r
+#define    QUEUE_EOSCAN_BIT    0x8000  // queue end of scan\r
+#define ADC_BUFFER_CLEAR_REG   0x2a\r
+#define ADC_QUEUE_HIGH_REG     0x2c    // high channel for internal queue, use CHAN_BITS() macro above\r
+#define DAC_CONTROL0_REG       0x50    // dac control register 0\r
+#define    DAC_ENABLE_BIT      0x8000  // dac controller enable bit\r
+#define DAC_CONTROL1_REG       0x52    // dac control register 0\r
+#define    DAC_RANGE_BITS(channel, range)      (((range) & 0x3) << (2 * ((channel) & 0x1)))\r
+#define    DAC_OUTPUT_ENABLE_BIT       0x80    // dac output enable bit\r
+#define DAC_BUFFER_CLEAR_REG 0x66      // clear dac buffer\r
+#define DAC_CONVERT_REG(channel)       ((0x70) + (2 * ((channel) & 0x1)))\r
+// read-only\r
+#define HW_STATUS_REG  0x0     // hardware status register, reading this apparently clears pending interrupts as well\r
+#define   DAC_UNDERRUN_BIT     0x1\r
+#define   ADC_OVERRUN_BIT 0x2\r
+#define   DAC_ACTIVE_BIT       0x4\r
+#define   ADC_ACTIVE_BIT       0x8\r
+#define   DAC_INTR_PENDING_BIT 0x10\r
+#define   ADC_INTR_PENDING_BIT 0x20\r
+#define   DAC_DONE_BIT 0x40\r
+#define   ADC_DONE_BIT 0x80\r
+#define   EXT_INTR_PENDING_BIT 0x100\r
+#define   ADC_STOP_BIT 0x200\r
+#define   PIPE_FULL_BIT(x)     (0x400 << ((x) & 0x1))\r
+#define   HW_REVISION(x)       (((x) >> 12) & 0xf)\r
+#define PIPE1_READ_REG 0x4\r
+// read-write\r
+#define ADC_QUEUE_FIFO_REG     0x100   // external channel/gain queue, uses same bits as ADC_QUEUE_LOAD_REG\r
+#define ADC_FIFO_REG 0x200     // adc data fifo\r
+\r
+// devpriv->dio_counter_iobase registers\r
+#define DIO_8255_OFFSET        0x0\r
+#define DO_REG 0x20\r
+#define DI_REG 0x28\r
+\r
+// analog input ranges for most boards\r
+static comedi_lrange ai_ranges =\r
+{\r
+       8,\r
+       {\r
+               BIP_RANGE(10),\r
+               BIP_RANGE(5),\r
+               BIP_RANGE(2.5),\r
+               BIP_RANGE(1.25),\r
+               UNI_RANGE(10),\r
+               UNI_RANGE(5),\r
+               UNI_RANGE(2.5),\r
+               UNI_RANGE(1.25)\r
+       }\r
+};\r
+\r
+// analog output ranges\r
+static comedi_lrange ao_ranges =\r
+{\r
+       4,\r
+       {\r
+               BIP_RANGE(5),\r
+               BIP_RANGE(10),\r
+               UNI_RANGE(5),\r
+               UNI_RANGE(10),\r
+       }\r
+};\r
+\r
+typedef struct pcidas64_board_struct\r
+{\r
+       char *name;\r
+       int device_id;  // pci device id\r
+       int ai_se_chans;        // number of ai inputs in single-ended mode\r
+       int ai_bits;    // analog input resolution\r
+       int ai_speed;   // fastest conversion period in ns\r
+       int ao_nchan;   // number of analog out channels\r
+       int ao_scan_speed;      // analog output speed (for a scan, not conversion)\r
+} pcidas64_board;\r
+\r
+static pcidas64_board pcidas64_boards[] =\r
+{\r
+       {\r
+               name:           "pci-das6402/16",\r
+               device_id:      0x1d,\r
+               ai_se_chans:    64,\r
+               ai_bits:        16,\r
+               ai_speed:       5000,\r
+               ao_nchan: 2,\r
+               ao_scan_speed:  10000,\r
+       },\r
+       {\r
+               name:           "pci-das6402/12",       // XXX check\r
+               device_id:      0x1e,\r
+               ai_se_chans:    64,\r
+               ai_bits:        12,\r
+               ai_speed:       5000,\r
+               ao_nchan: 2,\r
+               ao_scan_speed:  10000,\r
+       },\r
+       {\r
+               name:           "pci-das64/m1/16",\r
+               device_id:      0x35,\r
+               ai_se_chans:    64,\r
+               ai_bits:        16,\r
+               ai_speed:       1000,\r
+               ao_nchan: 2,\r
+               ao_scan_speed:  10000,\r
+       },\r
+       {\r
+               name:           "pci-das64/m2/16",\r
+               device_id:      0x36,\r
+               ai_se_chans:    64,\r
+               ai_bits:        16,\r
+               ai_speed:       500,\r
+               ao_nchan: 2,\r
+               ao_scan_speed:  10000,\r
+       },\r
+       {\r
+               name:           "pci-das64/m3/16",\r
+               device_id:      0x37,\r
+               ai_se_chans:    64,\r
+               ai_bits:        16,\r
+               ai_speed:       333,\r
+               ao_nchan: 2,\r
+               ao_scan_speed:  10000,\r
+       },\r
+\r
+#if 0\r
+       {\r
+               name:           "pci-das6402/16/jr",\r
+               device_id:      0 // XXX,\r
+               ai_se_chans:    64,\r
+               ai_bits:        16,\r
+               ai_speed:       5000,\r
+               ao_nchan: 0,\r
+               ao_scan_speed:  10000,\r
+       },\r
+       {\r
+               name:           "pci-das64/m1/16/jr",\r
+               device_id:      0 // XXX,\r
+               ai_se_chans:    64,\r
+               ai_bits:        16,\r
+               ai_speed:       1000,\r
+               ao_nchan: 0,\r
+               ao_scan_speed:  10000,\r
+       },\r
+       {\r
+               name:           "pci-das64/m2/16/jr",\r
+               device_id:      0 // XXX,\r
+               ai_se_chans:    64,\r
+               ai_bits:        16,\r
+               ai_speed:       500,\r
+               ao_nchan: 0,\r
+               ao_scan_speed:  10000,\r
+       },\r
+       {\r
+               name:           "pci-das64/m3/16/jr",\r
+               device_id:      0 // XXX,\r
+               ai_se_chans:    64,\r
+               ai_bits:        16,\r
+               ai_speed:       333,\r
+               ao_nchan: 0,\r
+               ao_scan_speed:  10000,\r
+       },\r
+       {\r
+               name:           "pci-das64/m1/14",\r
+               device_id:      0,      // XXX\r
+               ai_se_chans:    64,\r
+               ai_bits:        14,\r
+               ai_speed:       1000,\r
+               ao_nchan: 2,\r
+               ao_scan_speed:  10000,\r
+       },\r
+       {\r
+               name:           "pci-das64/m2/14",\r
+               device_id:      0,      // XXX\r
+               ai_se_chans:    64,\r
+               ai_bits:        14,\r
+               ai_speed:       500,\r
+               ao_nchan: 2,\r
+               ao_scan_speed:  10000,\r
+       },\r
+       {\r
+               name:           "pci-das64/m3/14",\r
+               device_id:      0,      // XXX\r
+               ai_se_chans:    64,\r
+               ai_bits:        14,\r
+               ai_speed:       333,\r
+               ao_nchan: 2,\r
+               ao_scan_speed:  10000,\r
+       },\r
+#endif\r
+\r
+};\r
+// Number of boards in cb_pcidas_boards\r
+#define N_BOARDS       (sizeof(pcidas64_boards) / sizeof(pcidas64_board))\r
+\r
+static struct pci_device_id pcidas64_pci_table[] __devinitdata = {\r
+       { PCI_VENDOR_ID_CB, 0x001d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },\r
+       { PCI_VENDOR_ID_CB, 0x001e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },\r
+       { PCI_VENDOR_ID_CB, 0x0035, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },\r
+       { PCI_VENDOR_ID_CB, 0x0036, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },\r
+       { PCI_VENDOR_ID_CB, 0x0037, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },\r
+       { 0 }\r
+};\r
+MODULE_DEVICE_TABLE(pci, pcidas64_pci_table);\r
+\r
+/*\r
+ * Useful for shorthand access to the particular board structure\r
+ */\r
+#define thisboard ((pcidas64_board *)dev->board_ptr)\r
+\r
+/* this structure is for data unique to this hardware driver.  If\r
+   several hardware drivers keep similar information in this structure,\r
+   feel free to suggest moving the variable to the comedi_device struct.  */\r
+typedef struct\r
+{\r
+       // base addresses (physical)\r
+       unsigned long plx9080_phys_iobase;\r
+       unsigned long main_phys_iobase;\r
+       unsigned long dio_counter_phys_iobase;\r
+       // base addresses (ioremapped)\r
+       unsigned long plx9080_iobase;\r
+       unsigned long main_iobase;\r
+       unsigned long dio_counter_iobase;\r
+       // divisor of master clock for analog input pacing\r
+       unsigned int ai_divisor;\r
+       volatile unsigned int ai_count; // number of analog input samples remaining\r
+       // divisors of master clock for analog output pacing\r
+       unsigned int ao_divisor;\r
+       volatile unsigned int ao_count; // number of analog output samples remaining\r
+       unsigned int ao_value[2];       // remember what the analog outputs are set to, to allow readback\r
+       unsigned int hw_revision;       // stc chip hardware revision number\r
+       unsigned int do_bits;   // remember digital ouput levels\r
+} pcidas64_private;\r
+\r
+/*\r
+ * most drivers define the following macro to make it easy to\r
+ * access the private structure.\r
+ */\r
+#define devpriv ((pcidas64_private *)dev->private)\r
+\r
+/*\r
+ * The comedi_driver structure tells the Comedi core module\r
+ * which functions to call to configure/deconfigure (attach/detach)\r
+ * the board, and also about the kernel module that contains\r
+ * the device code.\r
+ */\r
+static int attach(comedi_device *dev,comedi_devconfig *it);\r
+static int detach(comedi_device *dev);\r
+comedi_driver driver_cb_pcidas={\r
+       driver_name:    "cb_pcidas64",\r
+       module:         THIS_MODULE,\r
+       attach:         attach,\r
+       detach:         detach,\r
+};\r
+\r
+static int ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);\r
+static int ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);\r
+static int ao_readback_insn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);\r
+static int ai_cmd(comedi_device *dev,comedi_subdevice *s);\r
+static int ai_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd);\r
+//static int ao_cmd(comedi_device *dev,comedi_subdevice *s);\r
+//static int ao_inttrig(comedi_device *dev, comedi_subdevice *subdev, unsigned int trig_num);\r
+//static int ao_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd);\r
+static void handle_interrupt(int irq, void *d, struct pt_regs *regs);\r
+static int ai_cancel(comedi_device *dev, comedi_subdevice *s);\r
+//static int ao_cancel(comedi_device *dev, comedi_subdevice *s);\r
+static int dio_callback(int dir, int port, int data, void *arg);\r
+static int di_rbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);\r
+static int do_wbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);\r
+static void check_adc_timing(comedi_cmd *cmd);\r
+static unsigned int get_divisor(unsigned int ns, unsigned int flags);\r
+\r
+/*\r
+ * A convenient macro that defines init_module() and cleanup_module(),\r
+ * as necessary.\r
+ */\r
+COMEDI_INITCLEANUP(driver_cb_pcidas);\r
+\r
+/*\r
+ * Attach is called by the Comedi core to configure the driver\r
+ * for a particular board.\r
+ */\r
+static int attach(comedi_device *dev, comedi_devconfig *it)\r
+{\r
+       comedi_subdevice *s;\r
+       struct pci_dev* pcidev;\r
+       int index;\r
+       // base addresses\r
+       unsigned long plx9080_iobase;\r
+       unsigned long main_iobase;\r
+       unsigned long dio_counter_iobase;\r
+\r
+       printk("comedi%d: cb_pcidas64\n",dev->minor);\r
+\r
+/*\r
+ * Allocate the private structure area.\r
+ */\r
+       if(alloc_private(dev,sizeof(pcidas64_private)) < 0)\r
+               return -ENOMEM;\r
+\r
+/*\r
+ * Probe the device to determine what device in the series it is.\r
+ */\r
+\r
+       pci_for_each_dev(pcidev)\r
+       {\r
+               // is it not a computer boards card?\r
+               if(pcidev->vendor != PCI_VENDOR_ID_CB)\r
+                       continue;\r
+#ifdef PCIDAS64_DEBUG\r
+               printk(" found computer boards device id 0x%x on bus %i slot %i\n",\r
+                       pcidev->device, pcidev->bus->number, PCI_SLOT(pcidev->devfn));\r
+#endif\r
+               // loop through cards supported by this driver\r
+               for(index = 0; index < N_BOARDS; index++)\r
+               {\r
+                       if(pcidas64_boards[index].device_id != pcidev->device)\r
+                               continue;\r
+                       // was a particular bus/slot requested?\r
+                       if(it->options[0] || it->options[1])\r
+                       {\r
+                               // are we on the wrong bus/slot?\r
+                               if(pcidev->bus->number != it->options[0] ||\r
+                                  PCI_SLOT(pcidev->devfn) != it->options[1])\r
+                               {\r
+                                       continue;\r
+                               }\r
+                       }\r
+                       dev->board_ptr = pcidas64_boards + index;\r
+                       goto found;\r
+               }\r
+       }\r
+\r
+       printk("No supported ComputerBoards/MeasurementComputing card found\n");\r
+       return -EIO;\r
+\r
+found:\r
+\r
+       printk("Found %s on bus %i, slot %i\n", pcidas64_boards[index].name,\r
+               pcidev->bus->number, PCI_SLOT(pcidev->devfn));\r
+\r
+       //Initialize dev->board_name\r
+       dev->board_name = thisboard->name;\r
+\r
+       /* Initialize devpriv->control_status and devpriv->adc_fifo to point to\r
+        * their base address.\r
+        */\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)\r
+       plx9080_iobase =\r
+               pcidev->base_address[PLX9080_BADRINDEX] &\r
+               PCI_BASE_ADDRESS_MEM_MASK;\r
+       main_iobase =\r
+               pcidev->base_address[MAIN_BADRINDEX] &\r
+               PCI_BASE_ADDRESS_MEM_MASK;\r
+       dio_counter_iobase =\r
+               pcidev->base_address[DIO_COUNTER_BADRINDEX] &\r
+               PCI_BASE_ADDRESS_MEM_MASK;\r
+#else\r
+       if(pci_enable_device(pcidev))\r
+               return -EIO;\r
+       pci_set_master(pcidev);\r
+       plx9080_iobase =\r
+               pcidev->resource[PLX9080_BADRINDEX].start &\r
+               PCI_BASE_ADDRESS_MEM_MASK;\r
+       main_iobase =\r
+               pcidev->resource[MAIN_BADRINDEX].start &\r
+               PCI_BASE_ADDRESS_MEM_MASK;\r
+       dio_counter_iobase =\r
+               pcidev->resource[DIO_COUNTER_BADRINDEX].start &\r
+               PCI_BASE_ADDRESS_MEM_MASK;\r
+\r
+       if(check_mem_region(plx9080_iobase, PLX9080_IOSIZE))\r
+       {\r
+               /* Couldn't allocate io space */\r
+               printk(KERN_WARNING "couldn't allocate IO space\n");\r
+               return -EIO;\r
+       }\r
+       if(check_mem_region(main_iobase, MAIN_IOSIZE))\r
+       {\r
+               /* Couldn't allocate io space */\r
+               printk(KERN_WARNING "couldn't allocate IO space\n");\r
+               return -EIO;\r
+       }\r
+       if(check_mem_region(dio_counter_iobase, DIO_COUNTER_IOSIZE))\r
+       {\r
+               /* Couldn't allocate io space */\r
+               printk(KERN_WARNING "couldn't allocate IO space\n");\r
+               return -EIO;\r
+       }\r
+\r
+       request_mem_region(plx_iobase, PLX9080_IOSIZE, "cb_pcidas64");\r
+       devpriv->plx9080_phys_iobase = dio_counter_iobase;\r
+       request_mem_region(main_iobase, MAIN_IOSIZE, "cb_pcidas64");\r
+       devpriv->main_phys_iobase = dio_counter_iobase;\r
+       request_mem_region(dio_counter_iobase, DIO_COUNTER_IOSIZE, "cb_pcidas64");\r
+       devpriv->dio_counter_phys_iobase = dio_counter_iobase;\r
+\r
+#endif\r
+\r
+       // remap, won't work with 2.0 kernels but who cares\r
+       devpriv->plx9080_iobase = (unsigned long)ioremap(plx9080_iobase, PLX9080_IOSIZE);\r
+       devpriv->main_iobase = (unsigned long)ioremap(main_iobase, MAIN_IOSIZE);\r
+       devpriv->dio_counter_iobase = (unsigned long)ioremap(dio_counter_iobase, DIO_COUNTER_IOSIZE);\r
+\r
+       devpriv->hw_revision = HW_REVISION(readw(devpriv->main_iobase + HW_STATUS_REG));\r
+\r
+       // get irq\r
+       if(comedi_request_irq(pcidev->irq, handle_interrupt, SA_SHIRQ, "cb_pcidas64", dev ))\r
+       {\r
+               printk(" unable to allocate irq %d\n", pcidev->irq);\r
+               return -EINVAL;\r
+       }\r
+       dev->irq = pcidev->irq;\r
+\r
+#ifdef PCIDAS64_DEBUG\r
+\r
+printk(" plx9080 phys io addr 0x%lx\n", devpriv->plx9080_phys_iobase);\r
+printk(" main phys io addr 0x%lx\n", devpriv->main_phys_iobase);\r
+printk(" diocounter phys io addr 0x%lx\n", devpriv->dio_counter_phys_iobase);\r
+printk(" irq %i\n", dev->irq);\r
+\r
+printk(" plx9080 virt io addr 0x%lx\n", devpriv->plx9080_iobase);\r
+printk(" main virt io addr 0x%lx\n", devpriv->main_iobase);\r
+printk(" diocounter virt io addr 0x%lx\n", devpriv->dio_counter_iobase);\r
+printk(" irq %i\n", dev->irq);\r
+\r
+printk(" stc hardware revision %i\n", devpriv->hw_revision);\r
+\r
+// plx9080 dump\r
+printk(" plx interrupt status 0x%x\n", readl(devpriv->plx9080_iobase + PLX_INTRCS_REG));\r
+printk(" plx id bits 0x%x\n", readl(devpriv->plx9080_iobase + PLX_ID_REG));\r
+printk(" plx hardware revision 0x%x\n", readl(devpriv->plx9080_iobase + PLX_REVISION_REG));\r
+printk(" plx dma channel 0 mode 0x%x\n", readl(devpriv->plx9080_iobase + PLX_DMA0_MODE_REG));\r
+printk(" plx dma channel 0 pci address 0x%x\n", readl(devpriv->plx9080_iobase + PLX_DMA0_PCI_ADDRESS_REG));\r
+printk(" plx dma channel 0 local address 0x%x\n", readl(devpriv->plx9080_iobase + PLX_DMA0_LOCAL_ADDRESS_REG));\r
+printk(" plx dma channel 0 transfer size 0x%x\n", readl(devpriv->plx9080_iobase + PLX_DMA0_TRANSFER_SIZE_REG));\r
+printk(" plx dma channel 0 descriptor 0x%x\n", readl(devpriv->plx9080_iobase + PLX_DMA0_DESCRIPTOR_REG));\r
+printk(" plx dma channel 0 command status 0x%x\n", readl(devpriv->plx9080_iobase + PLX_DMA0_CS_REG));\r
+printk(" plx dma channel 0 threshold 0x%x\n", readl(devpriv->plx9080_iobase + PLX_DMA0_THRESHOLD_REG));\r
+\r
+#endif\r
+\r
+\r
+/*\r
+ * Allocate the subdevice structures.\r
+ */\r
+       dev->n_subdevices = 7;\r
+       if(alloc_subdevices(dev)<0)\r
+               return -ENOMEM;\r
+\r
+       s = dev->subdevices + 0;\r
+       /* analog input subdevice */\r
+       dev->read_subdev = s;\r
+       s->type = COMEDI_SUBD_AI;\r
+       s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF;\r
+       /* XXX Number of inputs in differential mode is ignored */\r
+       s->n_chan = thisboard->ai_se_chans;\r
+       s->len_chanlist = 8092;\r
+       s->maxdata = (1 << thisboard->ai_bits) - 1;\r
+       s->range_table = &ai_ranges;\r
+       s->insn_read = ai_rinsn;\r
+       s->do_cmd = ai_cmd;\r
+       s->do_cmdtest = ai_cmdtest;\r
+       s->cancel = ai_cancel;\r
+\r
+       /* analog output subdevice */\r
+       s = dev->subdevices + 1;\r
+       if(thisboard->ao_nchan)\r
+       {\r
+       //      dev->write_subdev = s;\r
+               s->type = COMEDI_SUBD_AO;\r
+               s->subdev_flags = SDF_READABLE | SDF_WRITEABLE | SDF_GROUND;\r
+               s->n_chan = thisboard->ao_nchan;\r
+               // analog out resolution is the same as analog input resolution, so use ai_bits\r
+               s->maxdata = (1 << thisboard->ai_bits) - 1;\r
+               s->range_table = &ao_ranges;\r
+               s->insn_read = ao_readback_insn;\r
+               s->insn_write = ao_winsn;\r
+       //      s->do_cmdtest = ao_cmdtest;\r
+       //      s->do_cmd = ao_cmd;\r
+       //      s->len_chanlist = thisboard->ao_nchan;\r
+       //      s->cancel = ao_cancel;\r
+       } else\r
+       {\r
+               s->type = COMEDI_SUBD_UNUSED;\r
+       }\r
+\r
+       // digital input\r
+       s = dev->subdevices + 2;\r
+       s->type=COMEDI_SUBD_DI;\r
+       s->subdev_flags = SDF_READABLE;\r
+       s->n_chan = 4;\r
+       s->maxdata = 1;\r
+       s->range_table = &range_digital;\r
+       s->insn_bits = di_rbits;\r
+\r
+       // digital output\r
+       s = dev->subdevices + 3;\r
+       s->type=COMEDI_SUBD_DO;\r
+       s->subdev_flags = SDF_WRITEABLE | SDF_READABLE;\r
+       s->n_chan = 4;\r
+       s->maxdata = 1;\r
+       s->range_table = &range_digital;\r
+       s->insn_bits = do_wbits;\r
+\r
+       /* 8255 */\r
+       s = dev->subdevices + 4;\r
+       subdev_8255_init(dev, s, dio_callback,\r
+               (void*) (devpriv->dio_counter_iobase + DIO_8255_OFFSET));\r
+\r
+       // user counter subd XXX\r
+       s = dev->subdevices + 5;\r
+       s->type = COMEDI_SUBD_UNUSED;\r
+\r
+       // calibration subd XXX\r
+       s = dev->subdevices + 6;\r
+       s->type = COMEDI_SUBD_UNUSED; \r
+\r
+       return 0;\r
+}\r
+\r
+\r
+/*\r
+ * _detach is called to deconfigure a device.  It should deallocate\r
+ * resources.\r
+ * This function is also called when _attach() fails, so it should be\r
+ * careful not to release resources that were not necessarily\r
+ * allocated by _attach().  dev->private and dev->subdevices are\r
+ * deallocated automatically by the core.\r
+ */\r
+static int detach(comedi_device *dev)\r
+{\r
+       printk("comedi%d: cb_pcidas: remove\n",dev->minor);\r
+\r
+       if(devpriv)\r
+       {\r
+               if(devpriv->plx9080_iobase)\r
+                       iounmap((void*)devpriv->plx9080_iobase);\r
+               if(devpriv->main_iobase)\r
+                       iounmap((void*)devpriv->main_iobase);\r
+               if(devpriv->dio_counter_iobase)\r
+                       iounmap((void*)devpriv->dio_counter_iobase);\r
+               if(devpriv->plx9080_phys_iobase)\r
+                       release_mem_region(devpriv->plx9080_iobase, PLX9080_IOSIZE);\r
+               if(devpriv->main_iobase)\r
+                       release_mem_region(devpriv->main_phys_iobase, MAIN_IOSIZE);\r
+               if(devpriv->dio_counter_iobase)\r
+                       release_mem_region(devpriv->dio_counter_phys_iobase, DIO_COUNTER_IOSIZE);\r
+       }\r
+       if(dev->irq)\r
+               comedi_free_irq(dev->irq, dev);\r
+       if(dev->subdevices)\r
+               subdev_8255_cleanup(dev,dev->subdevices + 4);\r
+\r
+       return 0;\r
+}\r
+\r
+static int ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)\r
+{\r
+       unsigned int bits, n, i;\r
+       const int timeout = 1000;\r
+\r
+       // disable card's interrupt sources\r
+       writew(0, devpriv->main_iobase + INTR_ENABLE_REG);\r
+\r
+       /* disable pacing, triggering, etc */\r
+       writew(ADC_ENABLE_BIT, devpriv->main_iobase + ADC_CONTROL0_REG);\r
+       writew(ADC_CONTROL1_DUMMY_BITS, devpriv->main_iobase + ADC_CONTROL1_REG);\r
+\r
+       // use internal queue\r
+       writew(HW_CONFIG_DUMMY_BITS, devpriv->main_iobase + HW_CONFIG_REG);\r
+\r
+       // load internal queue\r
+       bits = 0;\r
+       // set channel\r
+       bits |= CHAN_BITS(CR_CHAN(insn->chanspec));\r
+       // set gain\r
+       bits |= GAIN_BITS(CR_RANGE(insn->chanspec));\r
+       // set unipolar / bipolar\r
+       bits |= UNIP_BIT(CR_RANGE(insn->chanspec));\r
+       // set single-ended / differential\r
+       if(CR_AREF(insn->chanspec) != AREF_DIFF)\r
+               bits |= SE_BIT;\r
+       // set stop channel\r
+       writew(CHAN_BITS(CR_CHAN(insn->chanspec)), devpriv->main_iobase + ADC_QUEUE_HIGH_REG);\r
+       // set start channel, and rest of settings\r
+       writew(bits, devpriv->main_iobase + ADC_QUEUE_LOAD_REG);\r
+\r
+       // clear adc buffer\r
+       writew(0, devpriv->main_iobase + ADC_BUFFER_CLEAR_REG);\r
+\r
+       for(n = 0; n < insn->n; n++)\r
+       {\r
+               /* trigger conversion */\r
+               writew(0, devpriv->main_iobase + ADC_CONVERT_REG);\r
+\r
+               // wait for data\r
+               for(i = 0; i < timeout; i++)\r
+               {\r
+                       if(!(readw(devpriv->main_iobase + HW_STATUS_REG) & ADC_ACTIVE_BIT))\r
+                               break;\r
+               }\r
+               if(i == timeout)\r
+               {\r
+                       comedi_error(dev, " analog input read insn timed out");\r
+                       return -ETIME;\r
+               }\r
+               data[n] = readw(devpriv->main_iobase + PIPE1_READ_REG);\r
+       }\r
+\r
+       return n;\r
+}\r
+\r
+static int ai_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd)\r
+{      int err = 0;\r
+       int tmp;\r
+       unsigned int tmp_arg, tmp_arg2;\r
+       int i;\r
+       int aref;\r
+\r
+       /* step 1: make sure trigger sources are trivially valid */\r
+\r
+       tmp = cmd->start_src;\r
+       cmd->start_src &= TRIG_NOW | TRIG_EXT;\r
+       if(!cmd->start_src || tmp != cmd->start_src) err++;\r
+\r
+       tmp = cmd->scan_begin_src;\r
+       cmd->scan_begin_src &= TRIG_TIMER | TRIG_FOLLOW;\r
+       if(!cmd->scan_begin_src || tmp != cmd->scan_begin_src) err++;\r
+\r
+       tmp = cmd->convert_src;\r
+       cmd->convert_src &= TRIG_TIMER | TRIG_EXT;\r
+       if(!cmd->convert_src || tmp != cmd->convert_src) err++;\r
+\r
+       tmp = cmd->scan_end_src;\r
+       cmd->scan_end_src &= TRIG_COUNT;\r
+       if(!cmd->scan_end_src || tmp != cmd->scan_end_src) err++;\r
+\r
+       tmp=cmd->stop_src;\r
+       cmd->stop_src &= TRIG_COUNT | TRIG_EXT | TRIG_NONE;\r
+       if(!cmd->stop_src || tmp != cmd->stop_src) err++;\r
+\r
+       if(err) return 1;\r
+\r
+       /* step 2: make sure trigger sources are unique and mutually compatible */\r
+\r
+       // uniqueness check\r
+       if(cmd->start_src != TRIG_NOW &&\r
+               cmd->start_src != TRIG_EXT) err++;\r
+       if(cmd->scan_begin_src != TRIG_TIMER &&\r
+               cmd->scan_begin_src != TRIG_FOLLOW) err++;\r
+       if(cmd->convert_src != TRIG_TIMER &&\r
+               cmd->convert_src != TRIG_EXT) err++;\r
+       if(cmd->stop_src != TRIG_COUNT &&\r
+               cmd->stop_src != TRIG_NONE &&\r
+               cmd->stop_src != TRIG_EXT) err++;\r
+\r
+       // compatibility check\r
+       if(cmd->convert_src == TRIG_EXT &&\r
+               cmd->scan_begin_src == TRIG_TIMER)\r\r
                err++;\r
-
-       if(err) return 2;
-
-       /* step 3: make sure arguments are trivially compatible */
-
-       if(cmd->start_arg != 0)
-       {
-               cmd->start_arg = 0;
-               err++;
-       }
-       if(cmd->convert_src == TRIG_TIMER)
-       {
-               if(cmd->convert_arg < thisboard->ai_speed)
-               {
-                       cmd->convert_arg = thisboard->ai_speed;
-                       err++;
-               }
-               if(cmd->scan_begin_src == TRIG_TIMER)\r
-               {
-                       // if scans are timed faster than conversion rate allows\r
-                       if(cmd->convert_arg * cmd->chanlist_len > cmd->scan_begin_arg)
+\r
+       if(err) return 2;\r
+\r
+       /* step 3: make sure arguments are trivially compatible */\r
+\r
+       if(cmd->start_arg != 0)\r
+       {\r
+               cmd->start_arg = 0;\r
+               err++;\r
+       }\r
+       if(cmd->convert_src == TRIG_TIMER)\r
+       {\r
+               if(cmd->convert_arg < thisboard->ai_speed)\r
+               {\r
+                       cmd->convert_arg = thisboard->ai_speed;\r
+                       err++;\r
+               }\r
+               if(cmd->scan_begin_src == TRIG_TIMER)\r\r
+               {\r
+                       // if scans are timed faster than conversion rate allows\r\r
+                       if(cmd->convert_arg * cmd->chanlist_len > cmd->scan_begin_arg)\r
                        {\r
                                cmd->scan_begin_arg = cmd->convert_arg * cmd->chanlist_len;\r
                                err++;\r
                        }\r
                }\r
-       }
-
-       if(!cmd->chanlist_len)
-       {
-               cmd->chanlist_len = 1;
-               err++;
-       }
-       if(cmd->scan_end_arg != cmd->chanlist_len)
-       {
-               cmd->scan_end_arg = cmd->chanlist_len;
-               err++;
-       }
-
-       switch(cmd->stop_src)
-       {
-               case TRIG_EXT:
-                       if(cmd->stop_arg)
-                       {
-                               cmd->stop_arg = 0;
-                               err++;
-                       }
-                       break;
-               case TRIG_COUNT:
-                       if(!cmd->stop_arg)
-                       {
-                               cmd->stop_arg = 1;
-                               err++;
-                       }
-                       break;
-               case TRIG_NONE:
-                       if(cmd->stop_arg != 0)
-                       {
-                               cmd->stop_arg = 0;
-                               err++;
-                       }
-                       break;
-               default:
-                       break;
-       }
-
-       if(err) return 3;
-
-       /* step 4: fix up any arguments */
-
-       if(cmd->convert_src == TRIG_TIMER)
-       {
-               tmp_arg = cmd->convert_arg;
-               tmp_arg2 = cmd->scan_begin_arg;
-               check_adc_timing(cmd);
-               if(tmp_arg != cmd->convert_arg) err++;
-               if(tmp_arg2 != cmd->scan_begin_arg) err++;
-       }
-
-       if(err) return 4;
-
-       // make sure user is doesn't change analog reference mid chanlist
-       if(cmd->chanlist)
-       {
-               aref = CR_AREF(cmd->chanlist[0]);
-               for(i = 1; i < cmd->chanlist_len; i++)
-               {
-                       if(aref != CR_AREF(cmd->chanlist[i]))
-                       {
-                               comedi_error(dev, "all elements in chanlist must use the same analog reference");
-                               err++;
-                               break;
-                       }
-               }
-       }
-
-       if(err) return 5;
-
-       return 0;
-}
-
-static int ai_cmd(comedi_device *dev,comedi_subdevice *s)
-{
-       comedi_async *async = s->async;
-       comedi_cmd *cmd = &async->cmd;
-       unsigned int bits;
-       unsigned int convert_counter_value;
-       unsigned int scan_counter_value;
-       unsigned int i;
-
-       // disable card's interrupt sources
-       writew(0, devpriv->main_iobase + INTR_ENABLE_REG);
-
-       /* disable pacing, triggering, etc */
-       writew(0, devpriv->main_iobase + ADC_CONTROL0_REG);
-       writew(0, devpriv->main_iobase + ADC_CONTROL1_REG);
-
-       // use external queue
-       writew(EXT_QUEUE | HW_CONFIG_DUMMY_BITS, devpriv->main_iobase + HW_CONFIG_REG);
-
-       // set fifo size
-       writew(ADC_FIFO_8K_BITS | FIFO_SIZE_DUMMY_BITS, devpriv->main_iobase + FIFO_SIZE_REG);
-
-       // set conversion pacing
-       if(cmd->convert_src == TRIG_TIMER)
-       {
-               check_adc_timing(cmd);
-               // supposed to load counter with desired divisor minus 3
-               convert_counter_value = cmd->convert_arg / TIMER_BASE - 3;
-               // load lower 16 bits
-               writew(convert_counter_value & 0xffff, devpriv->main_iobase + ADC_SAMPLE_INTERVAL_LOWER_REG);
-               // load upper 8 bits
-               writew((convert_counter_value >> 16) & 0xff, devpriv->main_iobase + ADC_SAMPLE_INTERVAL_UPPER_REG);
-               // set scan pacing
-               if(cmd->convert_src == TRIG_TIMER)
-               {
-                       // figure out how long we need to delay at end of scan
-                       scan_counter_value = (cmd->scan_begin_arg - (cmd->convert_arg * cmd->chanlist_len))
-                               / TIMER_BASE;
-                       // load lower 16 bits
-                       writew(scan_counter_value & 0xffff, devpriv->main_iobase + ADC_DELAY_INTERVAL_LOWER_REG);
-                       // load upper 8 bits
-                       writew((scan_counter_value >> 16) & 0xff, devpriv->main_iobase + ADC_DELAY_INTERVAL_UPPER_REG);
-               }
-       }
-
-       // load hardware conversion counter with non-zero value so it doesn't mess with us
-       writew(~0, devpriv->main_iobase + ADC_COUNT_LOWER_REG);
-
-       /* XXX cannot write to queue fifo while dac fifo is being written to
-        * ( need spinlock, or try to use internal queue instead */
-       // clear queue pointer
-       writew(0, devpriv->main_iobase + ADC_QUEUE_CLEAR_REG);
-       // load external queue
-       for(i = 0; i < cmd->chanlist_len; i++)
-       {
-               bits = 0;
-               // set channel
-               bits |= CHAN_BITS(CR_CHAN(cmd->chanlist[i]));
-               // set gain
-               bits |= GAIN_BITS(CR_RANGE(cmd->chanlist[i]));
-               // set unipolar / bipolar
-               bits |= UNIP_BIT(CR_RANGE(cmd->chanlist[i]));
-               // set single-ended / differential
-               if(CR_AREF(cmd->chanlist[i]) != AREF_DIFF)
-                       bits |= SE_BIT;
-               // mark end of queue
-               if(i == cmd->chanlist_len - 1)
-                       bits |= QUEUE_EOSCAN_BIT | QUEUE_EOSEQ_BIT;
-               writew(bits, devpriv->main_iobase + ADC_QUEUE_FIFO_REG);
-       }
-       // prime queue holding register
-       writew(0, devpriv->main_iobase + ADC_QUEUE_LOAD_REG);
-
-       // clear adc buffer
-       writew(0, devpriv->main_iobase + ADC_BUFFER_CLEAR_REG);
-
-       // enable interrupts
-       bits = EN_ADC_OVERRUN_BIT | EN_ADC_DONE_INTR_BIT;
-       if(cmd->flags & TRIG_WAKE_EOS)
-               bits |= ADC_INTR_EOSCAN_BITS;
-       writew(bits, devpriv->main_iobase + INTR_ENABLE_REG);
-
-       /* set mode, disable software conversion gate */
-       bits = SW_NOGATE_BIT;
-       if(cmd->convert_src == TRIG_EXT)
-               bits |= ADC_MODE_BITS(13);      // good old mode 13
-       else
-               bits |= ADC_MODE_BITS(8);       // mode 8.  What else could you need?
-       writew(bits, devpriv->main_iobase + ADC_CONTROL1_REG);
-
-       /* enable pacing, triggering, etc */
-       bits = ADC_ENABLE_BIT;
-       writew(bits, devpriv->main_iobase + ADC_CONTROL0_REG);
-
-       // start aquisition
-       writew(0, devpriv->main_iobase + ADC_START_REG);
-
-       return 0;
-}
-
-static void handle_interrupt(int irq, void *d, struct pt_regs *regs)
-{
-       return;
-}
-
-static int ai_cancel(comedi_device *dev, comedi_subdevice *s)
-{
-       return 0;
-}
-
-static int ao_winsn(comedi_device *dev, comedi_subdevice *s,
-       comedi_insn *insn, lsampl_t *data)
-{
-       int chan = CR_CHAN(insn->chanspec);
-       int range = CR_RANGE(insn->chanspec);
-       unsigned int bits;
-
-       // do some initializing
-       writew(DAC_ENABLE_BIT, devpriv->main_iobase + DAC_CONTROL0_REG);
-
-       // set range
-       bits = DAC_OUTPUT_ENABLE_BIT;
-       bits |= DAC_RANGE_BITS(chan, range);
-       writew(bits, devpriv->main_iobase + DAC_CONTROL1_REG);
-
-       // clear buffer
-       writew(0, devpriv->main_iobase + DAC_BUFFER_CLEAR_REG);
-
-       // write to channel
-       writew(data[0], devpriv->main_iobase + DAC_CONVERT_REG(chan));
-
-       // remember output value
-       devpriv->ao_value[chan] = data[0];
-
-       return 1;
-}
-
-static int ao_readback_insn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
-{
-       data[0] = devpriv->ao_value[CR_CHAN(insn->chanspec)];
-
-       return 1;
-}
-
-static int dio_callback(int dir, int port, int data, void *arg)
-{
-       unsigned long iobase = (int)arg;
-
-       if(dir)
-       {
-               writeb(data, iobase + port);
-               return 0;
-       }else
-       {
-               return readb(iobase + port);
-       }
-}
-
-static int di_rbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
-{
-       lsampl_t bits;
-
-       bits = readb(devpriv->dio_counter_iobase + DI_REG);
-       bits &= 0xf;
-       data[1] = bits;
-       data[0] = 0;
-
-       return 2;
-}
-
-static int do_wbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
-{
-       lsampl_t wbits;
-
-       data[0] &= 0xf;
-       wbits = devpriv->do_bits;
-       // zero bits we are going to change
-       wbits &= ~data[0];
-       // set new bits
-       wbits |= data[0] & data[1];
-       devpriv->do_bits = wbits;
-
-       writeb(devpriv->do_bits, devpriv->dio_counter_iobase + DO_REG);
-
-       data[1] = wbits;
-
-       return 2;
-}
-
-// utility function that rounds desired timing to an achievable time.
-// adc paces conversions from master clock by dividing by (x + 3) where x is 24 bit number
-static void check_adc_timing(comedi_cmd *cmd)
-{
-       unsigned int convert_divisor, scan_divisor;
-       const int min_convert_divisor = 3;
-       const int max_convert_divisor = 0xffffff + min_convert_divisor;
-       unsigned long long max_scan_divisor, min_scan_divisor;
-
-       if(cmd->convert_src == TRIG_TIMER)
-       {
-               convert_divisor = get_divisor(cmd->convert_arg, cmd->flags);
-               if(convert_divisor > max_convert_divisor) convert_divisor = max_convert_divisor;
-               if(convert_divisor < min_convert_divisor) convert_divisor = min_convert_divisor;
-               cmd->convert_arg = convert_divisor * TIMER_BASE;
-
-               if(cmd->scan_begin_src == TRIG_TIMER)
-               {
-                       scan_divisor = get_divisor(cmd->scan_begin_arg, cmd->flags);
-                       min_scan_divisor = convert_divisor * cmd->chanlist_len;
-                       max_scan_divisor = min_scan_divisor + 0xffffff;
-                       if(scan_divisor > max_scan_divisor) scan_divisor = max_scan_divisor;
-                       if(scan_divisor < min_scan_divisor) scan_divisor = min_scan_divisor;
-                       cmd->scan_begin_arg = scan_divisor * TIMER_BASE;
-               }
-       }
-
-       return;
-}
-
-/* Gets nearest achievable timing given master clock speed, does not
- * take into account possible minimum/maximum divisor values.  Used
- * by other timing checking functions. */
-static unsigned int get_divisor(unsigned int ns, unsigned int flags)
-{
-       unsigned int divisor;
-
-       switch(flags & TRIG_ROUND_MASK)
-       {
-               case TRIG_ROUND_UP:
-                       divisor = (ns + TIMER_BASE - 1) / TIMER_BASE;
-                       break;
-               case TRIG_ROUND_DOWN:
-                       divisor = ns / TIMER_BASE;
-                       break;
-               case TRIG_ROUND_NEAREST:
-               default:
-                       divisor = (ns + TIMER_BASE / 2) / TIMER_BASE;
-                       break;
-       }
-
-       return divisor;
-}
+       }\r
+\r
+       if(!cmd->chanlist_len)\r
+       {\r
+               cmd->chanlist_len = 1;\r
+               err++;\r
+       }\r
+       if(cmd->scan_end_arg != cmd->chanlist_len)\r
+       {\r
+               cmd->scan_end_arg = cmd->chanlist_len;\r
+               err++;\r
+       }\r
+\r
+       switch(cmd->stop_src)\r
+       {\r
+               case TRIG_EXT:\r
+                       if(cmd->stop_arg)\r
+                       {\r
+                               cmd->stop_arg = 0;\r
+                               err++;\r
+                       }\r
+                       break;\r
+               case TRIG_COUNT:\r
+                       if(!cmd->stop_arg)\r
+                       {\r
+                               cmd->stop_arg = 1;\r
+                               err++;\r
+                       }\r
+                       break;\r
+               case TRIG_NONE:\r
+                       if(cmd->stop_arg != 0)\r
+                       {\r
+                               cmd->stop_arg = 0;\r
+                               err++;\r
+                       }\r
+                       break;\r
+               default:\r
+                       break;\r
+       }\r
+\r
+       if(err) return 3;\r
+\r
+       /* step 4: fix up any arguments */\r
+\r
+       if(cmd->convert_src == TRIG_TIMER)\r
+       {\r
+               tmp_arg = cmd->convert_arg;\r
+               tmp_arg2 = cmd->scan_begin_arg;\r
+               check_adc_timing(cmd);\r
+               if(tmp_arg != cmd->convert_arg) err++;\r
+               if(tmp_arg2 != cmd->scan_begin_arg) err++;\r
+       }\r
+\r
+       if(err) return 4;\r
+\r
+       // make sure user is doesn't change analog reference mid chanlist\r
+       if(cmd->chanlist)\r
+       {\r
+               aref = CR_AREF(cmd->chanlist[0]);\r
+               for(i = 1; i < cmd->chanlist_len; i++)\r
+               {\r
+                       if(aref != CR_AREF(cmd->chanlist[i]))\r
+                       {\r
+                               comedi_error(dev, "all elements in chanlist must use the same analog reference");\r
+                               err++;\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+\r
+       if(err) return 5;\r
+\r
+       return 0;\r
+}\r
+\r
+static int ai_cmd(comedi_device *dev,comedi_subdevice *s)\r
+{\r
+       comedi_async *async = s->async;\r
+       comedi_cmd *cmd = &async->cmd;\r
+       unsigned int bits;\r
+       unsigned int convert_counter_value;\r
+       unsigned int scan_counter_value;\r
+       unsigned int i;\r
+\r
+       // disable card's interrupt sources\r
+       writew(0, devpriv->main_iobase + INTR_ENABLE_REG);\r
+\r
+       /* disable pacing, triggering, etc */\r
+       writew(0, devpriv->main_iobase + ADC_CONTROL0_REG);\r
+       writew(ADC_CONTROL1_DUMMY_BITS, devpriv->main_iobase + ADC_CONTROL1_REG);\r
+\r
+       // use external queue\r
+       writew(EXT_QUEUE | HW_CONFIG_DUMMY_BITS, devpriv->main_iobase + HW_CONFIG_REG);\r
+\r
+       // set fifo size\r
+       writew(ADC_FIFO_8K_BITS | FIFO_SIZE_DUMMY_BITS, devpriv->main_iobase + FIFO_SIZE_REG);\r
+\r
+       // set conversion pacing\r
+       if(cmd->convert_src == TRIG_TIMER)\r
+       {\r
+               check_adc_timing(cmd);\r
+               // supposed to load counter with desired divisor minus 3\r
+               convert_counter_value = cmd->convert_arg / TIMER_BASE - 3;\r
+               // load lower 16 bits\r
+               writew(convert_counter_value & 0xffff, devpriv->main_iobase + ADC_SAMPLE_INTERVAL_LOWER_REG);\r
+               // load upper 8 bits\r
+               writew((convert_counter_value >> 16) & 0xff, devpriv->main_iobase + ADC_SAMPLE_INTERVAL_UPPER_REG);\r
+               // set scan pacing\r
+               if(cmd->convert_src == TRIG_TIMER)\r
+               {\r
+                       // figure out how long we need to delay at end of scan\r
+                       scan_counter_value = (cmd->scan_begin_arg - (cmd->convert_arg * (cmd->chanlist_len - 1)))\r
+                               / TIMER_BASE;\r
+                       // load lower 16 bits\r
+                       writew(scan_counter_value & 0xffff, devpriv->main_iobase + ADC_DELAY_INTERVAL_LOWER_REG);\r
+                       // load upper 8 bits\r
+                       writew((scan_counter_value >> 16) & 0xff, devpriv->main_iobase + ADC_DELAY_INTERVAL_UPPER_REG);\r
+               }\r
+       }\r
+\r
+       // load hardware conversion counter with non-zero value so it doesn't mess with us\r
+       writew(~0, devpriv->main_iobase + ADC_COUNT_LOWER_REG);\r
+\r
+       /* XXX cannot write to queue fifo while dac fifo is being written to\r
+        * ( need spinlock, or try to use internal queue instead */\r
+       // clear queue pointer\r
+       writew(0, devpriv->main_iobase + ADC_QUEUE_CLEAR_REG);\r
+       // load external queue\r
+       for(i = 0; i < cmd->chanlist_len; i++)\r
+       {\r
+               bits = 0;\r
+               // set channel\r
+               bits |= CHAN_BITS(CR_CHAN(cmd->chanlist[i]));\r
+               // set gain\r
+               bits |= GAIN_BITS(CR_RANGE(cmd->chanlist[i]));\r
+               // set unipolar / bipolar\r
+               bits |= UNIP_BIT(CR_RANGE(cmd->chanlist[i]));\r
+               // set single-ended / differential\r
+               if(CR_AREF(cmd->chanlist[i]) != AREF_DIFF)\r
+                       bits |= SE_BIT;\r
+               // mark end of queue\r
+               if(i == cmd->chanlist_len - 1)\r
+                       bits |= QUEUE_EOSCAN_BIT | QUEUE_EOSEQ_BIT;\r
+               writew(bits, devpriv->main_iobase + ADC_QUEUE_FIFO_REG);\r
+       }\r
+       // prime queue holding register\r
+       writew(0, devpriv->main_iobase + ADC_QUEUE_LOAD_REG);\r
+\r
+       // clear adc buffer\r
+       writew(0, devpriv->main_iobase + ADC_BUFFER_CLEAR_REG);\r
+\r
+       // enable interrupts\r
+       bits = EN_ADC_OVERRUN_BIT | EN_ADC_DONE_INTR_BIT;\r
+       if(cmd->flags & TRIG_WAKE_EOS)\r
+               bits |= ADC_INTR_EOSCAN_BITS;\r
+       else\r
+               bits |= ADC_INTR_QFULL_BITS;    // for clairity only, since quarter-full bits are zero\r
+       writew(bits, devpriv->main_iobase + INTR_ENABLE_REG);\r
+       // enable interrupts on plx 9080 XXX enabling more interrupt sources than are actually used\r
+       bits = ICS_PIE | ICS_PLIE | ICS_PAIE | ICS_PDIE | ICS_LIE | ICS_LDIE | ICS_DMA0_E | ICS_DMA1_E;\r
+       writel(bits, devpriv->plx9080_iobase + PLX_INTRCS_REG);\r
+\r
+       // disable dma for now XXX\r
+       writeb(0, devpriv->plx9080_iobase + PLX_DMA0_CS_REG);\r
+       writeb(0, devpriv->plx9080_iobase + PLX_DMA1_CS_REG);\r
+\r
+       /* set mode, disable software conversion gate */\r
+       bits = ADC_CONTROL1_DUMMY_BITS | SW_NOGATE_BIT;\r
+       if(cmd->convert_src == TRIG_EXT)\r
+               bits |= ADC_MODE_BITS(13);      // good old mode 13\r
+       else\r
+               bits |= ADC_MODE_BITS(8);       // mode 8.  What else could you need?\r
+       writew(bits, devpriv->main_iobase + ADC_CONTROL1_REG);\r
+\r
+       /* enable pacing, triggering, etc */\r
+       bits = ADC_ENABLE_BIT;\r
+       writew(bits, devpriv->main_iobase + ADC_CONTROL0_REG);\r
+\r
+       // start aquisition\r
+       writew(0, devpriv->main_iobase + ADC_START_REG);\r
+\r
+       return 0;\r
+}\r
+\r
+static void handle_interrupt(int irq, void *d, struct pt_regs *regs)\r
+{\r
+#ifdef PCIDAS64_DEBUG\r
+#endif\r
+       comedi_device *dev = d;\r
+       comedi_subdevice *s = dev->read_subdev;\r
+       comedi_async *async = s->async;\r
+       comedi_cmd *cmd = &async->cmd;\r
+       unsigned int num_samples = 0;\r
+       unsigned int i;\r
+       u16 data;\r
+       unsigned int status;\r
+       u32 plx_status;\r
+       u32 plx_bits;\r
+#ifdef PCIDAS64_DEBUG\r
+       static unsigned int intr_count = 0;\r
+       const int debug_count = 10;\r
+#endif\r
+\r
+       status = readw(devpriv->main_iobase + HW_STATUS_REG);\r
+       plx_status = readl(devpriv->plx9080_iobase + PLX_INTRCS_REG);\r
+       if((status &\r
+               (ADC_INTR_PENDING_BIT | ADC_DONE_BIT | ADC_STOP_BIT |\r
+               DAC_INTR_PENDING_BIT | DAC_DONE_BIT | EXT_INTR_PENDING_BIT)) == 0 &&\r
+               (plx_status & (ICS_DMA0_A | ICS_DMA1_A | ICS_LDIA | ICS_LIA | ICS_PAIA | ICS_PDIA)) == 0)\r
+       {\r
+#ifdef PCIDAS64_DEBUG\r
+               rt_printk(" cb_pcidas64 spurious interrupt");\r
+#endif\r
+               return;\r
+       }\r
+#ifdef PCIDAS64_DEBUG\r
+       intr_count++;\r
+       if(intr_count < debug_count)\r
+       {\r
+               rt_printk(" cb_pcidas64 interrupt status 0x%x\n", status);\r
+               rt_printk(" plx status 0x%x\n", plx_status);\r
+       }\r
+#endif\r
+\r
+       async->events = 0;\r
+\r
+       // if interrupt was due to analog input data being available\r
+       if(status & ADC_INTR_PENDING_BIT)\r
+       {\r
+               // figure out how many samples we should read from board's fifo\r
+               /* XXX should use ADC read/write pointer registers to figure out\r
+                * how many samples are actually in fifo */\r
+               if(cmd->flags & TRIG_WAKE_EOS)\r
+                       num_samples = cmd->chanlist_len;\r
+               else\r
+                       num_samples = QUARTER_AI_FIFO_SIZE;\r
+               // read samples\r
+               for(i = 0; i < num_samples; i++)\r
+               {\r
+                       data = readw(devpriv->main_iobase + ADC_FIFO_REG);\r
+                       comedi_buf_put(async, data);\r
+               }\r
+               async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;\r
+       }\r
+\r
+       // clear possible plx9080 interrupt sources\r
+       if(plx_status & ICS_LDIA)\r
+       { // clear local doorbell interrupt\r
+               plx_bits = readl(devpriv->plx9080_iobase + PLX_DBR_OUT_REG);\r
+               writel(plx_bits, devpriv->plx9080_iobase + PLX_DBR_OUT_REG);\r
+#ifdef PCIDAS64_DEBUG\r
+               if(intr_count < debug_count)\r
+                       rt_printk(" cleared local doorbell bits 0x%x\n", plx_bits);\r
+#endif\r
+       }\r
+       if(plx_status & ICS_DMA0_A)\r
+       {       // dma chan 0 interrupt\r
+               writeb(PLX_CLEAR_DMA_INTR_BIT, devpriv->plx9080_iobase + PLX_DMA0_CS_REG);\r
+#ifdef PCIDAS64_DEBUG\r
+               if(intr_count < debug_count)\r
+                       rt_printk(" cleared dma ch0 interrupt\n");\r
+#endif\r
+       }\r
+       if(plx_status & ICS_DMA1_A)\r
+       {       // dma chan 1 interrupt\r
+               writeb(PLX_CLEAR_DMA_INTR_BIT, devpriv->plx9080_iobase + PLX_DMA1_CS_REG);\r
+#ifdef PCIDAS64_DEBUG\r
+               if(intr_count < debug_count)\r
+                       rt_printk(" cleared dma ch1 interrupt\n");\r
+#endif\r
+       }\r
+\r
+       comedi_event(dev, s, async->events);\r
+       return;\r
+}\r
+\r
+static int ai_cancel(comedi_device *dev, comedi_subdevice *s)\r
+{\r
+       /* disable pacing, triggering, etc */\r
+       writew(0, devpriv->main_iobase + ADC_CONTROL0_REG);\r
+       writew(ADC_CONTROL1_DUMMY_BITS, devpriv->main_iobase + ADC_CONTROL1_REG);\r
+\r
+       return 0;\r
+}\r
+\r
+static int ao_winsn(comedi_device *dev, comedi_subdevice *s,\r
+       comedi_insn *insn, lsampl_t *data)\r
+{\r
+       int chan = CR_CHAN(insn->chanspec);\r
+       int range = CR_RANGE(insn->chanspec);\r
+       unsigned int bits;\r
+\r
+       // do some initializing\r
+       writew(DAC_ENABLE_BIT, devpriv->main_iobase + DAC_CONTROL0_REG);\r
+\r
+       // set range\r
+       bits = DAC_OUTPUT_ENABLE_BIT;\r
+       bits |= DAC_RANGE_BITS(chan, range);\r
+       writew(bits, devpriv->main_iobase + DAC_CONTROL1_REG);\r
+\r
+       // clear buffer\r
+       writew(0, devpriv->main_iobase + DAC_BUFFER_CLEAR_REG);\r
+\r
+       // write to channel\r
+       writew(data[0], devpriv->main_iobase + DAC_CONVERT_REG(chan));\r
+\r
+       // remember output value\r
+       devpriv->ao_value[chan] = data[0];\r
+\r
+       return 1;\r
+}\r
+\r
+static int ao_readback_insn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)\r
+{\r
+       data[0] = devpriv->ao_value[CR_CHAN(insn->chanspec)];\r
+\r
+       return 1;\r
+}\r
+\r
+static int dio_callback(int dir, int port, int data, void *arg)\r
+{\r
+       unsigned long iobase = (int)arg;\r
+\r
+       if(dir)\r
+       {\r
+               writeb(data, iobase + port);\r
+               return 0;\r
+       }else\r
+       {\r
+               return readb(iobase + port);\r
+       }\r
+}\r
+\r
+static int di_rbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)\r
+{\r
+       lsampl_t bits;\r
+\r
+       bits = readb(devpriv->dio_counter_iobase + DI_REG);\r
+       bits &= 0xf;\r
+       data[1] = bits;\r
+       data[0] = 0;\r
+\r
+       return 2;\r
+}\r
+\r
+static int do_wbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)\r
+{\r
+       lsampl_t wbits;\r
+\r
+       data[0] &= 0xf;\r
+       wbits = devpriv->do_bits;\r
+       // zero bits we are going to change\r
+       wbits &= ~data[0];\r
+       // set new bits\r
+       wbits |= data[0] & data[1];\r
+       devpriv->do_bits = wbits;\r
+\r
+       writeb(devpriv->do_bits, devpriv->dio_counter_iobase + DO_REG);\r
+\r
+       data[1] = wbits;\r
+\r
+       return 2;\r
+}\r
+\r
+/* utility function that rounds desired timing to an achievable time, and\r
+ * sets cmd members appropriately.\r
+ * adc paces conversions from master clock by dividing by (x + 3) where x is 24 bit number\r
+ */\r
+static void check_adc_timing(comedi_cmd *cmd)\r
+{\r
+       unsigned int convert_divisor, scan_divisor;\r
+       const int max_counter_value = 0xffffff; // board uses 24 bit counters for pacing\r
+       const int min_convert_divisor = 3;\r
+       const int max_convert_divisor = max_counter_value + min_convert_divisor;\r
+       unsigned long long max_scan_divisor, min_scan_divisor;\r
+\r
+       if(cmd->convert_src == TRIG_TIMER)\r
+       {\r
+               convert_divisor = get_divisor(cmd->convert_arg, cmd->flags);\r
+               if(convert_divisor > max_convert_divisor) convert_divisor = max_convert_divisor;\r
+               if(convert_divisor < min_convert_divisor) convert_divisor = min_convert_divisor;\r
+               cmd->convert_arg = convert_divisor * TIMER_BASE;\r
+\r
+               if(cmd->scan_begin_src == TRIG_TIMER)\r
+               {\r
+                       scan_divisor = get_divisor(cmd->scan_begin_arg, cmd->flags);\r
+                       // XXX check for integer overflows\r
+                       min_scan_divisor = convert_divisor * cmd->chanlist_len;\r
+                       max_scan_divisor = (convert_divisor * cmd->chanlist_len - 1) + max_counter_value;\r
+                       if(scan_divisor > max_scan_divisor) scan_divisor = max_scan_divisor;\r
+                       if(scan_divisor < min_scan_divisor) scan_divisor = min_scan_divisor;\r
+                       cmd->scan_begin_arg = scan_divisor * TIMER_BASE;\r
+               }\r
+       }\r
+\r
+       return;\r
+}\r
+\r
+/* Gets nearest achievable timing given master clock speed, does not\r
+ * take into account possible minimum/maximum divisor values.  Used\r
+ * by other timing checking functions. */\r
+static unsigned int get_divisor(unsigned int ns, unsigned int flags)\r
+{\r
+       unsigned int divisor;\r
+\r
+       switch(flags & TRIG_ROUND_MASK)\r
+       {\r
+               case TRIG_ROUND_UP:\r
+                       divisor = (ns + TIMER_BASE - 1) / TIMER_BASE;\r
+                       break;\r
+               case TRIG_ROUND_DOWN:\r
+                       divisor = ns / TIMER_BASE;\r
+                       break;\r
+               case TRIG_ROUND_NEAREST:\r
+               default:\r
+                       divisor = (ns + TIMER_BASE / 2) / TIMER_BASE;\r
+                       break;\r
+       }\r
+\r
+       return divisor;\r
+}\r
index 41ef31cfdf7e8955cbf8370a2d5a20dd774714d4..1a56684fa6d773607571011cb4e9ee960ca5b16e 100644 (file)
 #define PLX_DMA0_DESCRIPTOR_REG        0x90    // descriptor pointer register
 
 #define PLX_DMA0_CS_REG        0xa8    // command status register
+#define PLX_DMA1_CS_REG        0xa9
+#define  PLX_CLEAR_DMA_INTR_BIT        0x8
 
 #define PLX_DMA0_THRESHOLD_REG 0xb0    // command status register