Update from Allan.
authorDavid Schleef <ds@schleef.org>
Tue, 28 Aug 2001 23:23:44 +0000 (23:23 +0000)
committerDavid Schleef <ds@schleef.org>
Tue, 28 Aug 2001 23:23:44 +0000 (23:23 +0000)
comedi/drivers/amplc_pci230.c

index acddf4f6aaecbb435c6e466007d34c1d3430b95b..a836785514fbcdf1f82efb604abe0d3183fc6049 100644 (file)
@@ -1,4 +1,4 @@
-/*
+ /*
     comedi/drivers/amplc_pci230.c
     Driver for Amplicon PCI230 and PCI260 Multifunction I/O boards.
 
     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.
-
-************************************************************************
-
-TODO:
-fix pci autodetection, add support for bus/slot config options
-
 */
 
 #include <linux/kernel.h>
@@ -45,17 +39,15 @@ fix pci autodetection, add support for bus/slot config options
 #include "8253.h"
 #include "8255.h"
 
-
 /* PCI230 PCI configuration register information */
-
 #define PCI_VENDOR_ID_AMPLICON 0x14dc
 #define PCI_DEVICE_ID_PCI230 0x0000
 #define PCI_DEVICE_ID_PCI260 0x0006
 
 #define PCI230_IO1_SIZE 32             /* Size of I/O space 1 */
-#define PCI230_IO2_SIZE 32             /* Size of I/O space 2 - Check this!!! only appears to be 8 16bit words = 16 bytes long*/
+#define PCI230_IO2_SIZE 16             /* Size of I/O space 2 */
 
-/* PCI230 I/O SPACE 1 REGISTERS */
+/* PCI230 i/o space 1 registers. */
 #define PCI230_PPI_X_A   0x00   /* User PPI port A */
 #define PCI230_PPI_X_B   0x01   /* User PPI port B */
 #define PCI230_PPI_X_C   0x02   /* User PPI port C */
@@ -68,7 +60,7 @@ fix pci autodetection, add support for bus/slot config options
 #define PCI230_ZGAT_SCE  0x1D   /* Group Z Gate Configuration Register */
 #define PCI230_INT_SCE   0x1E   /* ISR Interrupt source mask register/Interrupt status */
   
-/* PCI230 I/O SPACE 2 REGISTERS */
+/* PCI230 i/o space 2 registers. */
 #define PCI230_DACCON  0x00
 #define PCI230_DACOUT1 0x02
 #define PCI230_DACOUT2 0x04
@@ -78,46 +70,45 @@ fix pci autodetection, add support for bus/slot config options
 #define PCI230_ADCEN   0x0C
 #define PCI230_ADCG    0x0E
 
-/* CONVERTOR RELATED CONSTANTS */
-#define PCI230_DAC_SETTLE 5            /* Analogue output settling time in µS (DAC itself is 1µS nominally) */  
-#define PCI230_ADC_SETTLE 1            /* Analogue input settling time in µS (ADC itself is 1.6µS nominally but we poll anyway) */  
-#define PCI230_MUX_SETTLE 1            /* ADC MUX settling time in µS - guess */
+/* Convertor related constants. */
+#define PCI230_DAC_SETTLE 5            /* Analogue output settling time in µs (DAC itself is 1µs nominally). */  
+#define PCI230_ADC_SETTLE 1            /* Analogue input settling time in µs (ADC itself is 1.6µs nominally but we poll anyway). */  
+#define PCI230_MUX_SETTLE 10   /* ADC MUX settling time in µS - 10µs for se, 20µs de. */
 
-/* DACCON VALUES */
+/* DACCON values. */
 #define PCI230_DAC_BUSY_BIT            1
 #define PCI230_DAC_BIP_BIT             0
 
-/* ADCCON WRITE VALUES */
-#define PCI230_ADC_TRIG_NONE   0
-#define PCI230_ADC_TRIG_SW             1
-#define PCI230_ADC_TRIG_EXTP   2
-#define PCI230_ADC_TRIG_EXTN   3
-#define PCI230_ADC_TRIG_Z2CT0  4
-#define PCI230_ADC_TRIG_Z2CT1  5
-#define PCI230_ADC_TRIG_Z2CT2  6
-#define PCI230_ADC_IR_UNI              (0<<3)  /* Input range unipolar */
-#define PCI230_ADC_IR_BIP              (1<<3)  /* Input range bipolar */
-#define PCI230_ADC_IM_SE               (0<<4)  /* Input mode single ended */
-#define PCI230_ADC_IM_DIF              (1<<4)  /* Input mode differential */
-#define PCI230_ADC_FIFO_EN             (1<<8)
+/* ADCCON write values. */
+#define PCI230_ADC_TRIG_NONE           0
+#define PCI230_ADC_TRIG_SW                     1
+#define PCI230_ADC_TRIG_EXTP           2
+#define PCI230_ADC_TRIG_EXTN           3
+#define PCI230_ADC_TRIG_Z2CT0          4
+#define PCI230_ADC_TRIG_Z2CT1          5
+#define PCI230_ADC_TRIG_Z2CT2          6
+#define PCI230_ADC_IR_UNI                      (0<<3)  /* Input range unipolar */
+#define PCI230_ADC_IR_BIP                      (1<<3)  /* Input range bipolar */
+#define PCI230_ADC_IM_SE                       (0<<4)  /* Input mode single ended */
+#define PCI230_ADC_IM_DIF                      (1<<4)  /* Input mode differential */
+#define PCI230_ADC_FIFO_EN                     (1<<8)
 #define PCI230_ADC_INT_FIFO_EMPTY      0
 #define PCI230_ADC_INT_FIFO_NEMPTY     (1<<9)
 #define PCI230_ADC_INT_FIFO_NHALF      (2<<9)
 #define PCI230_ADC_INT_FIFO_HALF       (3<<9)
 #define PCI230_ADC_INT_FIFO_NFULL      (4<<9)
 #define PCI230_ADC_INT_FIFO_FULL       (5<<9)
-#define PCI230_ADC_FIFO_RESET  (1<<12)
-#define PCI230_ADC_GLOB_RESET  (1<<13)
-#define PCI230_ADC_CONV        0xffff                          /* Value to write to ADCDATA to trigger ADC conversion in sotware trigger mode */
-#define PCI230_ADC_SW_CHAN(n)  ((n)<<12)       /* ADCCON software trigger channel selection - bit shift channel into upper 4 bit nibble of word */
+#define PCI230_ADC_FIFO_RESET          (1<<12)
+#define PCI230_ADC_GLOB_RESET          (1<<13)
+#define PCI230_ADC_CONV                                0xffff          /* Value to write to ADCDATA to trigger ADC conversion in sotware trigger mode */
 
-/* ADCCON READ VALUES */
+/* ADCCON read values. */
 #define PCI230_ADC_BUSY_BIT            15
 #define PCI230_ADC_FIFO_EMPTY  (1<<12)
 #define PCI230_ADC_FIFO_FULL   (1<<13)
 #define PCI230_ADC_FIFO_HALF   (1<<14)
 
-/* GROUP Z CLOCK CONFIGURATION REGISTER VALUES */
+/* Group Z clock configuration register values. */
 #define PCI230_ZCLK_CT0                        0
 #define PCI230_ZCLK_CT1                        8
 #define PCI230_ZCLK_CT2                        16
@@ -131,21 +122,19 @@ fix pci autodetection, add support for bus/slot config options
 #define PCI230_ZCLK_SRC_OUTNM1 6               /* The output of the preceding counter/timer channel (OUT n-1). */ 
 #define PCI230_ZCLK_SRC_EXTCLK 7               /* The dedicated external clock input for the group (X1/X2, Y1/Y2, Z1/Z2). */ 
 
-#define PCI230_TIMEBASE_10MHZ  100             /* 10MHz is 100ns. */
+#define PCI230_TIMEBASE_10MHZ  100             /* 10MHz => 100ns. */
 
-/* INTERRUPT ENABLES/STATUS REGISTER VALUES */
+/* Interrupt enables/status register values. */
 #define PCI230_INT_DISABLE             0
 #define PCI230_INT_PPI_C0              1
 #define PCI230_INT_PPI_C3              2
-#define PCI230_INT_ADC_DAC             4
-#define PCI230_INT_ZCLK_CT0            32
+#define PCI230_INT_ADC                 4
+#define PCI230_INT_ZCLK_CT1            32
 
-#define PCI230_TEST_BIT(val, n)        ((val>>n)&1)    /* Assumes you number bit with zero offset, ie. 0-15 */
+#define PCI230_TEST_BIT(val, n)        ((val>>n)&1)    /* Assumes bits numbered with zero offset, ie. 0-15 */
 
 /*
- * Board descriptions for two imaginary boards.  Describing the
- * boards in this way is optional, and completely driver-dependent.
- * Some drivers use arrays such as this, other do not.
+ * Board descriptions for the two boards supported.
  */
 typedef struct pci230_board_struct{
        char *name;
@@ -175,7 +164,7 @@ pci230_board pci230_boards[] = {
        ai_bits:        12,
        have_ao:        0,
        ao_chans:       0,
-       ao_bits:        0,
+       ao_bits:        0,      
        have_dio:       0,
        },
 };
