added support for poll(), and made a bunch of other changes for 'no good reason',
authorFrank Mori Hess <fmhess@speakeasy.net>
Mon, 20 Aug 2001 05:01:47 +0000 (05:01 +0000)
committerFrank Mori Hess <fmhess@speakeasy.net>
Mon, 20 Aug 2001 05:01:47 +0000 (05:01 +0000)
although it's a bit more careful about flushing out all the data with
stop_src TRIG_EXT and dma now.  Also, disabled non-conforming interpretation
of stop_arg for TRIG_EXT.

comedi/drivers/das1800.c

index 3fe023022789da400d017b68c19a2ce12099f72a..e023d8cec9f8f92ef0e8640093d15cd6cab9935c 100644 (file)
@@ -62,16 +62,13 @@ scan_begin_src triggers TRIG_TIMER and TRIG_EXT use the card's
 (convert_arg <= 64000).  This limitation does not apply if scan_begin_src
 is TRIG_FOLLOW.
 
-If stop_src is TRIG_EXT then stop_src_arg is the number of conversions to take
-after receiving the external trigger before stopping conversions.  It must be
-at least 1 and no more than 0x10000 == 65536
-
 NOTES:
 Only the DAS-1801ST has been tested by me.
 Unipolar and bipolar ranges cannot be mixed in the channel/gain list.
 
 TODO:
-       Add support for analog out on 'ao' cards.
+       Make it automatically allocate irq and dma channels if they are not specified
+       Add support for analog out on 'ao' cards
 */
 
 #include <linux/kernel.h>
@@ -94,8 +91,8 @@ TODO:
 #define DAS1800_SIZE           16      //uses 16 io addresses
 #define HALF_FIFO              512     // 1024 sample fifo
 #define TIMER_BASE             200     // 5 Mhz master clock
-#define MIN_DMA_TRANSFER       1024    // minimum dma transfer size I want to use
 #define UNIPOLAR               0x4     // bit that determines whether input range is uni/bipolar
+#define DMA_BUF_SIZE           0x1ff00 // size in bytes of dma buffers
 
 /* Registers for the das1800 */
 #define DAS1800_FIFO            0x0
@@ -160,13 +157,16 @@ enum{
 
 static int das1800_attach(comedi_device *dev, comedi_devconfig *it);
 static int das1800_detach(comedi_device *dev);
-int das1800_probe(comedi_device *dev);
+static int das1800_probe(comedi_device *dev);
 static int das1800_cancel(comedi_device *dev, comedi_subdevice *s);
 static void das1800_interrupt(int irq, void *d, struct pt_regs *regs);
+static int das1800_ai_poll(comedi_device *dev,comedi_subdevice *s);
+static void das1800_ai_handler(comedi_device *dev, unsigned int status);
 static void das1800_handle_dma(comedi_device *dev, comedi_subdevice *s);
+static void das1800_flush_dma(comedi_device *dev, comedi_subdevice *s);
+static void das1800_flush_dma_channel(comedi_device *dev, comedi_subdevice *s, unsigned int channel, u16 *buffer);
 static void das1800_handle_fifo_half_full(comedi_device *dev, comedi_subdevice *s);
 static void das1800_handle_fifo_not_empty(comedi_device *dev, comedi_subdevice *s);
-void disable_das1800(comedi_device *dev);
 static int das1800_ai_do_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd);
 static int das1800_ai_do_cmd(comedi_device *dev, comedi_subdevice *s);
 static int das1800_ai_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
@@ -431,7 +431,6 @@ das1800_board das1800_boards[] =
 
 typedef struct{
        volatile unsigned int count;  /* number of data points left to be taken */
-       volatile int forever;  /* flag indicating whether we should take data forever */
        unsigned int divisor1;  /* value to load into board's counter 1 for timed conversions */
        unsigned int divisor2;  /* value to load into board's counter 2 for timed conversions */
        int do_bits;    /* digital output bits */
@@ -442,11 +441,10 @@ typedef struct{
        unsigned int dma0;      /* dma channels used */
        unsigned int dma1;
        volatile unsigned int dma_current;      /* dma channel currently in use */
-       short *dma_buf0;        /* pointers to dma buffers */
-       short *dma_buf1;
-       volatile short *dma_current_buf;        /* pointer to dma buffer currently being used */
-       unsigned int dma_buf_max_size;  /* allocated size in bytes of dma buffers */
-       unsigned int dma_buf_size;      /* size of buffers currently used, depends on sampling frequency */
+       u16 *dma_buf0;  /* pointers to dma buffers */
+       u16 *dma_buf1;
+       u16 *dma_current_buf;   /* pointer to dma buffer currently being used */
+       unsigned int dma_transfer_size; /* size of transfer currently used, in bytes */
        int iobase2;    /* secondary io address used for analog out on 'ao' boards */
        short ao_update_bits; /* remembers the last write to the 'update' dac */
 }das1800_private;
@@ -645,14 +643,16 @@ static int das1800_attach(comedi_device *dev, comedi_devconfig *it)
                                }
                                devpriv->dma1 = dma1;
                        }