@@ -189,17 +178,21 @@ pci230_board pci230_boards[] = {
    several hardware drivers keep similar information in this structure,
    feel free to suggest moving the variable to the comedi_device struct.  */
 struct pci230_private{
-//     int data;
        struct pci_dev *pci_dev;
-       lsampl_t ao_readback[2];                /* Used for AO readback */
-       unsigned int pci_iobase;                /* PCI230's I/O space 1 */      
+       lsampl_t ao_readback[2];        /* Used for AO readback */
+       unsigned int pci_iobase;        /* PCI230's I/O space 1 */      
        /* Divisors for 8254 counter/timer. */    
        unsigned int divisor0;
        unsigned int divisor1;
        unsigned int divisor2;
-       unsigned int int_en;                    /* Interrupt Enables bits. */   
-       volatile unsigned int count;    /* Number of samples remaining. */
-       unsigned int bipolar;                   /* Set if bipolar range so we know to mangle it in interrupt handler. */
+       unsigned int int_en;            /* Interrupt enables bits. */   
+       unsigned int ai_count;          /* Number of analogue input samples remaining. */
+       unsigned int ao_count;          /* Number of analogue output samples remaining. */
+       unsigned int ai_stop;           /* Flag set when cmd->stop_src == TRIG_NONE - user chooses to stop continuous conversion by cancelation. */
+       unsigned int ao_stop;           /* Flag set when cmd->stop_src == TRIG_NONE - user chooses to stop continuous conversion by cancelation. */
+       unsigned int ai_bipolar;        /* Set if bipolar input range so we know to mangle it. */
+       unsigned int ao_bipolar;        /* Set if bipolar output range so we know to mangle it. */
+       unsigned int ier;                       /* Copy of interrupt enables/status register. */
 };
 
 #define devpriv ((struct pci230_private *)dev->private)
@@ -240,21 +233,54 @@ COMEDI_INITCLEANUP(driver_amplc_pci230);
 static int pci230_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
 static int pci230_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
 static int pci230_ao_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
+static int pci230_ct_insn_config(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
+static int pci230_ct_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
+static void pci230_ns_to_timer(unsigned int *ns,int round);
+static void pci230_z2_ct0(comedi_device *dev, unsigned int *ns,int round);
+static void pci230_z2_ct1(comedi_device *dev, unsigned int *ns,int round);
+static void pci230_z2_ct2(comedi_device *dev, unsigned int *ns,int round);
+static void pci230_cancel_ct0(comedi_device *dev);
+static void pci230_cancel_ct1(comedi_device *dev);
+static void pci230_cancel_ct2(comedi_device *dev);
+static void pci230_interrupt(int irq, void *d, struct pt_regs *regs);
+static int pci230_ao_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd);
+static int pci230_ao_cmd(comedi_device *dev, comedi_subdevice *s);
+static int pci230_ao_cancel(comedi_device *dev, comedi_subdevice *s);
+static void pci230_handle_ao(comedi_device *dev, comedi_subdevice *s);
 static int pci230_ai_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd);
 static int pci230_ai_cmd(comedi_device *dev, comedi_subdevice *s);
-static int pci230_ns_to_timer(unsigned int *ns,int round);
-static int pci230_z2_ct0(comedi_device *dev, unsigned int *ns,int round);
-static int pci230_z2_ct1(comedi_device *dev, unsigned int *ns,int round);
-static int pci230_z2_ct2(comedi_device *dev, unsigned int *ns,int round);
-static void pci230_interrupt(int irq, void *d, struct pt_regs *regs);
-static int pci230__cancel(comedi_device *dev, comedi_subdevice *s);
+static int pci230_ai_cancel(comedi_device *dev, comedi_subdevice *s);
+static void pci230_handle_ai(comedi_device *dev, comedi_subdevice *s);
+static void pci230_handle_fifo_half_full(comedi_device *dev, comedi_subdevice *s);
+static void pci230_handle_fifo_not_empty(comedi_device *dev, comedi_subdevice *s);
  
-/* print out string and val as bits - up to 16 */
-static void printb(int val) {
-       printk("\t%d%d%d%d ",PCI230_TEST_BIT(val, 15), PCI230_TEST_BIT(val, 14), PCI230_TEST_BIT(val, 13), PCI230_TEST_BIT(val, 12));
-       printk("%d%d%d%d ",     PCI230_TEST_BIT(val, 11), PCI230_TEST_BIT(val, 10), PCI230_TEST_BIT(val,  9), PCI230_TEST_BIT(val,  8));
-       printk("%d%d%d%d ",     PCI230_TEST_BIT(val,  7), PCI230_TEST_BIT(val,  6), PCI230_TEST_BIT(val,  5), PCI230_TEST_BIT(val,  4));
-       printk("%d%d%d%d\n",PCI230_TEST_BIT(val,  3), PCI230_TEST_BIT(val,  2), PCI230_TEST_BIT(val,  1), PCI230_TEST_BIT(val,  0));
+static sampl_t pci230_ai_read(comedi_device *dev)
+{
+       /* Read sample. */
+       sampl_t data = (sampl_t) inw(dev->iobase + PCI230_ADCDATA);
+
+       /* PCI230 is 12 bit - stored in upper bits of 16 bit register (lower four bits reserved for expansion). */
+       data = data>>4;
+
+       /* If a bipolar range was specified, mangle it (twos complement->straight binary). */ 
+       if (devpriv->ai_bipolar) {
+               data ^= 1<<(thisboard->ai_bits-1);
+       }
+       return data;
+}
+
+static void pci230_ao_write(comedi_device *dev, sampl_t data, int chan)
+{
+       /* If a bipolar range was specified, mangle it (straight binary->twos complement). */ 
+       if (devpriv->ao_bipolar) {
+               data ^= 1<<(thisboard->ai_bits-1);
+       }
+               
+       /* PCI230 is 12 bit - stored in upper bits of 16 bit register (lower four bits reserved for expansion). */
+       data = data<<4;
+               
+       /* Write data. */
+       outw((unsigned int) data, dev->iobase + (((chan) == 0) ? PCI230_DACOUT1 : PCI230_DACOUT2));
 }
 
 /*
@@ -266,12 +292,12 @@ static void printb(int val) {
 static int pci230_attach(comedi_device *dev,comedi_devconfig *it)
 {
        comedi_subdevice *s;
-       int pci_iobase, iobase = 0;             /* PCI230's I/O spaces 1 and 2 */
+       int pci_iobase, iobase = 0;             /* PCI230's I/O spaces 1 and 2 respectively. */
        struct pci_dev *pci_dev;
-       int i=0,ret;
+       int i=0,irq_hdl;
 
        printk("comedi%d: amplc_pci230\n",dev->minor);
-
+       
        /* Find card */
        pci_for_each_dev(pci_dev){
                if(pci_dev->vendor != PCI_VENDOR_ID_AMPLICON)
@@ -289,20 +315,21 @@ static int pci230_attach(comedi_device *dev,comedi_devconfig *it)
        }
        dev->board_ptr = pci230_boards+i;
        
-       /* Read base addressses of the PCI230's two I/O regions from PCI configuration register */
+       /* Read base addressses of the PCI230's two I/O regions from PCI configuration register. */
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
        pci_iobase = pci_dev->base_address[2] & PCI_BASE_ADDRESS_IO_MASK;
        iobase = pci_dev->base_address[3] & PCI_BASE_ADDRESS_IO_MASK;
 #else
-       if(pci_enable_device(pci_dev))
-               return -EIO;
-       pci_iobase = pci_dev->resource[2].start & PCI_BASE_ADDRESS_IO_MASK;
-       iobase = pci_dev->resource[3].start & PCI_BASE_ADDRESS_IO_MASK;
+       if(pci_enable_device(pci_dev)<0)return -EIO;
+
+       pci_iobase = pci_dev->resource[2].start;
+       iobase = pci_dev->resource[3].start;
 #endif
 
        printk("comedi%d: amplc_pci230: I/O region 1 0x%04x I/O region 2 0x%04x\n",dev->minor, pci_iobase, iobase);
 
-       /* Allocate the private structure area using alloc_private() (macro defined in comedidev.h.) */
+       /* Allocate the private structure area using alloc_private().
+        * Macro defined in comedidev.h - memsets struct fields to 0. */
        if((alloc_private(dev,sizeof(struct pci230_private)))<0)
                return -ENOMEM;
        devpriv->pci_dev = pci_dev;
@@ -329,18 +356,26 @@ static int pci230_attach(comedi_device *dev,comedi_devconfig *it)
  */
        dev->board_name = thisboard->name;
 
+       /* Register the interrupt handler. */
+       irq_hdl = comedi_request_irq(devpriv->pci_dev->irq, pci230_interrupt, 0, "PCI230", dev );
+       if(irq_hdl<0) {
+               printk("comedi%d: amplc_pci230: unable to register irq, commands will not be available %d\n", dev->minor, devpriv->pci_dev->irq);
+       }
+       else {
+               dev->irq = devpriv->pci_dev->irq;
+               printk("comedi%d: amplc_pci230: registered irq %d\n", dev->minor, devpriv->pci_dev->irq);
+       }
 
 /*
  * Allocate the subdevice structures.  alloc_subdevice() is a
  * convenient macro defined in comedidev.h.  It relies on
  * n_subdevices being set correctly.
  */
-       dev->n_subdevices=3;
+       dev->n_subdevices=4;
        if(alloc_subdevices(dev)<0)
                return -ENOMEM;
 
        s=dev->subdevices+0;
-       dev->read_subdev=s;
        /* analog input subdevice */
        s->type=COMEDI_SUBD_AI;
        s->subdev_flags=SDF_READABLE;
@@ -348,9 +383,14 @@ static int pci230_attach(comedi_device *dev,comedi_devconfig *it)
        s->maxdata=(1<<thisboard->ai_bits)-1;
        s->range_table=&pci230_ai_range;
        s->insn_read = &pci230_ai_rinsn;
-       s->do_cmd = &pci230_ai_cmd;
        s->len_chanlist = thisboard->ai_chans;
-       s->do_cmdtest = &pci230_ai_cmdtest;
+       /* Only register commands if the interrupt handler is installed. */
+       if(irq_hdl==0) {
+               dev->read_subdev=s;
+               s->do_cmd = &pci230_ai_cmd;
+               s->do_cmdtest = &pci230_ai_cmdtest;
+               s->cancel = pci230_ai_cancel;
+       }
 
        s=dev->subdevices+1;
        /* analog output subdevice */
@@ -361,6 +401,14 @@ static int pci230_attach(comedi_device *dev,comedi_devconfig *it)
        s->range_table=&pci230_ao_range;
        s->insn_write = &pci230_ao_winsn;
        s->insn_read = &pci230_ao_rinsn;
+       s->len_chanlist = thisboard->ao_chans;
+       /* Only register commands if the interrupt handler is installed. */
+       if(irq_hdl==0) {
+               dev->write_subdev=s;
+               s->do_cmd = &pci230_ao_cmd;
+               s->do_cmdtest = &pci230_ao_cmdtest;
+               s->cancel = pci230_ao_cancel;
+       }
 
        s=dev->subdevices+2;
        /* digital i/o subdevice */
@@ -370,15 +418,15 @@ static int pci230_attach(comedi_device *dev,comedi_devconfig *it)
                s->type = COMEDI_SUBD_UNUSED;
        }
 
-       /* Register the interrupt handler. */
-       ret = comedi_request_irq(devpriv->pci_dev->irq, pci230_interrupt, 0, "PCI230", dev );
-       if(ret<0)
-       {
-               printk("comedi%d: amplc_pci230: unable to register irq %d\n", dev->minor, devpriv->pci_dev->irq);
-               return ret;
-       }
-       dev->irq = devpriv->pci_dev->irq;
-       printk("comedi%d: amplc_pci230: registered irq %d\n", dev->minor, devpriv->pci_dev->irq);
+       s=dev->subdevices+3;
+       /* timer subdevice */
+       s->type=COMEDI_SUBD_TIMER;
+       s->subdev_flags=SDF_READABLE;
+       s->n_chan=1;
+       s->maxdata=0xffff;
+       s->range_table=&range_digital;
+       s->insn_config = pci230_ct_insn_config;
+       s->insn_read = &pci230_ct_rinsn;
 
        printk("attached\n");
 
@@ -417,48 +465,58 @@ static int pci230_detach(comedi_device *dev)
 }
 
 /*
- * "instructions" read/write data in "one-shot" or "software-triggered"
- * mode.
- */
+ *  COMEDI_SUBD_AI instruction; 
+ */  
 static int pci230_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
 {
        int n,i;
-       int chan, range;
-       unsigned int d;
+       int chan, range, aref;
        unsigned int status;
        unsigned int adccon, adcen, adcg;
-       unsigned int bipolar;
 
        /* Unpack channel and range. */
        chan = CR_CHAN(insn->chanspec);
        range = CR_RANGE(insn->chanspec);
-       
-       printk("\ncomedi%d: amplc_pci230::pci230_ai_rinsn() chan %d range %d\n",dev->minor, chan, range);
+       aref = CR_AREF(insn->chanspec);
 
        /* If bit 2 of range unset, range is referring to bipolar element in range table */
-       bipolar = !PCI230_TEST_BIT(range, 2);
-       adccon = PCI230_ADC_IM_SE | PCI230_ADC_TRIG_SW | PCI230_ADC_SW_CHAN(chan);
-       if (bipolar) {
-               adccon = adccon | PCI230_ADC_IR_BIP;
-               adcg = range<<(chan-chan%2);
+       adccon = PCI230_ADC_TRIG_SW | PCI230_ADC_FIFO_RESET;
+       devpriv->ai_bipolar = !PCI230_TEST_BIT(range, 2);
+       if (aref==AREF_DIFF) {
+               /* Differential. */
+               adcen = 3<<2*chan;
+               adccon |= PCI230_ADC_IM_DIF;    
+               if (devpriv->ai_bipolar) {
+                       adccon |= PCI230_ADC_IR_BIP;
+                       adcg = range<<(2*chan-2*chan%2);
+               }
+               else {
+                       adccon |= PCI230_ADC_IR_UNI;
+                       adcg = ((range&(~4))+1)<<(2*chan-2*chan%2);
+               }
        }
        else {
-               adccon = adccon | PCI230_ADC_IR_UNI;
-               adcg = ((range&(~4))+1)<<(chan-chan%2);
+               /* Single ended. */
+               adcen = 1<<chan;
+               adccon |= PCI230_ADC_IM_SE;     
+               if (devpriv->ai_bipolar) {
+                       adccon |= PCI230_ADC_IR_BIP;
+                       adcg = range<<(chan-chan%2);
+               }
+               else {
+                       adccon |= PCI230_ADC_IR_UNI;
+                       adcg = ((range&(~4))+1)<<(chan-chan%2);
+               }
        }
-       adcen = 1<<chan;
-
-       /* Specify uni/bip, se/diff, s/w conversion and channel. */
-       outw_p(adccon, dev->iobase + PCI230_ADCCON);
-       printk("comedi%d: amplc_pci230::pci230_ai_rinsn() wrote PCI230_ADCCON",dev->minor); printb(adccon);
 
        /* Enable only this channel in the scan list - otherwise by default we'll get one sample from each channel. */
        outw_p(adcen, dev->iobase + PCI230_ADCEN);
-       printk("comedi%d: amplc_pci230::pci230_ai_rinsn() wrote PCI230_ADCEN", dev->minor); printb(adcen);
 
        /* Set gain for channel. */
        outw_p(adcg, dev->iobase + PCI230_ADCG);
-       printk("comedi%d: amplc_pci230::pci230_ai_rinsn() wrote PCI230_ADCG", dev->minor); printb(adcg);
+
+       /* Specify uni/bip, se/diff, s/w conversion, and reset FIFO (even though we're not using it - MEV says so). */
+       outw_p(adccon, dev->iobase + PCI230_ADCCON);
 
        /* Wait for mux to settle */
        udelay(PCI230_MUX_SETTLE);
@@ -472,7 +530,6 @@ static int pci230_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *i
                /* wait for conversion to end */
                for(i=0;i<TIMEOUT;i++){
                        status = inw(dev->iobase + PCI230_ADCCON);
-                       printk("comedi%d: amplc_pci230::pci230_ai_rinsn() read PCI230_ADCCON",dev->minor); printb(status);
                        if(PCI230_TEST_BIT(status, PCI230_ADC_BUSY_BIT))break;
                }
                if(i==TIMEOUT){
@@ -483,31 +540,107 @@ static int pci230_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *i
                }
 
                /* read data */
-               d = inw(dev->iobase + PCI230_ADCDATA);
-               printk("comedi%d: amplc_pci230::pci230_ai_rinsn() read PCI230_ADCDATA 0x%04x\n",dev->minor, d);
+               data[n] = pci230_ai_read(dev);
+       }
 
-               /* PCI230 is 12 bit - stored in upper bits of 16 bit register (lower four bits reserved for expansion). */
-               d = d>>4;
+       /* return the number of samples read/written */
+       return n;
+}
 