-                       devpriv->dma_buf_max_size = 0x1ff00;
-                       devpriv->dma_buf0 = kmalloc(devpriv->dma_buf_max_size, GFP_KERNEL | GFP_DMA);
+                       devpriv->dma_buf0 = kmalloc(DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA);
                        if(devpriv->dma_buf0 == NULL)
                                return -ENOMEM;
                        devpriv->dma_current_buf = devpriv->dma_buf0;
-                       devpriv->dma_buf1 = kmalloc(devpriv->dma_buf_max_size, GFP_KERNEL | GFP_DMA);
-                       if(devpriv->dma_buf1 == NULL)
-                               return -ENOMEM;
+                       if(dma1)
+                       {
+                               devpriv->dma_buf1 = kmalloc(DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA);
+                               if(devpriv->dma_buf1 == NULL)
+                                       return -ENOMEM;
+                       }
                        flags = claim_dma_lock();
                        disable_dma(devpriv->dma0);
                        set_dma_mode(devpriv->dma0, DMA_MODE_READ);
@@ -683,6 +683,7 @@ static int das1800_attach(comedi_device *dev, comedi_devconfig *it)
        s->do_cmd = das1800_ai_do_cmd;
        s->do_cmdtest = das1800_ai_do_cmdtest;
        s->insn_read = das1800_ai_rinsn;
+       s->poll = das1800_ai_poll;
        s->cancel = das1800_cancel;
 
        /* analog out */
@@ -719,7 +720,7 @@ static int das1800_attach(comedi_device *dev, comedi_devconfig *it)
        s->range_table = &range_digital;
        s->insn_bits = das1800_do_wbits;
 
-       disable_das1800(dev);
+       das1800_cancel(dev, dev->read_subdev);
 
        // initialize digital out channels
        outb(devpriv->do_bits, dev->iobase + DAS1800_DIGITAL);
@@ -773,7 +774,6 @@ int das1800_probe(comedi_device *dev)
 
        switch(id)
        {
-               // das-1800st-da
                case 0x3:
                        if(board == das1801st_da || board == das1802st_da ||
                                board == das1701st_da || board == das1702st_da)
@@ -784,7 +784,6 @@ int das1800_probe(comedi_device *dev)
                        printk(" Board model (probed, not recommended): das-1800st-da series\n");
                        return das1801st;
                        break;
-               // das-1800hr-da
                case 0x4:
                        if(board == das1802hr_da || board == das1702hr_da)
                        {
@@ -833,58 +832,79 @@ int das1800_probe(comedi_device *dev)
                        return das1801hc;
                        break;
                default :
-                       printk(" Board model: probe returned 0x%x (unknown)\n", id);
+                       printk(" Board model: probe returned 0x%x (unknown, please report)\n", id);
                        return board;
                        break;
        }
        return -1;
 }
 