-               /* If a bipolar range was specified, mangle it (twos complement->straight binary). */ 
-               if (bipolar) {
-                       d ^= 1<<(thisboard->ai_bits-1);
+/*
+ *  COMEDI_SUBD_AO instructions; 
+ */  
+static int pci230_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+{
+       int i;
+       int chan, range;
+       
+       /* Unpack channel and range. */
+       chan = CR_CHAN(insn->chanspec);
+       range = CR_RANGE(insn->chanspec);
+
+       /* Set range - see analogue output range table; 0 => unipolar 10V, 1 => bipolar +/-10V range scale */
+       devpriv->ao_bipolar = PCI230_TEST_BIT(range, PCI230_DAC_BIP_BIT);
+       outw(range, dev->iobase + PCI230_DACCON);
+
+       /* Writing a list of values to an AO channel is probably not
+        * very useful, but that's how the interface is defined. */
+       for(i=0;i<insn->n;i++){
+               /* Store the value to be written to the DAC in our pci230_private struct before mangling it. */
+               devpriv->ao_readback[chan] = data[i];
+
+               /* Write value to DAC. */
+               pci230_ao_write(dev, data[i], chan);
+
+#if 0
+               /* XXX screw the user.  Only do this if the board gets upset if you don't */
+               /* If we're writing more than one sample, wait for output to settle between successive writes */
+               if (insn->n > 1) {
+               udelay(PCI230_DAC_SETTLE);
                }
-               data[n] = d;
+#endif
        }
 
        /* return the number of samples read/written */
-       return n;
+       return i;
 }
 
-static int pci230_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,
+/* AO subdevices should have a read insn as well as a write insn.
+ * Usually this means copying a value stored in devpriv. */
+static int pci230_ao_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+{
+       int i;
+       int chan = CR_CHAN(insn->chanspec);
+
+       for(i=0;i<insn->n;i++)
+               data[i] = devpriv->ao_readback[chan];
+
+       return i;
+}
+
+/*
+ *  COMEDI_SUBD_TIMER instructions; 
+ *  
+ *  insn_config allows user to start and stop counter/timer 2 (SK1 pin 21).
+ *  Period specified in ns.
+ *
+ *  rinsn returns counter/timer's actual period in ns.  
+ */
+static int pci230_ct_insn_config(comedi_device *dev,comedi_subdevice *s,
+       comedi_insn *insn,lsampl_t *data)
+{
+       unsigned int ns;
+       
+       if(insn->n!=1)return -EINVAL;
+
+       ns = data[0];
+       if (ns == 0) {
+               //Stop counter/timer 2.
+               pci230_cancel_ct2(dev);
+       }
+       else {
+               //Start conter/timer 2 with period ns.
+               pci230_z2_ct2(dev, &ns, TRIG_ROUND_MASK);
+       }
+
+       return 1;
+}
+
+static int pci230_ct_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+{
+       if(insn->n!=1)return -EINVAL;
+       
+       /* Return the actual period set in ns. */
+       data[0] = PCI230_TIMEBASE_10MHZ*devpriv->divisor1*devpriv->divisor2;
+       return 1;
+}
+
+static int pci230_ao_cmdtest(comedi_device *dev,comedi_subdevice *s,
        comedi_cmd *cmd)
 {
        int err=0;
        int tmp;
 
-       printk("comedi%d: amplc_pci230::pci230_ai_cmdtest()\n",dev->minor);
-
        /* cmdtest tests a particular command to see if it is valid.
         * Using the cmdtest ioctl, a user can create a valid cmd
         * and then have it executes by the cmd ioctl.
@@ -520,15 +653,15 @@ static int pci230_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,
         * if this fails. */
 
        tmp=cmd->start_src;
-       cmd->start_src &= TRIG_NOW | TRIG_EXT;
+       cmd->start_src &= TRIG_INT;
        if(!cmd->start_src || tmp!=cmd->start_src)err++;
 
        tmp=cmd->scan_begin_src;
-       cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
+       cmd->scan_begin_src &= TRIG_TIMER;
        if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++;
 
        tmp=cmd->convert_src;
-       cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
+       cmd->convert_src &= TRIG_NOW;
        if(!cmd->convert_src || tmp!=cmd->convert_src)err++;
 
        tmp=cmd->scan_end_src;
@@ -545,18 +678,9 @@ static int pci230_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,
         * "source conflict" returned by comedilib to user mode process 
         * if this fails. */
 
-       if(cmd->start_src!=TRIG_NOW &&
-          cmd->start_src!=TRIG_EXT)err++;
-       if(cmd->scan_begin_src!=TRIG_TIMER &&
-          cmd->scan_begin_src!=TRIG_EXT)err++;
-       if(cmd->convert_src!=TRIG_TIMER &&
-          cmd->convert_src!=TRIG_EXT)err++;
        if(cmd->stop_src!=TRIG_COUNT &&
           cmd->stop_src!=TRIG_NONE)err++;
        
-       /* XXX Should check here if we aren't able to have multiple
-        * sources as TRIG_EXT */
-
        if(err)return 2;
 
        /* Step 3: make sure arguments are trivially compatible.
@@ -568,28 +692,167 @@ static int pci230_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,
                err++;
        }
 
-#define MAX_SPEED      10000           /* in nanoseconds */
-#define MIN_SPEED      1000000000      /* in nanoseconds */
+#define MAX_SPEED      3205            /* 3205ns => 312kHz */
+#define MIN_SPEED      4294967295u     /* 4294967295ns = 4.29s - Comedi limit due to unsigned int cmd.  Driver limit = 2^32 (2 cascaded 16bit counters) * 100ns (default 10MHz onboard clock) = 429s */
 
        if(cmd->scan_begin_src==TRIG_TIMER){
-               if(cmd->scan_begin_arg<MAX_SPEED){
-                       cmd->scan_begin_arg=MAX_SPEED;
+               if(cmd->scan_begin_src<MAX_SPEED){
+                       cmd->scan_begin_src=MAX_SPEED;
                        err++;
                }
-               if(cmd->scan_begin_arg>MIN_SPEED){
-                       cmd->scan_begin_arg=MIN_SPEED;
+               if(cmd->scan_begin_src>MIN_SPEED){
+                       cmd->scan_begin_src=MIN_SPEED;
                        err++;
                }
-       }else{
-               /* external trigger */
-               /* should be level/edge, hi/lo specification here */
-               /* should specify multiple external triggers */
-               /* XXX Do you really have 10 available triggering channels? */
-               if(cmd->scan_begin_arg>9){
-                       cmd->scan_begin_arg=9;
+       }
+
+       if(cmd->scan_end_arg!=cmd->chanlist_len){
+               cmd->scan_end_arg=cmd->chanlist_len;
+               err++;
+       }
+       if(cmd->stop_src==TRIG_NONE){
+               /* TRIG_NONE */
+               if(cmd->stop_arg!=0){
+                       cmd->stop_arg=0;
                        err++;
                }
        }
+
+       if(err)return 3;
+
+       /* Step 4: fix up any arguments.
+        * "argument conflict" returned by comedilib to user mode process 
+        * if this fails. */
+
+       if(cmd->scan_begin_src==TRIG_TIMER){
+               tmp=cmd->scan_begin_arg;
+               pci230_ns_to_timer(&cmd->scan_begin_arg,cmd->flags&TRIG_ROUND_MASK);
+               if(tmp!=cmd->scan_begin_arg)err++;
+       }
+
+       if(err)return 4;
+
+       return 0;
+}
+
+static int pci230_ao_inttrig(comedi_device *dev,comedi_subdevice *s,
+               unsigned int x)
+{
+       if(x!=0)return -EINVAL;
+
+       /* Enable DAC interrupt. */
+       devpriv->ier |= PCI230_INT_ZCLK_CT1;
+       outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE);
+
+       s->async->inttrig=NULL;
+
+       return 1;
+}
+
+static int pci230_ao_cmd(comedi_device *dev,comedi_subdevice *s)
+{
+       int range;
+       
+       /* Get the command. */
+       comedi_async *async = s->async;
+       comedi_cmd *cmd = &async->cmd;
+
+       /* Calculate number of conversions required. */
+       if(cmd->stop_src == TRIG_COUNT) {
+               devpriv->ao_count = cmd->stop_arg * cmd->chanlist_len;
+               devpriv->ao_stop = 0;
+       }
+       else {
+               /* TRIG_NONE, user calls cancel. */
+               devpriv->ao_count = 0;
+               devpriv->ao_stop = 1;
+       }
+       
+       /* Disable DAC interrupt. */
+       devpriv->ier &= ~PCI230_INT_ZCLK_CT1;
+       outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE);
+
+       /* Set range - see analogue output range table; 0 => unipolar 10V, 1 => bipolar +/-10V range scale */
+       range = CR_RANGE(cmd->chanlist[0]);
+       devpriv->ao_bipolar = PCI230_TEST_BIT(range, PCI230_DAC_BIP_BIT);
+       outw(range, dev->iobase + PCI230_DACCON);
+
+       /* Set the counter timers to the specified sampling frequency.
+        * TODO - when Comedi supports concurrent commands, this must be
+        * changed; using ct0 and ct1 for DAC will screw up ADC pacer
+        * which uses ct2 and ct0.  Change to only use ct1 for DAC?
+        */
+       pci230_z2_ct1(dev, &cmd->convert_arg, cmd->flags & TRIG_ROUND_MASK);    /* cmd->convert_arg is sampling period in ns */
+
+       s->async->inttrig=pci230_ao_inttrig;
+
+       return 0;
+}
+
+static int pci230_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,
+       comedi_cmd *cmd)
+{
+       int err=0;
+       int tmp;
+
+       /* cmdtest tests a particular command to see if it is valid.
+        * Using the cmdtest ioctl, a user can create a valid cmd
+        * and then have it executes by the cmd ioctl.
+        *
+        * cmdtest returns 1,2,3,4 or 0, depending on which tests
+        * the command passes. */
+
+       /* Step 1: make sure trigger sources are trivially valid.
+        * "invalid source" returned by comedilib to user mode process 
+        * if this fails. */
+
+       tmp=cmd->start_src;
+       cmd->start_src &= TRIG_NOW;
+       if(!cmd->start_src || tmp!=cmd->start_src)err++;
+
+       tmp=cmd->scan_begin_src;
+       cmd->scan_begin_src &= TRIG_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_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
+        * "source conflict" returned by comedilib to user mode process 
+        * if this fails. */
+
+       if(cmd->start_src!=TRIG_NOW)err++;
+       if(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)err++;
+       
+       if(err)return 2;
+
+       /* Step 3: make sure arguments are trivially compatible.
+        * "invalid argument" returned by comedilib to user mode process 
+        * if this fails. */
+
+       if(cmd->start_arg!=0){
+               cmd->start_arg=0;
+               err++;
+       }
+
+#define MAX_SPEED      3205            /* 3205ns => 312kHz max sampling - claimed max for single channel; should really be fn(nchans) */
+#define MIN_SPEED      4294967295u     /* 4294967295ns = 4.29s - Comedi limit due to unsigned int cmd.  Driver limit = 2^32 (2 cascaded 16bit counters) * 100ns (default 10MHz onboard clock) = 429s */
+
        if(cmd->convert_src==TRIG_TIMER){
                if(cmd->convert_arg<MAX_SPEED){
                        cmd->convert_arg=MAX_SPEED;
@@ -601,9 +864,10 @@ static int pci230_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,
                }
        }else{
                /* external trigger */
-               /* see above */
-               if(cmd->convert_arg>9){
-                       cmd->convert_arg=9;
+               /* convert_arg == 0 => trigger on -ve edge. */
+               /* convert_arg == 1 => trigger on +ve edge. */
+               if(cmd->convert_arg>1){
+                       cmd->convert_arg=1;                     /* Default to trigger on +ve edge. */
                        err++;
                }
        }
@@ -612,12 +876,7 @@ static int pci230_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,
                cmd->scan_end_arg=cmd->chanlist_len;
                err++;
        }
-       if(cmd->stop_src==TRIG_COUNT){
-               if(cmd->stop_arg>0x00ffffff){
-                       cmd->stop_arg=0x00ffffff;
-                       err++;
-               }
-       }else{
+       if(cmd->stop_src==TRIG_NONE){
                /* TRIG_NONE */
                if(cmd->stop_arg!=0){
                        cmd->stop_arg=0;
@@ -631,20 +890,10 @@ static int pci230_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,
         * "argument conflict" returned by comedilib to user mode process 
         * if this fails. */
 
-       if(cmd->scan_begin_src==TRIG_TIMER){
-               tmp=cmd->scan_begin_arg;
-               pci230_ns_to_timer(&cmd->scan_begin_arg,cmd->flags&TRIG_ROUND_MASK);
-               if(tmp!=cmd->scan_begin_arg)err++;
-       }
        if(cmd->convert_src==TRIG_TIMER){
                tmp=cmd->convert_arg;
                pci230_ns_to_timer(&cmd->convert_arg,cmd->flags&TRIG_ROUND_MASK);
                if(tmp!=cmd->convert_arg)err++;
-               if(cmd->scan_begin_src==TRIG_TIMER &&
-                 cmd->scan_begin_arg<cmd->convert_arg*cmd->scan_end_arg){
-                       cmd->scan_begin_arg=cmd->convert_arg*cmd->scan_end_arg;
-                       err++;
-               }
        }
 
        if(err)return 4;
@@ -654,71 +903,69 @@ static int pci230_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,
 
 static int pci230_ai_cmd(comedi_device *dev,comedi_subdevice *s)
 {
-       int i, chan, range, aref;
+       int i, chan, range, diff;
        unsigned int adccon, adcen, adcg;
-
+       
        /* Get the command. */
        comedi_async *async = s->async;
        comedi_cmd *cmd = &async->cmd;
 
-       /* Print the command */
-       printk("comedi%d: amplc_pci230::pci230_ai_cmd()\n",dev->minor);
-       printk("\tflags \t\t %d\n", cmd->flags);
-       printk("\tstart \t\t src %d arg %d\n", cmd->start_src, cmd->start_arg);
-       printk("\tscan_begin \t src %d arg %d\n", cmd->scan_begin_src, cmd->scan_begin_arg);
-       printk("\tconvert \t src %d arg %d\n", cmd->convert_src, cmd->convert_arg);
-       printk("\tscan_end \t src %d arg %d\n", cmd->scan_end_src, cmd->scan_end_arg);
-       printk("\tstop \t\t src %d arg %d\n", cmd->stop_src, cmd->stop_arg);
-       for (chan = 0; chan < cmd->chanlist_len; chan++) {
-               printk("\tchannel %d\t range %d\t aref %d\n", CR_CHAN(cmd->chanlist[chan]), CR_RANGE(cmd->chanlist[chan]), CR_AREF(cmd->chanlist[chan]));
-       }
-
-       if(!dev->irq)
-       {
-               /* XXX Instead of checking here, just don't set do_cmd in
-                * _attach() */
-               comedi_error(dev, "no irq assigned for PCI230, cannot do hardware conversions");
-               return -1;
-       }
-
        /* Calculate number of conversions required. */
        if(cmd->stop_src == TRIG_COUNT) {
-               devpriv->count = cmd->stop_arg * cmd->chanlist_len;
-               printk("comedi%d: amplc_pci230::pci230_ai_cmd() total number of conversions %d\n",dev->minor, devpriv->count);
+               devpriv->ai_count = cmd->stop_arg * cmd->chanlist_len;
+               devpriv->ai_stop = 0;
        }
        else {
-               devpriv->count = 0;
+               /* TRIG_NONE, user calls cancel. */
+               devpriv->ai_count = 0;
+               devpriv->ai_stop = 1;
        }
-
+       
        /* Steps;
-        * - Disable board interrupts.
-        * - Reset FIFO, specify uni/bip, se/diff, and start conversion source to none.
+        * - Disable ADC interrupts.
+        * - Enable and reset FIFO, specify uni/bip, se/diff, and start conversion source to none.
         * - Set channel scan list.
         * - Set channel gains.
-        * - Set the counter timers to the specified sampling frequency.
         * - Enable conversion complete interrupt.
-        * - Enable FIFO, set FIFO interrupt trigger level, set start conversion source to counter 1.
+        * - Set the counter timers to the specified sampling frequency.
+        * - Enable FIFO, set FIFO interrupt trigger level, set start conversion source to counter 0. 
         */
 
-       /* Disable interrupts. */
-       outb(PCI230_INT_DISABLE, devpriv->pci_iobase + PCI230_INT_SCE);
-       printk("comedi%d: amplc_pci230::pci230_ai_cmd wrote PCI230_INT_SCE",dev->minor); printb(PCI230_INT_DISABLE);
+       /* Disable ADC interrupt. */
+       devpriv->ier &= ~PCI230_INT_ADC;
+       outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE);
+
+       if (CR_AREF(cmd->chanlist[0])==AREF_DIFF) {
+               /* Differential - all channels must be differential. */
+               diff = 1;
+               adccon = PCI230_ADC_IM_DIF;     
+       }
+       else {
+               /* Single ended - all channels must be single-ended. */
+               diff = 0;
+               adccon = PCI230_ADC_IM_SE;      
+       }
 
-       adccon = PCI230_ADC_FIFO_RESET | PCI230_ADC_IM_SE | PCI230_ADC_TRIG_NONE;
+       adccon |= PCI230_ADC_FIFO_RESET | PCI230_ADC_FIFO_EN;
        adcg = 0;
        adcen = 0;
 
        /* If bit 2 of range unset, range is referring to bipolar element in range table */
-       /* XXX set range? */
        range = CR_RANGE(cmd->chanlist[0]);
-       devpriv->bipolar = !PCI230_TEST_BIT(range, 2);  
-       if (devpriv->bipolar) {
+       devpriv->ai_bipolar = !PCI230_TEST_BIT(range, 2);       
+       if (devpriv->ai_bipolar) {
                adccon |= PCI230_ADC_IR_BIP;
                for (i = 0; i < cmd->chanlist_len; i++) {
                        chan = CR_CHAN(cmd->chanlist[i]);
                        range = CR_RANGE(cmd->chanlist[i]);
-                       adcg |= range<<(chan-chan%2);
-                       adcen |= 1<<chan;
+                       if (diff) {
+                               adcg |= range<<(2*chan-2*chan%2);
+                               adcen |= 3<<2*chan;
+                       }
+                       else {
+                               adcg |= range<<(chan-chan%2);
+                               adcen |= 1<<chan;
+                       }
                }
        }
        else {
@@ -726,40 +973,65 @@ static int pci230_ai_cmd(comedi_device *dev,comedi_subdevice *s)
                for (i = 0; i < cmd->chanlist_len; i++) {
                        chan = CR_CHAN(cmd->chanlist[i]);
                        range = CR_RANGE(cmd->chanlist[i]);
-                       adcg |= ((range&(~4))+1)<<(chan-chan%2);
-                       adcen |= 1<<chan;
+                       if (diff) {
+                               adcg |= ((range&(~4))+1)<<(2*chan-2*chan%2);
+                               adcen |= 3<<2*chan;
+                       }
+                       else {
+                               adcg |= ((range&(~4))+1)<<(chan-chan%2);
+                               adcen |= 1<<chan;
+                       }
                }
        }
 
-       /* Reset FIFO, specify uni/bip, se/diff, and start conversion source to none. */
-       outw_p(adccon, dev->iobase + PCI230_ADCCON);
-       printk("comedi%d: amplc_pci230::pci230_ai_cmd wrote PCI230_ADCCON",dev->minor); printb(adccon);
+       /* Enable and reset FIFO, specify FIFO trigger level full, specify uni/bip, se/diff, and start conversion source to none. */
+       outw(adccon | PCI230_ADC_INT_FIFO_FULL | PCI230_ADC_TRIG_NONE, dev->iobase + PCI230_ADCCON);
 
        /* Set channel scan list. */
-       outw_p(adcen, dev->iobase + PCI230_ADCEN);
-       printk("comedi%d: amplc_pci230::pci230_ai_cmd wrote PCI230_ADCEN", dev->minor); printb(adcen);
+       outw(adcen, dev->iobase + PCI230_ADCEN);
 
        /* Set channel gains. */
-       outw_p(adcg, dev->iobase + PCI230_ADCG);
-       printk("comedi%d: amplc_pci230::pci230_ai_cmd wrote PCI230_ADCG", dev->minor); printb(adcg);
+       outw(adcg, dev->iobase + PCI230_ADCG);
 
-       /* Set the counter timers to the specified sampling frequency. */
-       pci230_z2_ct1(dev, &cmd->convert_arg, cmd->flags & TRIG_ROUND_MASK);
+       /* Enable ADC (conversion complete) interrupt. */
+       devpriv->ier |= PCI230_INT_ADC;
+       outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE);
 
-       /* Enable conversion complete interrupt. */
-       outb(PCI230_INT_ADC_DAC, devpriv->pci_iobase + PCI230_INT_SCE);
-       printk("comedi%d: amplc_pci230::pci230_ai_cmd wrote PCI230_INT_SCE",dev->minor); printb(PCI230_INT_ADC_DAC);
+       /* Set start conversion source. */
+       if(cmd->convert_src == TRIG_TIMER) {
+               /* Onboard counter/timer 0. */
+               adccon = adccon | PCI230_ADC_TRIG_Z2CT0;
 
-       /* Enable FIFO, set FIFO interrupt trigger level, set start conversion source to counter 1. */
-       adccon = (adccon & ~PCI230_ADC_FIFO_RESET) | PCI230_ADC_FIFO_EN | PCI230_ADC_TRIG_Z2CT1;
-       if (devpriv->count < 2048) {
-               adccon = adccon | PCI230_ADC_INT_FIFO_NEMPTY;
+               /* Set the counter timers to the specified sampling frequency. */
+               pci230_z2_ct0(dev, &cmd->convert_arg, cmd->flags & TRIG_ROUND_MASK);    /* cmd->convert_arg is sampling period in ns */
        }
        else {
+               /* TRIG_EXT - external trigger. */
+               if (cmd->convert_arg) {
+                       /* Trigger on +ve edge. */
+                       adccon = adccon | PCI230_ADC_TRIG_EXTP;
+               }
+               else {
+                       /* Trigger on -ve edge. */
+                       adccon = adccon | PCI230_ADC_TRIG_EXTN;
+               }       
+       }
+
+       /* Set FIFO interrupt trigger level. */
+       if(cmd->stop_src == TRIG_COUNT) {
+               if (devpriv->ai_count < 2048) {
+                       adccon = adccon | PCI230_ADC_INT_FIFO_NEMPTY;
+               }
+               else {
+                       adccon = adccon | PCI230_ADC_INT_FIFO_HALF;
+               }
+       }
+       else {
+               /* TRIG_NONE - trigger on half-full FIFO. */
                adccon = adccon | PCI230_ADC_INT_FIFO_HALF;
        }
-       outw_p(adccon, dev->iobase + PCI230_ADCCON);
-       printk("comedi%d: amplc_pci230::pci230_ai_cmd wrote PCI230_ADCCON",dev->minor); printb(adccon);
+       
+       outw(adccon, dev->iobase + PCI230_ADCCON);
 
        return 0;
 }
@@ -770,333 +1042,291 @@ static int pci230_ai_cmd(comedi_device *dev,comedi_subdevice *s)
  * convert ns nanoseconds to a counter value suitable for programming
  * the device.  Also, it should adjust ns so that it cooresponds to
  * the actual time that the device will use. */
-static int pci230_ns_to_timer(unsigned int *ns,int round)
+static void pci230_ns_to_timer(unsigned int *ns,int round)
 {
        unsigned int divisor0, divisor1;
        i8253_cascade_ns_to_timer_2div(PCI230_TIMEBASE_10MHZ, &divisor0, &divisor1, ns, TRIG_ROUND_MASK);
-       printk("comedi: amplc_pci230::pci230_ns_to_timer divisor0 %d divisor1 %d ns %d\n",divisor0, divisor1, *ns);
-       return *ns;
+       return;
 }
 
 /* 
  *  Set ZCLK_CT0 to square wave mode with period of ns.
- *  Default clk source for DAC.
+ *  Default clk source for ADC.
  */
-static int pci230_z2_ct0(comedi_device *dev, unsigned int *ns,int round)
+static void pci230_z2_ct0(comedi_device *dev, unsigned int *ns,int round)
 {
        /* For two cascaded counter/timers, calculate the divide ratios required to give a square wave of period ns. */
        i8253_cascade_ns_to_timer_2div(PCI230_TIMEBASE_10MHZ, &devpriv->divisor2, &devpriv->divisor0, ns, TRIG_ROUND_MASK);
-       printk("comedi%d: amplc_pci230::pci230_z2_ct0() divisor2 %d divisor0 %d ns %d\n",dev->minor, devpriv->divisor2, devpriv->divisor0, *ns);
 
     /* Generic i8254_load calls; program counters' divide ratios. */
-       i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 2, devpriv->divisor2, 2);       /* Counter 2, divisor2, square wave (8254 mode 2). */
-       i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 0, devpriv->divisor0, 2);       /* Counter 0, divisor0, square wave (8254 mode 2). */
+       i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 2, devpriv->divisor2, 3);       /* Counter 2, divisor2, square wave (8254 mode 3). */
+       i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 0, devpriv->divisor0, 3);       /* Counter 0, divisor0, square wave (8254 mode 3). */
 
        /* PCI 230 specific - ties up counter clk inputs with clk sources */
        outb(PCI230_ZCLK_CT2 | PCI230_ZCLK_SRC_10MHZ, devpriv->pci_iobase + PCI230_ZCLK_SCE);   /* Program counter 2's input clock source. */
        outb(PCI230_ZCLK_CT0 | PCI230_ZCLK_SRC_OUTNM1, devpriv->pci_iobase + PCI230_ZCLK_SCE);  /* Program counter 0's input clock source. */
 
-       return *ns;
+       return;
+}
+
+static void pci230_cancel_ct0(comedi_device *dev)
+{
+       devpriv->divisor2 = 0;
+       devpriv->divisor0 = 0;
+       i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 2, devpriv->divisor2, 0);       /* Counter 2, divisor2, 8254 mode 0. */
+       i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 0, devpriv->divisor0, 0);       /* Counter 0, divisor0, 8254 mode 0. */
 }
 
 /* 
  *  Set ZCLK_CT1 to square wave mode with period of ns.
- *  Default clk source for ADC.
+ *  Default clk source for DAC.
  */