-static int das1800_cancel(comedi_device *dev, comedi_subdevice *s)
+static int das1800_ai_poll(comedi_device *dev,comedi_subdevice *s)
 {
-       devpriv->forever = 0;
-       devpriv->count = 0;
-       disable_das1800(dev);
-       return 0;
+       unsigned long flags;
+       unsigned int status;
+
+       // prevent race with interrupt handler
+       comedi_spin_lock_irqsave(&dev->spinlock, flags);
+       status = inb(dev->iobase + DAS1800_STATUS);
+       das1800_ai_handler(dev, status);
+       comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+       return s->async->buf_int_count - s->async->buf_user_count;
 }
 
 static void das1800_interrupt(int irq, void *d, struct pt_regs *regs)
 {
-       int status;
-       unsigned long irq_flags;
        comedi_device *dev = d;
-       comedi_subdevice *s = dev->subdevices + 0;      /* analog input subdevice */
-       comedi_async *async;
+       int status;
+       unsigned long flags;
 
+       /* Prevent race with das1800_ai_poll() on multi processor systems.
+        * Also protects indirect addressing in das1800_ai_handler */
+       comedi_spin_lock_irqsave(&dev->spinlock, flags);
        status = inb(dev->iobase + DAS1800_STATUS);
+
+       if(dev->attached == 0)
+       {
+               comedi_error(dev, "premature interrupt");
+               return;
+       }
        /* if interrupt was not caused by das-1800 */
-       if(!(status & INT) || !(dev->attached))
+       if(!(status & INT))
        {
+               comedi_error(dev, "spurious interrupt");
                return;
        }
-       async = s->async;
-       comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
+       /* clear interrupt */
+       outb(FNE, dev->iobase + DAS1800_STATUS);
+
+       das1800_ai_handler(dev, status);
+
+       comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+}
+
+// the guts of the interrupt handler, that is shared with das1800_ai_poll
+static void das1800_ai_handler(comedi_device *dev, unsigned int status)
+{
+       comedi_subdevice *s = dev->subdevices + 0;      /* analog input subdevice */
+       comedi_async *async = s->async;
+       comedi_cmd *cmd = &async->cmd;
+
+       async->events = 0;
+       // select adc for base address + 0
        outb(ADC, dev->iobase + DAS1800_SELECT);
-       // dma buffer full or about-triggering (stop_src == TRIG_EXT)
+       // dma buffer full
        if(devpriv->irq_dma_bits & DMA_ENABLED)
        {
-               if(status & (DMATC | CT0TC))
-               {
-                       das1800_handle_dma(dev, s);
-               }
-       }
-       // if fifo half full, and there has not an external stop trigger
-       else if((status & FHF) && !(status & CT0TC))
-       {
+               das1800_handle_dma(dev, s);
+       }else if(status & FHF)
+       {       // if fifo half full
                das1800_handle_fifo_half_full(dev, s);
-       } else if(status & FNE)
-       {
+       }else if(status & FNE)
+       {       // if fifo not empty
                das1800_handle_fifo_not_empty(dev, s);
        }
-       /* clear interrupt */
-       outb(FNE, dev->iobase + DAS1800_STATUS);
 
-       comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
        async->events |= COMEDI_CB_BLOCK;
        /* if the card's fifo has overflowed */
        if(status & OVF)
@@ -893,20 +913,27 @@ static void das1800_interrupt(int irq, void *d, struct pt_regs *regs)
                das1800_cancel(dev, s);
                async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
                comedi_event(dev, s, async->events);
-               async->events = 0;
                return;
        }
-       // if stop_src TRIG_EXT has occurred
-       if(status & CT0TC) devpriv->forever = 0;
-       /* stop taking data if appropriate */
-       if(devpriv->count == 0 && devpriv->forever == 0)
+
+       // stop taking data if appropriate
+       /* stop_src TRIG_EXT */
+       if(status & CT0TC)
        {
-               disable_das1800(dev);           /* disable hardware conversions */
+               // make sure we get all remaining data from board before quitting
+               if(devpriv->irq_dma_bits & DMA_ENABLED)
+                       das1800_flush_dma(dev, s);
+               else
+                       das1800_handle_fifo_not_empty(dev, s);
+               das1800_cancel(dev, s);         /* disable hardware conversions */
+               async->events |= COMEDI_CB_EOA;
+       }else if(cmd->stop_src == TRIG_COUNT && devpriv->count == 0)
+       { // stop_src TRIG_COUNT
+               das1800_cancel(dev, s);         /* disable hardware conversions */
                async->events |= COMEDI_CB_EOA;
        }
 
        comedi_event(dev, s, async->events);
-       async->events = 0;
 
        return;
 }
@@ -914,13 +941,12 @@ static void das1800_interrupt(int irq, void *d, struct pt_regs *regs)
 static void das1800_handle_dma(comedi_device *dev, comedi_subdevice *s)
 {
        unsigned long flags;
-       unsigned long numPoints, leftover;
-       long maxPoints, residue;
-       short dpnt;
-       volatile short *buffer;
+       unsigned int numPoints;
        int unipolar;
        int i;
        const int dual_dma = devpriv->irq_dma_bits & DMA_DUAL;
+       const int bytes_per_sample = 2;
+       comedi_cmd *cmd = &s->async->cmd;
 
        flags = claim_dma_lock();
        disable_dma(devpriv->dma_current);
@@ -929,99 +955,114 @@ static void das1800_handle_dma(comedi_device *dev, comedi_subdevice *s)
        clear_dma_ff(devpriv->dma_current);
 
        // figure out how many points to read
-       maxPoints = devpriv->dma_buf_size  / sizeof(short);
-       /* residue is the number of points left to be done on the dma
-        * transfer.  It should always be zero at this point unless
-        * the stop_src is set to external triggering.
-        */
-       residue = get_dma_residue(devpriv->dma_current) / sizeof(short);
-       numPoints = maxPoints - residue;
-       if(devpriv->forever == 0 && devpriv->count < numPoints)
+       numPoints = (devpriv->dma_transfer_size - get_dma_residue(devpriv->dma_current)) /
+               bytes_per_sample;
+       /* if we only need some of the points */
+       if(cmd->stop_src == TRIG_COUNT && devpriv->count < numPoints)
                numPoints = devpriv->count;
 
-       // figure out how many points will be stored next time
-       leftover = 0;
-       if(devpriv->forever)
-               leftover = maxPoints;
-       else
+       /* see if card is using a unipolar or bipolar range so we can munge data correctly */
+       unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB;
+
+       // read data from dma buffer
+       for( i = 0; i < numPoints; i++)
        {
-               if(dual_dma)
+               /* convert to unsigned type if we are in a bipolar mode */
+               if(!unipolar);
+                       devpriv->dma_current_buf[i] += 1 << (thisboard->resolution - 1);
+               comedi_buf_put(s->async, devpriv->dma_current_buf[i]);
+               if(s->async->cmd.stop_src == TRIG_COUNT)
+                       devpriv->count--;
+       }
+
+       // re-enable  dma channel
+       set_dma_addr(devpriv->dma_current, virt_to_bus(devpriv->dma_current_buf));
+       set_dma_count(devpriv->dma_current, devpriv->dma_transfer_size);
+       enable_dma(devpriv->dma_current);
+       release_dma_lock(flags);
+
+       if(dual_dma)
+       {
+               // read data from the other channel next time
+               if(devpriv->dma_current == devpriv->dma0)
                {
-                       if(devpriv->count > 2 * maxPoints)
-                               leftover = devpriv->count - 2 * maxPoints;
-               }else
+                       devpriv->dma_current = devpriv->dma1;
+                       devpriv->dma_current_buf = devpriv->dma_buf1;
+               }
+               else
                {
-                       if(devpriv->count > maxPoints)
-                               leftover = devpriv->count - maxPoints;
+                       devpriv->dma_current = devpriv->dma0;
+                       devpriv->dma_current_buf = devpriv->dma_buf0;
                }
-               if(leftover > maxPoints)
-                       leftover = maxPoints;
        }
-       /* there should only be a residue if collection was stopped by having
-        * the stop_src set to an external trigger, in which case there
-        * will be no more data
-        */
-       if(residue)
-               leftover = 0;
 
-       // remember buffer before dma_current_buf gets changed
-       buffer = devpriv->dma_current_buf;
+       return;
+}
 