-static int pci230_z2_ct1(comedi_device *dev, unsigned int *ns,int round)
+static void pci230_z2_ct1(comedi_device *dev, unsigned int *ns,int round)
 {
        /* For two cascaded counter/timers, calculate the divide ratios required to give a square wave of period ns. */
        i8253_cascade_ns_to_timer_2div(PCI230_TIMEBASE_10MHZ, &devpriv->divisor0, &devpriv->divisor1, ns, TRIG_ROUND_MASK);
-       printk("comedi%d: amplc_pci230::pci230_z2_ct1() divisor0 %d divisor1 %d ns %d\n",dev->minor, devpriv->divisor0, devpriv->divisor1, *ns);
 
     /* Generic i8254_load calls; program counters' divide ratios. */
-       i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 0, devpriv->divisor0, 2);       /* Counter 0, divisor0, square wave (8254 mode 2). */
-       i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 1, devpriv->divisor1, 2);       /* Counter 1, divisor1, square wave (8254 mode 2). */
+       i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 0, devpriv->divisor0, 3);       /* Counter 0, divisor0, square wave (8254 mode 3). */
+       i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 1, devpriv->divisor1, 3);       /* Counter 1, divisor1, square wave (8254 mode 3). */
 
        /* PCI 230 specific - ties up counter clk inputs with clk sources */
        outb(PCI230_ZCLK_CT0 | PCI230_ZCLK_SRC_10MHZ, devpriv->pci_iobase + PCI230_ZCLK_SCE);   /* Program counter 0's input clock source. */
        outb(PCI230_ZCLK_CT1 | PCI230_ZCLK_SRC_OUTNM1, devpriv->pci_iobase + PCI230_ZCLK_SCE);  /* Program counter 1's input clock source. */
 