-       // read data from the other buffer next time
-       if(devpriv->dma_current_buf == devpriv->dma_buf0)
-       {
-               devpriv->dma_current_buf = devpriv->dma_buf1;
-       }
-       else
-       {
-               devpriv->dma_current_buf = devpriv->dma_buf0;
-       }
+// utility function used by das1800_flush_dma()
+static void das1800_flush_dma_channel(comedi_device *dev, comedi_subdevice *s, unsigned int channel, u16 *buffer)
+{
+       unsigned long flags;
+       unsigned int numPoints;
+       int unipolar;
+       int i;
+       const int bytes_per_sample = 2;
 
-       // re-enable  dma (single dma)
-       if((dual_dma == 0) && leftover)
-       {
-               set_dma_addr(devpriv->dma_current, virt_to_bus(devpriv->dma_current_buf));
-               set_dma_count(devpriv->dma_current, leftover * sizeof(short));
-               enable_dma(devpriv->dma_current);
-               release_dma_lock(flags);
-       }
+       flags = claim_dma_lock();
+       disable_dma(channel);
+
+       /* clear flip-flop to make sure 2-byte registers
+        * get set correctly */
+       clear_dma_ff(channel);
 
-       /* see if card is using a unipolar or bipolar range */
+       // figure out how many points to read
+       numPoints = (devpriv->dma_transfer_size - get_dma_residue(channel)) /
+               bytes_per_sample;
+
+       /* see if card is using a unipolar or bipolar range so we can munge data correctly */
        unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB;
 
        // read data from dma buffer
        for( i = 0; i < numPoints; i++)
        {
-               /* write data point to comedi buffer */
-               dpnt = buffer[i];
                /* convert to unsigned type if we are in a bipolar mode */
                if(!unipolar);
-                       dpnt += 1 << (thisboard->resolution - 1);
-               comedi_buf_put(s->async, dpnt);
-               if(devpriv->count > 0) devpriv->count--;
+                       buffer[i] += 1 << (thisboard->resolution - 1);
+               comedi_buf_put(s->async, buffer[i]);
        }
 
-       // re-enable  dma (dual dma)
+       release_dma_lock(flags);
+
+       return;
+}
+
+/* flushes remaining data from board when external trigger has stopped aquisition
+ * and we are using dma transfers */
+static void das1800_flush_dma(comedi_device *dev, comedi_subdevice *s)
+{
+       const int dual_dma = devpriv->irq_dma_bits & DMA_DUAL;
+
+       das1800_flush_dma_channel(dev, s, devpriv->dma_current, devpriv->dma_current_buf);
+
        if(dual_dma)
        {
-               if(leftover)
-               {
-                       set_dma_addr(devpriv->dma_current, virt_to_bus(buffer));
-                       set_dma_count(devpriv->dma_current, leftover * sizeof(short));
-                       enable_dma(devpriv->dma_current);
-                       release_dma_lock(flags);
-               }
-               // read data from the other channel next time
+               // switch to other channel and flush it
                if(devpriv->dma_current == devpriv->dma0)
                {
                        devpriv->dma_current = devpriv->dma1;
+                       devpriv->dma_current_buf = devpriv->dma_buf1;
                }
                else
                {
                        devpriv->dma_current = devpriv->dma0;
+                       devpriv->dma_current_buf = devpriv->dma_buf0;
                }
+               das1800_flush_dma_channel(dev, s, devpriv->dma_current, devpriv->dma_current_buf);
        }
 
+       // get any remaining samples in fifo
+       das1800_handle_fifo_not_empty(dev, s);
+
        return;
 }
 