-       return *ns;
+       return;
+}
+
+static void pci230_cancel_ct1(comedi_device *dev)
+{
+       devpriv->divisor0 = 0;
+       devpriv->divisor1 = 0;
+       i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 0, devpriv->divisor0, 0);       /* Counter 0, divisor0, 8254 mode 0. */
+       i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 1, devpriv->divisor1, 0);       /* Counter 1, divisor1, 8254 mode 0. */
 }
 
 /* 
  *  Set ZCLK_CT2 to square wave mode with period of ns.
+ *  Default clk source for external freq. generator (COMEDI_SUBD_TIMER).
  */
-static int pci230_z2_ct2(comedi_device *dev, unsigned int *ns,int round)
+static void pci230_z2_ct2(comedi_device *dev, unsigned int *ns,int round)
 {
        /* For two cascaded counter/timers, calculate the divide ratios required to give a square wave of period ns. */
        i8253_cascade_ns_to_timer_2div(PCI230_TIMEBASE_10MHZ, &devpriv->divisor1, &devpriv->divisor2, ns, TRIG_ROUND_MASK);
-       printk("comedi%d: amplc_pci230::pci230_z2_ct2() divisor1 %d divisor2 %d ns %d\n",dev->minor, devpriv->divisor1, devpriv->divisor2, *ns);
 
     /* Generic i8254_load calls; program counters' divide ratios. */
-       i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 1, devpriv->divisor1, 2);       /* Counter 1, divisor1, square wave (8254 mode 2). */
-       i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 2, devpriv->divisor2, 2);       /* Counter 2, divisor2, square wave (8254 mode 2). */
+       i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 1, devpriv->divisor1, 3);       /* Counter 1, divisor1, square wave (8254 mode 3). */
+       i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 2, devpriv->divisor2, 3);       /* Counter 2, divisor2, square wave (8254 mode 3). */
 
        /* PCI 230 specific - ties up counter clk inputs with clk sources */
        outb(PCI230_ZCLK_CT1 | PCI230_ZCLK_SRC_10MHZ, devpriv->pci_iobase + PCI230_ZCLK_SCE);   /* Program counter 1's input clock source. */
        outb(PCI230_ZCLK_CT2 | PCI230_ZCLK_SRC_OUTNM1, devpriv->pci_iobase + PCI230_ZCLK_SCE);  /* Program counter 2's input clock source. */
 