@@ -1029,24 +1070,25 @@ static void das1800_handle_fifo_half_full(comedi_device *dev, comedi_subdevice *
 {
        int i;          /* loop index */
        int numPoints = 0;      /* number of points to read */
-       sampl_t dpnt;
+       u16 data[HALF_FIFO];
        int unipolar;
+       comedi_cmd *cmd = &s->async->cmd;
 
        unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB;
 
        numPoints = HALF_FIFO;
        /* if we only need some of the points */
-       if( devpriv->forever == 0 && devpriv->count < numPoints)
+       if(cmd->stop_src == TRIG_COUNT && devpriv->count < numPoints)
                numPoints = devpriv->count;
+       insw(dev->iobase + DAS1800_FIFO, data, numPoints);
        for( i = 0; i < numPoints; i++)
        {
                /* write data point to buffer */
-               dpnt = inw(dev->iobase + DAS1800_FIFO);
                /* convert to unsigned type if we are in a bipolar mode */
                if(!unipolar);
-                       dpnt += 1 << (thisboard->resolution - 1);
-               comedi_buf_put(s->async, dpnt);
-               if(devpriv->count > 0) devpriv->count--;
+                       data[i] += 1 << (thisboard->resolution - 1);
+               comedi_buf_put(s->async, data[i]);
+               if(cmd->stop_src == TRIG_COUNT) devpriv->count--;
        }
        return;
 }
@@ -1055,31 +1097,33 @@ static void das1800_handle_fifo_not_empty(comedi_device *dev, comedi_subdevice *
 {
        sampl_t dpnt;
        int unipolar;
+       comedi_cmd *cmd = &s->async->cmd;
 
        unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB;
 
        while(inb(dev->iobase + DAS1800_STATUS) & FNE)
        {
-               if(devpriv->count == 0 && devpriv->forever == 0)
+               if(cmd->stop_src == TRIG_COUNT && devpriv->count == 0)
                        break;
                dpnt = inw(dev->iobase + DAS1800_FIFO);
                /* convert to unsigned type if we are in a bipolar mode */
                if(!unipolar);
                        dpnt += 1 << (thisboard->resolution - 1);
                comedi_buf_put(s->async, dpnt);
-               if(devpriv->count > 0) devpriv->count--;
+               if(cmd->stop_src == TRIG_COUNT) devpriv->count--;
        }
 
        return;
 }
 
-void disable_das1800(comedi_device *dev)
+static int das1800_cancel(comedi_device *dev, comedi_subdevice *s)
 {
        outb(0x0, dev->iobase + DAS1800_STATUS);        /* disable conversions */
-       outb(0x0, dev->iobase + DAS1800_CONTROL_A);     /* disable and clear fifo and stop triggering */
        outb(0x0, dev->iobase + DAS1800_CONTROL_B);     /* disable interrupts and dma */
+       outb(0x0, dev->iobase + DAS1800_CONTROL_A);     /* disable and clear fifo and stop triggering */
        if(devpriv->dma0) disable_dma(devpriv->dma0);
        if(devpriv->dma1) disable_dma(devpriv->dma1);
+       return 0;
 }
 
 /* test analog input cmd */
@@ -1163,18 +1207,18 @@ static int das1800_ai_do_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_c
        switch(cmd->stop_src)
        {
                case TRIG_EXT:
-                       if(cmd->stop_arg > 0x10000)
+                       if(cmd->stop_arg)
                        {
-                               cmd->stop_arg = 0x10000;
+                               cmd->stop_arg = 0;
                                err++;
                        }
+                       break;
                case TRIG_COUNT:
                        if(!cmd->stop_arg)
                        {
                                cmd->stop_arg = 1;
                                err++;
                        }
-                       // this break is for both TRIG_EXT and TRIG_COUNT
                        break;
                case TRIG_NONE:
                        if(cmd->stop_arg != 0)
@@ -1361,7 +1405,7 @@ int setup_counters(comedi_device *dev, comedi_cmd cmd)
        if(cmd.stop_src == TRIG_EXT)
        {
                // load counter 0 in mode 0
-               i8254_load(dev->iobase + DAS1800_COUNTER, 0, cmd.stop_arg, 0);
+               i8254_load(dev->iobase + DAS1800_COUNTER, 0, 1, 0);
        }
 
        return 0;
@@ -1381,13 +1425,13 @@ void setup_dma(comedi_device *dev, comedi_cmd cmd)
        {
                case TRIG_FOLLOW:       // not in burst mode
                        if(cmd.convert_src == TRIG_TIMER)
-                               devpriv->dma_buf_size = suggest_transfer_size(dev, cmd.convert_arg);
+                               devpriv->dma_transfer_size = suggest_transfer_size(dev, cmd.convert_arg);
                        break;
                case TRIG_TIMER:
-                       devpriv->dma_buf_size = suggest_transfer_size(dev, cmd.scan_begin_arg * cmd.chanlist_len);
+                       devpriv->dma_transfer_size = suggest_transfer_size(dev, cmd.scan_begin_arg / cmd.chanlist_len);
                        break;
                default:
-                       devpriv->dma_buf_size = devpriv->dma_buf_max_size;
+                       devpriv->dma_transfer_size = DMA_BUF_SIZE;
                        break;
        }
 
@@ -1398,12 +1442,12 @@ void setup_dma(comedi_device *dev, comedi_cmd cmd)
        clear_dma_ff(devpriv->dma0);
        set_dma_addr(devpriv->dma0, virt_to_bus(devpriv->dma_buf0));
        // set appropriate size of transfer
-       if(devpriv->count * sizeof(short) >= devpriv->dma_buf_size || devpriv->forever)
-               set_dma_count(devpriv->dma0, devpriv->dma_buf_size);
-       else
-               set_dma_count(devpriv->dma0, devpriv->count * sizeof(short));
+       set_dma_count(devpriv->dma0, devpriv->dma_transfer_size);
+       devpriv->dma_current = devpriv->dma0;
+       devpriv->dma_current_buf = devpriv->dma_buf0;
+       enable_dma(devpriv->dma0);
        // set up dual dma if appropriate
-       if(dual_dma && (devpriv->count * sizeof(short) > devpriv->dma_buf_size || devpriv->forever))
+       if(dual_dma)
        {
                disable_dma(devpriv->dma1);
                /* clear flip-flop to make sure 2-byte registers for
@@ -1411,15 +1455,9 @@ void setup_dma(comedi_device *dev, comedi_cmd cmd)
                clear_dma_ff(devpriv->dma1);
                set_dma_addr(devpriv->dma1, virt_to_bus(devpriv->dma_buf1));
                // set appropriate size of transfer
-               if(devpriv->count * sizeof(short) >= 2 * devpriv->dma_buf_size || devpriv->forever)
-                       set_dma_count(devpriv->dma1, devpriv->dma_buf_size);
-               else
-                       set_dma_count(devpriv->dma1, devpriv->count * sizeof(short) - devpriv->dma_buf_size);
+               set_dma_count(devpriv->dma1, devpriv->dma_transfer_size);
                enable_dma(devpriv->dma1);
        }
-       devpriv->dma_current = devpriv->dma0;
-       devpriv->dma_current_buf = devpriv->dma_buf0;
-       enable_dma(devpriv->dma0);
        release_dma_lock(lock_flags);
 
        return;
@@ -1434,6 +1472,7 @@ void program_chanlist(comedi_device *dev, comedi_cmd cmd)
        const int range_bitshift = 8;
 
        n = cmd.chanlist_len;
+       // spinlock protects indirect addressing
        comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
        outb(QRAM, dev->iobase + DAS1800_SELECT); /* select QRAM for baseAddress + 0x0 */
        outb(n - 1, dev->iobase + DAS1800_QRAM_ADDRESS);        /*set QRAM address start */
@@ -1463,7 +1502,7 @@ static int das1800_ai_do_cmd(comedi_device *dev, comedi_subdevice *s)
                return -1;
        }
 
-       /* disable dma on TRIG_WAKE_EOS (to reduce latency) or TRIG_RT
+       /* disable dma on TRIG_WAKE_EOS, or TRIG_RT
         * (because dma in handler is unsafe at hard real-time priority) */
        if(cmd.flags & (TRIG_WAKE_EOS | TRIG_RT))
        {
@@ -1486,17 +1525,9 @@ static int das1800_ai_do_cmd(comedi_device *dev, comedi_subdevice *s)
        if(cmd.stop_src == TRIG_COUNT)
        {
                devpriv->count = cmd.stop_arg * cmd.chanlist_len;
-               devpriv->forever = 0;
-               /* if they want just a few points, interrupt fifo not empty */
-               if(cmd.stop_arg * cmd.scan_end_arg < HALF_FIFO)
-                       devpriv->irq_dma_bits &= ~FIMD;
-       }else
-       {
-               devpriv->forever = 1;
-               devpriv->count = 0;
        }
 
-       disable_das1800(dev);
+       das1800_cancel(dev, s);
 
        // determine proper bits for control registers
        control_a = control_a_bits(cmd);
@@ -1511,7 +1542,6 @@ static int das1800_ai_do_cmd(comedi_device *dev, comedi_subdevice *s)
                return ret;
        }
        setup_dma(dev, cmd);
-       async->events = 0;
        outb(control_c, dev->iobase + DAS1800_CONTROL_C);
        // set conversion rate and length for burst mode
        if(control_c & BMDE)
@@ -1565,8 +1595,6 @@ static int das1800_ai_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn
        outb(0x0, dev->iobase + DAS1800_QRAM_ADDRESS);  /*finish write to QRAM */
        outb(ADC, dev->iobase + DAS1800_SELECT);        /* select ADC for baseAddress + 0x0 */
 
-       udelay(2);
-
        for(n = 0; n < insn->n; n++)
        {
                /* trigger conversion */
@@ -1698,7 +1726,7 @@ unsigned int burst_convert_arg(unsigned int convert_arg, int round_mode)
 // utility function that suggests a dma transfer size based on the conversion period 'ns'
 unsigned int suggest_transfer_size(comedi_device *dev, unsigned int ns)
 {
-       int size;
+       unsigned int size;
        unsigned int freq = 1000000000 / ns;
        static const int sample_size = 2;       // size in bytes of one sample from board
 
@@ -1706,8 +1734,8 @@ unsigned int suggest_transfer_size(comedi_device *dev, unsigned int ns)
        size = (freq / 3) * sample_size;
 
        // set a minimum and maximum size allowed
-       if(size > devpriv->dma_buf_max_size)
-               size = devpriv->dma_buf_max_size - devpriv->dma_buf_max_size % sample_size;
+       if(size > DMA_BUF_SIZE)
+               size = DMA_BUF_SIZE - DMA_BUF_SIZE % sample_size;
        else if(size < sample_size)
                size = sample_size;