-       return *ns;
-}
-
-static int pci230_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
-{
-       int i;
-       int chan, range;
-       unsigned int d;
-       unsigned int bipolar;
-       
-       /* Unpack channel and range. */
-       chan = CR_CHAN(insn->chanspec);
-       range = CR_RANGE(insn->chanspec);
-
-       /* Set range - see analogue output range table; 0 => unipolar 10V, 1 => bipolar +/-10V range scale */
-       bipolar = PCI230_TEST_BIT(range, PCI230_DAC_BIP_BIT);
-       outw(range, dev->iobase + PCI230_DACCON);
-
-       /* Writing a list of values to an AO channel is probably not
-        * very useful, but that's how the interface is defined. */
-       for(i=0;i<insn->n;i++){
-               d = data[i];
-
-               /* Store the value to be written to the DAC in our pci230_private struct before mangling it. */
-               devpriv->ao_readback[chan] = d;
-
-               /* If a bipolar range was specified, mangle it (straight binary->twos complement). */ 
-               if (bipolar) {
-                       d ^= 1<<(thisboard->ai_bits-1);
-               }
-               
-               /* PCI230 is 12 bit - stored in upper bits of 16 bit register (lower four bits reserved for expansion). */
-               d = d<<4;
-               
-               /* Write data. */
-               outw(d, dev->iobase + (((chan) == 0) ? PCI230_DACOUT1 : PCI230_DACOUT2));
-               printk("comedi%d: amplc_pci230::pci230_ao_rinsn() wrote PCI230_DACOUTx 0x%04x\n",dev->minor, d);
-
-#if 0
-               /* XXX screw the user.  Only do this if the board gets upset if you don't */
-               /* If we're writing more than one sample, wait for output to settle between successive writes */
-               if (insn->n > 1) {
-               udelay(PCI230_DAC_SETTLE);
-               }
-#endif
-       }
-
-       /* return the number of samples read/written */
-       return i;
+       return;
 }
 
-/* AO subdevices should have a read insn as well as a write insn.
- * Usually this means copying a value stored in devpriv. */
-static int pci230_ao_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+static void pci230_cancel_ct2(comedi_device *dev)
 {
-       int i;
-       int chan = CR_CHAN(insn->chanspec);
-
-       for(i=0;i<insn->n;i++)
-               data[i] = devpriv->ao_readback[chan];
-
-       return i;
+       devpriv->divisor1 = 0;
+       devpriv->divisor2 = 0;
+       i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 1, devpriv->divisor1, 0);       /* Counter 1, divisor1, 8254 mode 0. */
+       i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 2, devpriv->divisor2, 0);       /* Counter 2, divisor2, 8254 mode 0. */
 }
 
 /* Interrupt handler */
 static void pci230_interrupt(int irq, void *d, struct pt_regs *regs)
 {
+       int status_int;
        comedi_device *dev = (comedi_device*) d;
-       comedi_subdevice *s = dev->read_subdev;
-       comedi_async *async;
-       int status_int, status_fifo;
-       int i;
-       unsigned int data;
-
-       /* 
-        * Check to see whether this driver has been configured yet.
-        * This must be done first, because if the board is asserting
-        * interrupts and our driver's attach fn. hasn't been called
-        * we can't even talk to the board (base addresses for native
-        * IO regions aren't set...)
-        */
-
-       /* XXX doesn't make sense, since interrupt is allocated in the
-        * _attach function, _after_ the IO regions are allocated */
-
-       /* XXX I (Frank Hess) do this in drivers to prevent a null
-        * dereference when the handler tries to use async.  This
-        * probably won't help too much here as it is though, since
-        * it doesn't clear the interrupt before returning and so
-        * will lock up the (single cpu) computer. */
-
-       if(dev->attached == 0) {
-               comedi_error(dev, "premature interrupt");
-               return;
-       }
-
-       printk("comedi%d: amplc_pci230::pci230_interrupt executing interrupt handler\n", dev->minor);
+       comedi_subdevice *s;
 
        /* Read interrupt status/enable register. */
        status_int = inb(devpriv->pci_iobase + PCI230_INT_SCE);
-       printk("comedi%d: amplc_pci230::pci230_interrupt read PCI230_INT_SCE",dev->minor); printb(status_int);
 
-       /* Disable board interrupts. */
-       outb(PCI230_INT_DISABLE, devpriv->pci_iobase + PCI230_INT_SCE);
-       printk("comedi%d: amplc_pci230::pci230_interrupt wrote PCI230_INT_SCE",dev->minor); printb(PCI230_INT_DISABLE);
+       /* Disable all of board's interrupts.
+        * (Only those interrrupts that need re-enabling, are, later in the handler).  */
+       devpriv->ier = PCI230_INT_DISABLE; 
+       outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE);
 
        /* 
-        * Check to see whether this board emitted the interrupt, and if it did,  
-        * whether it is for a supported function.
+        * Check the source of interrupt and handle it.
+        * The PCI230 can cope with concurrent ADC, DAC, PPI C0 and C3 interrupts.
+        * However, at present (Comedi-0.7.60) does not allow concurrent
+        * execution of commands, instructions or a mixture of the two.
         */
        if (status_int == PCI230_INT_DISABLE) {
                printk("comedi%d: amplc_pci230::pci230_interrupt spurious interrupt",dev->minor);
-               return;
        }
-       else if (status_int & PCI230_INT_PPI_C0) {
-               printk("comedi%d: amplc_pci230::pci230_interrupt PPI C0 interrupt, not implemented",dev->minor);
-               return;
+       
+       if (status_int & PCI230_INT_ZCLK_CT1) {
+               s = dev->write_subdev;
+               s->async->events = 0;
+               pci230_handle_ao(dev, s);
+               comedi_event(dev, s, s->async->events);
+               s->async->events = 0;
+       }
+
+       if (status_int & PCI230_INT_ADC) {
+               s = dev->read_subdev;
+               s->async->events = 0;
+               pci230_handle_ai(dev, s);
+               comedi_event(dev, s, s->async->events);
+               s->async->events = 0;
        }
-       else if (status_int & PCI230_INT_PPI_C3) {
-               printk("comedi%d: amplc_pci230::pci230_interrupt PPI C3 interrupt, not implemented",dev->minor);
-               return;
+
+       return;
+}
+
+static void pci230_handle_ao(comedi_device *dev, comedi_subdevice *s) {
+       sampl_t data;
+       int i, ret;
+       comedi_async *async = s->async;
+       comedi_cmd *cmd = &async->cmd;
+
+       for (i = 0; i < cmd->chanlist_len; i++) {
+               /* Read sample from Comedi's circular buffer. */
+               ret = comedi_buf_get(s->async, &data);
+               if(ret < 0) {
+                       comedi_error(dev, "buffer underrun");
+                       return;                                                                         // XXX does comedi_buf_get set s->async->events with appropriate flags in this instance?
+               }
+               /* Write value to DAC. */
+               pci230_ao_write(dev, data, cmd->chanlist[i]);
+
+               if(async->cmd.stop_src == TRIG_COUNT) {
+                       if(devpriv->ao_count > 0) devpriv->ao_count--;
+                       if(devpriv->ao_count == 0) break;
+               }
+       }
+
+       if(devpriv->ao_count == 0 && devpriv->ao_stop == 0) {
+               /* End of DAC. */
+               async->events |= COMEDI_CB_EOA;
+               pci230_ao_cancel(dev, s);
        }
-       else if (status_int & PCI230_INT_ZCLK_CT0) {
-               printk("comedi%d: amplc_pci230::pci230_interrupt ZCLK CT0 interrupt, not implemented",dev->minor);
-               return;
+       else {
+               /* More samples required, tell Comedi to block. */
+               async->events |= COMEDI_CB_BLOCK;
+               /* Enable DAC (conversion complete) interrupt (and leave any other enabled interrupts as they are). */
+               devpriv->ier |= PCI230_INT_ZCLK_CT1;
+               outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE);
        }
+       return;
+}
 
+static void pci230_handle_ai(comedi_device *dev, comedi_subdevice *s) {
+       int error = 0;
+       int status_fifo;
+       
        /* Read FIFO state. */
        status_fifo = inw(dev->iobase + PCI230_ADCCON);
-       printk("comedi%d: amplc_pci230::pci230_interrupt read PCI230_ADCCON",dev->minor); printb(status_fifo);
        
-       /* Check to see whether FIFO enabled. */
-       if (!(status_fifo & PCI230_ADC_FIFO_EN)) {
-               printk("comedi%d: amplc_pci230::pci230_interrupt FIFO not enabled",dev->minor);
-               return;
-       }
-
-       /* Ok., only reason we're here is because;
-        * - Board has raised a conversion complete interrupt.
-        * - FIFO is enabled.
-        */
-
-       async = s->async;
-       async->events = 0;
-
        if (status_fifo & PCI230_ADC_FIFO_FULL) {
-               /* 
-                * Report error and return - if we didn´t do this, but instead handled it in a similar
-                * manner to the half full FIFO case, FIFO overruns would go unnoticed by the caller.
-                */
-               printk("comedi%d: amplc_pci230::pci230_interrupt FIFO overflow",dev->minor);
-
-               /* Cancel sampled conversion. */
-               pci230__cancel(dev, s); 
+               /* Report error otherwise FIFO overruns will go unnoticed by the caller. */
                comedi_error(dev, "FIFO overrun");
-               async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
-               comedi_event(dev, s, async->events);
-               async->events = 0;
-               return;
+               error++;
        }
        else if (status_fifo & PCI230_ADC_FIFO_HALF) {
-               /* FIFO is at least half full */
-               printk("comedi%d: amplc_pci230::pci230_interrupt FIFO half full\n",dev->minor);
-               for (i = 0; i < 2048; i++) {
-                       /* Read sample. */
-                       data = inw(dev->iobase + PCI230_ADCDATA);
-
-                       /* PCI230 is 12 bit - stored in upper bits of 16 bit register (lower four bits reserved for expansion). */
-                       data = data>>4;
-
-                       /* If a bipolar range was specified, mangle it (twos complement->straight binary). */ 
-                       if (devpriv->bipolar) {
-                               data ^= 1<<(thisboard->ai_bits-1);
-                       }
-
-                       /* Store in Comedi's circular buffer. */
-                       comedi_buf_put(async, (sampl_t) data);
-
-                       if(async->cmd.stop_src == TRIG_COUNT)
-                       {
-                               if(--devpriv->count == 0) {
-                                       /* Acquisition complete. */
-                                       printk("comedi%d: amplc_pci230::pci230_interrupt acquisition complete\n",dev->minor);
-                                       pci230__cancel(dev, s);
-                                       async->events |= COMEDI_CB_EOA;
-                                       break;
-                               }
-                       }
-               }
-               /* More samples required, tell Comedi to block, and enable boards interrupt (don´t change trigger level). */
-               async->events |= COMEDI_CB_BLOCK;
-               outb(PCI230_INT_ADC_DAC, devpriv->pci_iobase + PCI230_INT_SCE);
-               printk("comedi%d: amplc_pci230::pci230_interrupt wrote PCI230_INT_SCE\n",dev->minor); printb(PCI230_INT_ADC_DAC);
+               /* FIFO is at least half full. */
+               pci230_handle_fifo_half_full(dev, s);
        }
        else if (status_fifo & PCI230_ADC_FIFO_EMPTY) {
-               /* FIFO empty but we got an interrupt */
-               printk("comedi%d: amplc_pci230::pci230_interrupt FIFO empty - spurious interrupt\n",dev->minor);
+               /* FIFO empty but we got an interrupt. */
+               printk("comedi%d: amplc_pci230::pci230_handle_ai FIFO empty - spurious interrupt\n",dev->minor);
        }
        else {
                /* FIFO is less than half full, but not empty. */
-               /* We should only ever get here if no. samples to read < half fifo size. */
-               printk("comedi%d: amplc_pci230::pci230_interrupt FIFO less than half full, but not empty\n",dev->minor);
-               while (devpriv->count != 0) {
-                       if (inw(dev->iobase + PCI230_ADCCON) & PCI230_ADC_FIFO_EMPTY) {
-                               /* The FIFO is empty, block. */
-                               printk("comedi%d: amplc_pci230::pci230_interrupt FIFO now empty, want %d samples\n",dev->minor, devpriv->count);
-
-                               /* More samples required, tell Comedi to block, and enable boards interrupt (don´t change trigger level). */
-                               outb(PCI230_INT_ADC_DAC, devpriv->pci_iobase + PCI230_INT_SCE);
-                               printk("comedi%d: amplc_pci230::pci230_interrupt wrote PCI230_INT_SCE\n",dev->minor); printb(PCI230_INT_ADC_DAC);
-                               async->events |= COMEDI_CB_BLOCK;
-                               comedi_event(dev, s, async->events);
-                               return;
-                       }
-                       /* There are sample(s) to read from FIFO, read one. */
-                       data = inw(dev->iobase + PCI230_ADCDATA);
+               pci230_handle_fifo_not_empty(dev, s);
+       }
+
+       if (error) {
+               /* Cancel sampled conversion. */
+               s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+               pci230_ai_cancel(dev, s);       
+       }
+       if(devpriv->ai_count == 0 && devpriv->ai_stop == 0) {
+               /* Acquisition complete. */
+               s->async->events |= COMEDI_CB_EOA;
+               pci230_ai_cancel(dev, s);                       /* disable hardware conversions */
+       }
+       else {
+               /* More samples required, tell Comedi to block. */
+               s->async->events |= COMEDI_CB_BLOCK;
+               /* Enable ADC (conversion complete) interrupt (and leave any other enabled interrupts as they are). */
+               devpriv->ier |= PCI230_INT_ADC;
+               outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE);
+       }
+       return;
+}
 
-                       /* PCI230 is 12 bit - stored in upper bits of 16 bit register (lower four bits reserved for expansion). */
-                       data = data>>4;
+static void pci230_handle_fifo_half_full(comedi_device *dev, comedi_subdevice *s) {
+       int i;
 
-                       /* If a bipolar range was specified, mangle it (twos complement->straight binary). */ 
-                       if (devpriv->bipolar) {
-                               data ^= 1<<(thisboard->ai_bits-1);
-                       }
+       for (i = 0; i < 2048; i++) {
+               /* Read sample and store in Comedi's circular buffer. */
+               comedi_buf_put(s->async, pci230_ai_read(dev));
 
-                       /* Store in Comedi's circular buffer. */
-                       comedi_buf_put(async, (sampl_t) data);
+               if(s->async->cmd.stop_src == TRIG_COUNT)
+               {
+                       if(--devpriv->ai_count == 0) {
+                               /* Acquisition complete. */
+                               return;
+                       }
+               }
+       }
+       /* More samples required. */
+       return;
+}
 
-                       if(devpriv->count > 0) devpriv->count--;
+static void pci230_handle_fifo_not_empty(comedi_device *dev, comedi_subdevice *s) {
+       while (devpriv->ai_count != 0) {
+               if (inw(dev->iobase + PCI230_ADCCON) & PCI230_ADC_FIFO_EMPTY) {
+                       /* The FIFO is empty, block. */
+                       return;
                }
+               /* There are sample(s) to read from FIFO, read one and store in Comedi's circular buffer. */
+               comedi_buf_put(s->async, pci230_ai_read(dev));
 
-               /* Acquisition complete. */
-               printk("comedi%d: amplc_pci230::pci230_interrupt acquisition complete\n",dev->minor);
-               pci230__cancel(dev, s);
-               async->events |= COMEDI_CB_EOA;
+               if(devpriv->ai_count > 0) devpriv->ai_count--;
        }
+       /* Acquisition complete. */
+       return;
+}
 
-       comedi_event(dev, s, async->events);
-       async->events = 0;
 
-       return;
+static int pci230_ao_cancel(comedi_device *dev, comedi_subdevice *s) {
+       devpriv->ao_count = 0;
+       devpriv->ao_stop = 0;
+
+       /* Stop counter/timers. */
+       pci230_cancel_ct1(dev);
+
+       /* Disable DAC interrupt. */
+       devpriv->ier &= ~PCI230_INT_ZCLK_CT1;
+       outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE);
+
+       return 0;
 }
 
-static int pci230__cancel(comedi_device *dev, comedi_subdevice *s)
-{
-       /* Disable interrupts. */
-       outb(PCI230_INT_DISABLE, devpriv->pci_iobase + PCI230_INT_SCE);
-       printk("comedi%d: amplc_pci230::pci230__cancel disabled interrupts\n",dev->minor);
+static int pci230_ai_cancel(comedi_device *dev, comedi_subdevice *s) {
+       devpriv->ai_count = 0;
+       devpriv->ai_stop = 0;
+
+       /* Stop counter/timers. */
+       pci230_cancel_ct0(dev);
+
+       /* Disable ADC interrupt. */
+       devpriv->ier &= ~PCI230_INT_ADC;
+       outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE);
 
        /* Reset FIFO and set start conversion source to none. */
-       outw_p(PCI230_ADC_FIFO_RESET | PCI230_ADC_TRIG_NONE, dev->iobase + PCI230_ADCCON);
-       printk("comedi%d: amplc_pci230::pci230__cancel reset FIFO and set conv src to none\n", dev->minor);
+       outw(PCI230_ADC_FIFO_RESET | PCI230_ADC_TRIG_NONE, dev->iobase + PCI230_ADCCON);
 
        /* Clear channel scan list. */
-       outw_p(0x0000, dev->iobase + PCI230_ADCEN);
-       printk("comedi%d: amplc_pci230::pci230__cancel cleared scan list\n", dev->minor);
+       outw(0x0000, dev->iobase + PCI230_ADCEN);
 
        /* Clear channel gains. */
-       outw_p(0x0000, dev->iobase + PCI230_ADCG);
-       printk("comedi%d: amplc_pci230::pci230__cancel cleared channel gains\n", dev->minor);
+       outw(0x0000, dev->iobase + PCI230_ADCG);
 
        return 0;
 }