staging: comedi: das1800: complete the refactor to remove all forward declarations
authorH Hartley Sweeten <hartleys@visionengravers.com>
Thu, 24 May 2012 01:19:17 +0000 (18:19 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 5 Jun 2012 03:38:10 +0000 (20:38 -0700)
Complete the refactor of the das1800 driver to remove all the
forward declarations.

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

index 2ac3443..079fb06 100644 (file)
@@ -183,46 +183,6 @@ enum {
        das1802hr, das1802hr_da, das1801hc, das1802hc, das1801ao, das1802ao
 };
 
-static int das1800_probe(struct comedi_device *dev);
-static int das1800_cancel(struct comedi_device *dev,
-                         struct comedi_subdevice *s);
-static irqreturn_t das1800_interrupt(int irq, void *d);
-static int das1800_ai_poll(struct comedi_device *dev,
-                          struct comedi_subdevice *s);
-static void das1800_ai_handler(struct comedi_device *dev);
-static void das1800_handle_dma(struct comedi_device *dev,
-                              struct comedi_subdevice *s, unsigned int status);
-static void das1800_flush_dma(struct comedi_device *dev,
-                             struct comedi_subdevice *s);
-static void das1800_flush_dma_channel(struct comedi_device *dev,
-                                     struct comedi_subdevice *s,
-                                     unsigned int channel, uint16_t *buffer);
-static void das1800_handle_fifo_half_full(struct comedi_device *dev,
-                                         struct comedi_subdevice *s);
-static void das1800_handle_fifo_not_empty(struct comedi_device *dev,
-                                         struct comedi_subdevice *s);
-static int das1800_ai_do_cmdtest(struct comedi_device *dev,
-                                struct comedi_subdevice *s,
-                                struct comedi_cmd *cmd);
-static int das1800_ai_do_cmd(struct comedi_device *dev,
-                            struct comedi_subdevice *s);
-static int das1800_ai_rinsn(struct comedi_device *dev,
-                           struct comedi_subdevice *s,
-                           struct comedi_insn *insn, unsigned int *data);
-static int das1800_ao_winsn(struct comedi_device *dev,
-                           struct comedi_subdevice *s,
-                           struct comedi_insn *insn, unsigned int *data);
-static int das1800_di_rbits(struct comedi_device *dev,
-                           struct comedi_subdevice *s,
-                           struct comedi_insn *insn, unsigned int *data);
-static int das1800_do_wbits(struct comedi_device *dev,
-                           struct comedi_subdevice *s,
-                           struct comedi_insn *insn, unsigned int *data);
-
-static int das1800_set_frequency(struct comedi_device *dev);
-static unsigned int burst_convert_arg(unsigned int convert_arg, int round_mode);
-static unsigned int suggest_transfer_size(struct comedi_cmd *cmd);
-
 /* analog input ranges */
 static const struct comedi_lrange range_ai_das1801 = {
        8,
@@ -515,421 +475,203 @@ static const struct comedi_lrange range_ao_2 = {
 };
 */
 
-static int das1800_init_dma(struct comedi_device *dev, unsigned int dma0,
-                           unsigned int dma1)
+static inline uint16_t munge_bipolar_sample(const struct comedi_device *dev,
+                                           uint16_t sample)
 {
-       unsigned long flags;
-
-       /*  need an irq to do dma */
-       if (dev->irq && dma0) {
-               /* encode dma0 and dma1 into 2 digit hexadecimal for switch */
-               switch ((dma0 & 0x7) | (dma1 << 4)) {
-               case 0x5:       /*  dma0 == 5 */
-                       devpriv->dma_bits |= DMA_CH5;
-                       break;
-               case 0x6:       /*  dma0 == 6 */
-                       devpriv->dma_bits |= DMA_CH6;
-                       break;
-               case 0x7:       /*  dma0 == 7 */
-                       devpriv->dma_bits |= DMA_CH7;
-                       break;
-               case 0x65:      /*  dma0 == 5, dma1 == 6 */
-                       devpriv->dma_bits |= DMA_CH5_CH6;
-                       break;
-               case 0x76:      /*  dma0 == 6, dma1 == 7 */
-                       devpriv->dma_bits |= DMA_CH6_CH7;
-                       break;
-               case 0x57:      /*  dma0 == 7, dma1 == 5 */
-                       devpriv->dma_bits |= DMA_CH7_CH5;
-                       break;
-               default:
-                       dev_err(dev->hw_dev, " only supports dma channels 5 through 7\n"
-                               " Dual dma only allows the following combinations:\n"
-                               " dma 5,6 / 6,7 / or 7,5\n");
-                       return -EINVAL;
-                       break;
-               }
-               if (request_dma(dma0, dev->driver->driver_name)) {
-                       dev_err(dev->hw_dev, "failed to allocate dma channel %i\n",
-                               dma0);
-                       return -EINVAL;
-               }
-               devpriv->dma0 = dma0;
-               devpriv->dma_current = dma0;
-               if (dma1) {
-                       if (request_dma(dma1, dev->driver->driver_name)) {
-                               dev_err(dev->hw_dev, "failed to allocate dma channel %i\n",
-                                       dma1);
-                               return -EINVAL;
-                       }
-                       devpriv->dma1 = dma1;
-               }
-               devpriv->ai_buf0 = kmalloc(DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA);
-               if (devpriv->ai_buf0 == NULL)
-                       return -ENOMEM;
-               devpriv->dma_current_buf = devpriv->ai_buf0;
-               if (dma1) {
-                       devpriv->ai_buf1 =
-                           kmalloc(DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA);
-                       if (devpriv->ai_buf1 == NULL)
-                               return -ENOMEM;
-               }
-               flags = claim_dma_lock();
-               disable_dma(devpriv->dma0);
-               set_dma_mode(devpriv->dma0, DMA_MODE_READ);
-               if (dma1) {
-                       disable_dma(devpriv->dma1);
-                       set_dma_mode(devpriv->dma1, DMA_MODE_READ);
-               }
-               release_dma_lock(flags);
-       }
-       return 0;
+       sample += 1 << (thisboard->resolution - 1);
+       return sample;
 }
 
-static int das1800_attach(struct comedi_device *dev,
-                         struct comedi_devconfig *it)
+static void munge_data(struct comedi_device *dev, uint16_t * array,
+                      unsigned int num_elements)
 {
-       struct comedi_subdevice *s;
-       unsigned long iobase = it->options[0];
-       unsigned int irq = it->options[1];
-       unsigned int dma0 = it->options[2];
-       unsigned int dma1 = it->options[3];
-       unsigned long iobase2;
-       int board;
-       int retval;
+       unsigned int i;
+       int unipolar;
 
-       /* allocate and initialize dev->private */
-       if (alloc_private(dev, sizeof(struct das1800_private)) < 0)
-               return -ENOMEM;
+       /* see if card is using a unipolar or bipolar range so we can munge data correctly */
+       unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB;
 
-       printk(KERN_DEBUG "comedi%d: %s: io 0x%lx", dev->minor,
-              dev->driver->driver_name, iobase);
-       if (irq) {
-               printk(KERN_CONT ", irq %u", irq);
-               if (dma0) {
-                       printk(KERN_CONT ", dma %u", dma0);
-                       if (dma1)
-                               printk(KERN_CONT " and %u", dma1);
-               }
+       /* convert to unsigned type if we are in a bipolar mode */
+       if (!unipolar) {
+               for (i = 0; i < num_elements; i++)
+                       array[i] = munge_bipolar_sample(dev, array[i]);
        }
-       printk(KERN_CONT "\n");
+}
 
-       if (iobase == 0) {
-               dev_err(dev->hw_dev, "io base address required\n");
-               return -EINVAL;
-       }
+static void das1800_handle_fifo_half_full(struct comedi_device *dev,
+                                         struct comedi_subdevice *s)
+{
+       int numPoints = 0;      /* number of points to read */
+       struct comedi_cmd *cmd = &s->async->cmd;
 
-       /* check if io addresses are available */
-       if (!request_region(iobase, DAS1800_SIZE, dev->driver->driver_name)) {
-               printk
-                   (" I/O port conflict: failed to allocate ports 0x%lx to 0x%lx\n",
-                    iobase, iobase + DAS1800_SIZE - 1);
-               return -EIO;
-       }
-       dev->iobase = iobase;
+       numPoints = FIFO_SIZE / 2;
+       /* if we only need some of the points */
+       if (cmd->stop_src == TRIG_COUNT && devpriv->count < numPoints)
+               numPoints = devpriv->count;
+       insw(dev->iobase + DAS1800_FIFO, devpriv->ai_buf0, numPoints);
+       munge_data(dev, devpriv->ai_buf0, numPoints);
+       cfc_write_array_to_buffer(s, devpriv->ai_buf0,
+                                 numPoints * sizeof(devpriv->ai_buf0[0]));
+       if (cmd->stop_src == TRIG_COUNT)
+               devpriv->count -= numPoints;
+       return;
+}
 
-       board = das1800_probe(dev);
-       if (board < 0) {
-               dev_err(dev->hw_dev, "unable to determine board type\n");
-               return -ENODEV;
-       }
+static void das1800_handle_fifo_not_empty(struct comedi_device *dev,
+                                         struct comedi_subdevice *s)
+{
+       short dpnt;
+       int unipolar;
+       struct comedi_cmd *cmd = &s->async->cmd;
 
-       dev->board_ptr = das1800_boards + board;
-       dev->board_name = thisboard->name;
+       unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB;
 
-       /*  if it is an 'ao' board with fancy analog out then we need extra io ports */
-       if (thisboard->ao_ability == 2) {
-               iobase2 = iobase + IOBASE2;
-               if (!request_region(iobase2, DAS1800_SIZE,
-                                   dev->driver->driver_name)) {
-                       printk
-                           (" I/O port conflict: failed to allocate ports 0x%lx to 0x%lx\n",
-                            iobase2, iobase2 + DAS1800_SIZE - 1);
-                       return -EIO;
-               }
-               devpriv->iobase2 = iobase2;
+       while (inb(dev->iobase + DAS1800_STATUS) & FNE) {
+               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 = munge_bipolar_sample(dev, dpnt);
+               cfc_write_to_buffer(s, dpnt);
+               if (cmd->stop_src == TRIG_COUNT)
+                       devpriv->count--;
        }
 
-       /* grab our IRQ */
-       if (irq) {
-               if (request_irq(irq, das1800_interrupt, 0,
-                               dev->driver->driver_name, dev)) {
-                       dev_dbg(dev->hw_dev, "unable to allocate irq %u\n",
-                               irq);
-                       return -EINVAL;
-               }
-       }
-       dev->irq = irq;
+       return;
+}
 
-       /*  set bits that tell card which irq to use */
-       switch (irq) {
-       case 0:
-               break;
-       case 3:
-               devpriv->irq_dma_bits |= 0x8;
-               break;
-       case 5:
-               devpriv->irq_dma_bits |= 0x10;
-               break;
-       case 7:
-               devpriv->irq_dma_bits |= 0x18;
-               break;
-       case 10:
-               devpriv->irq_dma_bits |= 0x28;
-               break;
-       case 11:
-               devpriv->irq_dma_bits |= 0x30;
-               break;
-       case 15:
-               devpriv->irq_dma_bits |= 0x38;
-               break;
-       default:
-               dev_err(dev->hw_dev, "irq out of range\n");
-               return -EINVAL;
-               break;
-       }
+/* Utility function used by das1800_flush_dma() and das1800_handle_dma().
+ * Assumes dma lock is held */
+static void das1800_flush_dma_channel(struct comedi_device *dev,
+                                     struct comedi_subdevice *s,
+                                     unsigned int channel, uint16_t *buffer)
+{
+       unsigned int num_bytes, num_samples;
+       struct comedi_cmd *cmd = &s->async->cmd;
 
-       retval = das1800_init_dma(dev, dma0, dma1);
-       if (retval < 0)
-               return retval;
+       disable_dma(channel);
 
-       if (devpriv->ai_buf0 == NULL) {
-               devpriv->ai_buf0 =
-                   kmalloc(FIFO_SIZE * sizeof(uint16_t), GFP_KERNEL);
-               if (devpriv->ai_buf0 == NULL)
-                       return -ENOMEM;
-       }
+       /* clear flip-flop to make sure 2-byte registers
+        * get set correctly */
+       clear_dma_ff(channel);
 
-       if (alloc_subdevices(dev, 4) < 0)
-               return -ENOMEM;
+       /*  figure out how many points to read */
+       num_bytes = devpriv->dma_transfer_size - get_dma_residue(channel);
+       num_samples = num_bytes / sizeof(short);
 
-       /* analog input subdevice */
-       s = dev->subdevices + 0;
-       dev->read_subdev = s;
-       s->type = COMEDI_SUBD_AI;
-       s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_GROUND | SDF_CMD_READ;
-       if (thisboard->common)
-               s->subdev_flags |= SDF_COMMON;
-       s->n_chan = thisboard->qram_len;
-       s->len_chanlist = thisboard->qram_len;
-       s->maxdata = (1 << thisboard->resolution) - 1;
-       s->range_table = thisboard->range_ai;
-       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 */
-       s = dev->subdevices + 1;
-       if (thisboard->ao_ability == 1) {
-               s->type = COMEDI_SUBD_AO;
-               s->subdev_flags = SDF_WRITABLE;
-               s->n_chan = thisboard->ao_n_chan;
-               s->maxdata = (1 << thisboard->resolution) - 1;
-               s->range_table = &range_ao_1;
-               s->insn_write = das1800_ao_winsn;
-       } else {
-               s->type = COMEDI_SUBD_UNUSED;
-       }
+       /* if we only need some of the points */
+       if (cmd->stop_src == TRIG_COUNT && devpriv->count < num_samples)
+               num_samples = devpriv->count;
 
-       /* di */
-       s = dev->subdevices + 2;
-       s->type = COMEDI_SUBD_DI;
-       s->subdev_flags = SDF_READABLE;
-       s->n_chan = 4;
-       s->maxdata = 1;
-       s->range_table = &range_digital;
-       s->insn_bits = das1800_di_rbits;
+       munge_data(dev, buffer, num_samples);
+       cfc_write_array_to_buffer(s, buffer, num_bytes);
+       if (s->async->cmd.stop_src == TRIG_COUNT)
+               devpriv->count -= num_samples;
 
-       /* do */
-       s = dev->subdevices + 3;
-       s->type = COMEDI_SUBD_DO;
-       s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
-       s->n_chan = thisboard->do_n_chan;
-       s->maxdata = 1;
-       s->range_table = &range_digital;
-       s->insn_bits = das1800_do_wbits;
+       return;
+}
 
-       das1800_cancel(dev, dev->read_subdev);
+/* flushes remaining data from board when external trigger has stopped acquisition
+ * and we are using dma transfers */
+static void das1800_flush_dma(struct comedi_device *dev,
+                             struct comedi_subdevice *s)
+{
+       unsigned long flags;
+       const int dual_dma = devpriv->irq_dma_bits & DMA_DUAL;
 
-       /*  initialize digital out channels */
-       outb(devpriv->do_bits, dev->iobase + DAS1800_DIGITAL);
+       flags = claim_dma_lock();
+       das1800_flush_dma_channel(dev, s, devpriv->dma_current,
+                                 devpriv->dma_current_buf);
 
-       /*  initialize analog out channels */
-       if (thisboard->ao_ability == 1) {
-               /*  select 'update' dac channel for baseAddress + 0x0 */
-               outb(DAC(thisboard->ao_n_chan - 1),
-                    dev->iobase + DAS1800_SELECT);
-               outw(devpriv->ao_update_bits, dev->iobase + DAS1800_DAC);
+       if (dual_dma) {
+               /*  switch to other channel and flush it */
+               if (devpriv->dma_current == devpriv->dma0) {
+                       devpriv->dma_current = devpriv->dma1;
+                       devpriv->dma_current_buf = devpriv->ai_buf1;
+               } else {
+                       devpriv->dma_current = devpriv->dma0;
+                       devpriv->dma_current_buf = devpriv->ai_buf0;
+               }
+               das1800_flush_dma_channel(dev, s, devpriv->dma_current,
+                                         devpriv->dma_current_buf);
        }
 
-       return 0;
-};
+       release_dma_lock(flags);
 
-static void das1800_detach(struct comedi_device *dev)
-{
-       if (dev->iobase)
-               release_region(dev->iobase, DAS1800_SIZE);
-       if (dev->irq)
-               free_irq(dev->irq, dev);
-       if (dev->private) {
-               if (devpriv->iobase2)
-                       release_region(devpriv->iobase2, DAS1800_SIZE);
-               if (devpriv->dma0)
-                       free_dma(devpriv->dma0);
-               if (devpriv->dma1)
-                       free_dma(devpriv->dma1);
-               kfree(devpriv->ai_buf0);
-               kfree(devpriv->ai_buf1);
-       }
-};
+       /*  get any remaining samples in fifo */
+       das1800_handle_fifo_not_empty(dev, s);
 
-/* probes and checks das-1800 series board type
- */
-static int das1800_probe(struct comedi_device *dev)
+       return;
+}
+
+static void das1800_handle_dma(struct comedi_device *dev,
+                              struct comedi_subdevice *s, unsigned int status)
 {
-       int id;
-       int board;
+       unsigned long flags;
+       const int dual_dma = devpriv->irq_dma_bits & DMA_DUAL;
 
-       id = (inb(dev->iobase + DAS1800_DIGITAL) >> 4) & 0xf;   /* get id bits */
-       board = ((struct das1800_board *)dev->board_ptr) - das1800_boards;
+       flags = claim_dma_lock();
+       das1800_flush_dma_channel(dev, s, devpriv->dma_current,
+                                 devpriv->dma_current_buf);
+       /*  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);
 
-       switch (id) {
-       case 0x3:
-               if (board == das1801st_da || board == das1802st_da ||
-                   board == das1701st_da || board == das1702st_da) {
-                       dev_dbg(dev->hw_dev, "Board model: %s\n",
-                               das1800_boards[board].name);
-                       return board;
-               }
-               printk
-                   (" Board model (probed, not recommended): das-1800st-da series\n");
-               return das1801st;
-               break;
-       case 0x4:
-               if (board == das1802hr_da || board == das1702hr_da) {
-                       dev_dbg(dev->hw_dev, "Board model: %s\n",
-                               das1800_boards[board].name);
-                       return board;
-               }
-               printk
-                   (" Board model (probed, not recommended): das-1802hr-da\n");
-               return das1802hr;
-               break;
-       case 0x5:
-               if (board == das1801ao || board == das1802ao ||
-                   board == das1701ao || board == das1702ao) {
-                       dev_dbg(dev->hw_dev, "Board model: %s\n",
-                               das1800_boards[board].name);
-                       return board;
-               }
-               printk
-                   (" Board model (probed, not recommended): das-1800ao series\n");
-               return das1801ao;
-               break;
-       case 0x6:
-               if (board == das1802hr || board == das1702hr) {
-                       dev_dbg(dev->hw_dev, "Board model: %s\n",
-                               das1800_boards[board].name);
-                       return board;
-               }
-               printk
-                   (" Board model (probed, not recommended): das-1802hr\n");
-               return das1802hr;
-               break;
-       case 0x7:
-               if (board == das1801st || board == das1802st ||
-                   board == das1701st || board == das1702st) {
-                       dev_dbg(dev->hw_dev, "Board model: %s\n",
-                               das1800_boards[board].name);
-                       return board;
-               }
-               printk
-                   (" Board model (probed, not recommended): das-1800st series\n");
-               return das1801st;
-               break;
-       case 0x8:
-               if (board == das1801hc || board == das1802hc) {
-                       dev_dbg(dev->hw_dev, "Board model: %s\n",
-                               das1800_boards[board].name);
-                       return board;
+       if (status & DMATC) {
+               /*  clear DMATC interrupt bit */
+               outb(CLEAR_INTR_MASK & ~DMATC, dev->iobase + DAS1800_STATUS);
+               /*  switch dma channels for next time, if appropriate */
+               if (dual_dma) {
+                       /*  read data from the other channel next time */
+                       if (devpriv->dma_current == devpriv->dma0) {
+                               devpriv->dma_current = devpriv->dma1;
+                               devpriv->dma_current_buf = devpriv->ai_buf1;
+                       } else {
+                               devpriv->dma_current = devpriv->dma0;
+                               devpriv->dma_current_buf = devpriv->ai_buf0;
+                       }
                }
-               printk
-                   (" Board model (probed, not recommended): das-1800hc series\n");
-               return das1801hc;
-               break;
-       default:
-               printk
-                   (" Board model: probe returned 0x%x (unknown, please report)\n",
-                    id);
-               return board;
-               break;
        }
-       return -1;
+
+       return;
 }
 
-static int das1800_ai_poll(struct comedi_device *dev,
-                          struct comedi_subdevice *s)
+static int das1800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
 {
-       unsigned long flags;
-
-       /*  prevent race with interrupt handler */
-       spin_lock_irqsave(&dev->spinlock, flags);
-       das1800_ai_handler(dev);
-       spin_unlock_irqrestore(&dev->spinlock, flags);
-
-       return s->async->buf_write_count - s->async->buf_read_count;
+       outb(0x0, dev->iobase + DAS1800_STATUS);        /* disable conversions */
+       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;
 }
 
-static irqreturn_t das1800_interrupt(int irq, void *d)
+/* the guts of the interrupt handler, that is shared with das1800_ai_poll */
+static void das1800_ai_handler(struct comedi_device *dev)
 {
-       struct comedi_device *dev = d;
-       unsigned int status;
+       struct comedi_subdevice *s = dev->subdevices + 0;       /* analog input subdevice */
+       struct comedi_async *async = s->async;
+       struct comedi_cmd *cmd = &async->cmd;
+       unsigned int status = inb(dev->iobase + DAS1800_STATUS);
 
-       if (dev->attached == 0) {
-               comedi_error(dev, "premature interrupt");
-               return IRQ_HANDLED;
-       }
-
-       /* Prevent race with das1800_ai_poll() on multi processor systems.
-        * Also protects indirect addressing in das1800_ai_handler */
-       spin_lock(&dev->spinlock);
-       status = inb(dev->iobase + DAS1800_STATUS);
-
-       /* if interrupt was not caused by das-1800 */
-       if (!(status & INT)) {
-               spin_unlock(&dev->spinlock);
-               return IRQ_NONE;
-       }
-       /* clear the interrupt status bit INT */
-       outb(CLEAR_INTR_MASK & ~INT, dev->iobase + DAS1800_STATUS);
-       /*  handle interrupt */
-       das1800_ai_handler(dev);
-
-       spin_unlock(&dev->spinlock);
-       return IRQ_HANDLED;
-}
-
-/* the guts of the interrupt handler, that is shared with das1800_ai_poll */
-static void das1800_ai_handler(struct comedi_device *dev)
-{
-       struct comedi_subdevice *s = dev->subdevices + 0;       /* analog input subdevice */
-       struct comedi_async *async = s->async;
-       struct comedi_cmd *cmd = &async->cmd;
-       unsigned int status = inb(dev->iobase + DAS1800_STATUS);
-
-       async->events = 0;
-       /*  select adc for base address + 0 */
-       outb(ADC, dev->iobase + DAS1800_SELECT);
-       /*  dma buffer full */
-       if (devpriv->irq_dma_bits & DMA_ENABLED) {
-               /*  look for data from dma transfer even if dma terminal count hasn't happened yet */
-               das1800_handle_dma(dev, s, status);
-       } else if (status & FHF) {      /*  if fifo half full */
-               das1800_handle_fifo_half_full(dev, s);
-       } else if (status & FNE) {      /*  if fifo not empty */
-               das1800_handle_fifo_not_empty(dev, s);
+       async->events = 0;
+       /*  select adc for base address + 0 */
+       outb(ADC, dev->iobase + DAS1800_SELECT);
+       /*  dma buffer full */
+       if (devpriv->irq_dma_bits & DMA_ENABLED) {
+               /*  look for data from dma transfer even if dma terminal count hasn't happened yet */
+               das1800_handle_dma(dev, s, status);
+       } else if (status & FHF) {      /*  if fifo half full */
+               das1800_handle_fifo_half_full(dev, s);
+       } else if (status & FNE) {      /*  if fifo not empty */
+               das1800_handle_fifo_not_empty(dev, s);
        }
 
        async->events |= COMEDI_CB_BLOCK;
@@ -965,182 +707,75 @@ static void das1800_ai_handler(struct comedi_device *dev)
        return;
 }
 
-static void das1800_handle_dma(struct comedi_device *dev,
-                              struct comedi_subdevice *s, unsigned int status)
+static int das1800_ai_poll(struct comedi_device *dev,
+                          struct comedi_subdevice *s)
 {
        unsigned long flags;
-       const int dual_dma = devpriv->irq_dma_bits & DMA_DUAL;
-
-       flags = claim_dma_lock();
-       das1800_flush_dma_channel(dev, s, devpriv->dma_current,
-                                 devpriv->dma_current_buf);
-       /*  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 (status & DMATC) {
-               /*  clear DMATC interrupt bit */
-               outb(CLEAR_INTR_MASK & ~DMATC, dev->iobase + DAS1800_STATUS);
-               /*  switch dma channels for next time, if appropriate */
-               if (dual_dma) {
-                       /*  read data from the other channel next time */
-                       if (devpriv->dma_current == devpriv->dma0) {
-                               devpriv->dma_current = devpriv->dma1;
-                               devpriv->dma_current_buf = devpriv->ai_buf1;
-                       } else {
-                               devpriv->dma_current = devpriv->dma0;
-                               devpriv->dma_current_buf = devpriv->ai_buf0;
-                       }
-               }
-       }
 
-       return;
-}
+       /*  prevent race with interrupt handler */
+       spin_lock_irqsave(&dev->spinlock, flags);
+       das1800_ai_handler(dev);
+       spin_unlock_irqrestore(&dev->spinlock, flags);
 
-static inline uint16_t munge_bipolar_sample(const struct comedi_device *dev,
-                                           uint16_t sample)
-{
-       sample += 1 << (thisboard->resolution - 1);
-       return sample;
+       return s->async->buf_write_count - s->async->buf_read_count;
 }
 
-static void munge_data(struct comedi_device *dev, uint16_t * array,
-                      unsigned int num_elements)
+static irqreturn_t das1800_interrupt(int irq, void *d)
 {
-       unsigned int i;
-       int unipolar;
-
-       /* see if card is using a unipolar or bipolar range so we can munge data correctly */
-       unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB;
+       struct comedi_device *dev = d;
+       unsigned int status;
 
-       /* convert to unsigned type if we are in a bipolar mode */
-       if (!unipolar) {
-               for (i = 0; i < num_elements; i++)
-                       array[i] = munge_bipolar_sample(dev, array[i]);
+       if (dev->attached == 0) {
+               comedi_error(dev, "premature interrupt");
+               return IRQ_HANDLED;
        }
-}
-
-/* Utility function used by das1800_flush_dma() and das1800_handle_dma().
- * Assumes dma lock is held */
-static void das1800_flush_dma_channel(struct comedi_device *dev,
-                                     struct comedi_subdevice *s,
-                                     unsigned int channel, uint16_t *buffer)
-{
-       unsigned int num_bytes, num_samples;
-       struct comedi_cmd *cmd = &s->async->cmd;
-
-       disable_dma(channel);
-
-       /* clear flip-flop to make sure 2-byte registers
-        * get set correctly */
-       clear_dma_ff(channel);
-
-       /*  figure out how many points to read */
-       num_bytes = devpriv->dma_transfer_size - get_dma_residue(channel);
-       num_samples = num_bytes / sizeof(short);
-
-       /* if we only need some of the points */
-       if (cmd->stop_src == TRIG_COUNT && devpriv->count < num_samples)
-               num_samples = devpriv->count;
-
-       munge_data(dev, buffer, num_samples);
-       cfc_write_array_to_buffer(s, buffer, num_bytes);
-       if (s->async->cmd.stop_src == TRIG_COUNT)
-               devpriv->count -= num_samples;
 
-       return;
-}
-
-/* flushes remaining data from board when external trigger has stopped acquisition
- * and we are using dma transfers */
-static void das1800_flush_dma(struct comedi_device *dev,
-                             struct comedi_subdevice *s)
-{
-       unsigned long flags;
-       const int dual_dma = devpriv->irq_dma_bits & DMA_DUAL;
-
-       flags = claim_dma_lock();
-       das1800_flush_dma_channel(dev, s, devpriv->dma_current,
-                                 devpriv->dma_current_buf);
+       /* Prevent race with das1800_ai_poll() on multi processor systems.
+        * Also protects indirect addressing in das1800_ai_handler */
+       spin_lock(&dev->spinlock);
+       status = inb(dev->iobase + DAS1800_STATUS);
 
-       if (dual_dma) {
-               /*  switch to other channel and flush it */
-               if (devpriv->dma_current == devpriv->dma0) {
-                       devpriv->dma_current = devpriv->dma1;
-                       devpriv->dma_current_buf = devpriv->ai_buf1;
-               } else {
-                       devpriv->dma_current = devpriv->dma0;
-                       devpriv->dma_current_buf = devpriv->ai_buf0;
-               }
-               das1800_flush_dma_channel(dev, s, devpriv->dma_current,
-                                         devpriv->dma_current_buf);
+       /* if interrupt was not caused by das-1800 */
+       if (!(status & INT)) {
+               spin_unlock(&dev->spinlock);
+               return IRQ_NONE;
        }
+       /* clear the interrupt status bit INT */
+       outb(CLEAR_INTR_MASK & ~INT, dev->iobase + DAS1800_STATUS);
+       /*  handle interrupt */
+       das1800_ai_handler(dev);
 
-       release_dma_lock(flags);
-
-       /*  get any remaining samples in fifo */
-       das1800_handle_fifo_not_empty(dev, s);
-
-       return;
-}
-
-static void das1800_handle_fifo_half_full(struct comedi_device *dev,
-                                         struct comedi_subdevice *s)
-{
-       int numPoints = 0;      /* number of points to read */
-       struct comedi_cmd *cmd = &s->async->cmd;
-
-       numPoints = FIFO_SIZE / 2;
-       /* if we only need some of the points */
-       if (cmd->stop_src == TRIG_COUNT && devpriv->count < numPoints)
-               numPoints = devpriv->count;
-       insw(dev->iobase + DAS1800_FIFO, devpriv->ai_buf0, numPoints);
-       munge_data(dev, devpriv->ai_buf0, numPoints);
-       cfc_write_array_to_buffer(s, devpriv->ai_buf0,
-                                 numPoints * sizeof(devpriv->ai_buf0[0]));
-       if (cmd->stop_src == TRIG_COUNT)
-               devpriv->count -= numPoints;
-       return;
+       spin_unlock(&dev->spinlock);
+       return IRQ_HANDLED;
 }
 
-static void das1800_handle_fifo_not_empty(struct comedi_device *dev,
-                                         struct comedi_subdevice *s)
+/* converts requested conversion timing to timing compatible with
+ * hardware, used only when card is in 'burst mode'
+ */
+static unsigned int burst_convert_arg(unsigned int convert_arg, int round_mode)
 {
-       short dpnt;
-       int unipolar;
-       struct comedi_cmd *cmd = &s->async->cmd;
+       unsigned int micro_sec;
 
-       unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB;
+       /*  in burst mode, the maximum conversion time is 64 microseconds */
+       if (convert_arg > 64000)
+               convert_arg = 64000;
 
-       while (inb(dev->iobase + DAS1800_STATUS) & FNE) {
-               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 = munge_bipolar_sample(dev, dpnt);
-               cfc_write_to_buffer(s, dpnt);
-               if (cmd->stop_src == TRIG_COUNT)
-                       devpriv->count--;
+       /*  the conversion time must be an integral number of microseconds */
+       switch (round_mode) {
+       case TRIG_ROUND_NEAREST:
+       default:
+               micro_sec = (convert_arg + 500) / 1000;
+               break;
+       case TRIG_ROUND_DOWN:
+               micro_sec = convert_arg / 1000;
+               break;
+       case TRIG_ROUND_UP:
+               micro_sec = (convert_arg - 1) / 1000 + 1;
+               break;
        }
 
-       return;
-}
-
-static int das1800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
-{
-       outb(0x0, dev->iobase + DAS1800_STATUS);        /* disable conversions */
-       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;
+       /*  return number of nanoseconds */
+       return micro_sec * 1000;
 }
 
 /* test analog input cmd */
@@ -1322,10 +957,6 @@ static int das1800_ai_do_cmdtest(struct comedi_device *dev,
        return 0;
 }
 
-/* analog input cmd interface */
-
-/* first, some utility functions used in the main ai_do_cmd() */
-
 /* returns appropriate bits for control register a, depending on command */
 static int control_a_bits(struct comedi_cmd cmd)
 {
@@ -1396,15 +1027,34 @@ static int control_c_bits(struct comedi_cmd cmd)
        return control_c;
 }
 
-/* sets up counters */
-static int setup_counters(struct comedi_device *dev, struct comedi_cmd cmd)
+/* loads counters with divisor1, divisor2 from private structure */
+static int das1800_set_frequency(struct comedi_device *dev)
 {
-       /*  setup cascaded counters for conversion/scan frequency */
-       switch (cmd.scan_begin_src) {
-       case TRIG_FOLLOW:       /*  not in burst mode */
-               if (cmd.convert_src == TRIG_TIMER) {
-                       /* set conversion frequency */
-                       i8253_cascade_ns_to_timer_2div(TIMER_BASE,
+       int err = 0;
+
+       /*  counter 1, mode 2 */
+       if (i8254_load(dev->iobase + DAS1800_COUNTER, 0, 1, devpriv->divisor1,
+                      2))
+               err++;
+       /*  counter 2, mode 2 */
+       if (i8254_load(dev->iobase + DAS1800_COUNTER, 0, 2, devpriv->divisor2,
+                      2))
+               err++;
+       if (err)
+               return -1;
+
+       return 0;
+}
+
+/* sets up counters */
+static int setup_counters(struct comedi_device *dev, struct comedi_cmd cmd)
+{
+       /*  setup cascaded counters for conversion/scan frequency */
+       switch (cmd.scan_begin_src) {
+       case TRIG_FOLLOW:       /*  not in burst mode */
+               if (cmd.convert_src == TRIG_TIMER) {
+                       /* set conversion frequency */
+                       i8253_cascade_ns_to_timer_2div(TIMER_BASE,
                                                       &(devpriv->divisor1),
                                                       &(devpriv->divisor2),
                                                       &(cmd.convert_arg),
@@ -1436,6 +1086,44 @@ static int setup_counters(struct comedi_device *dev, struct comedi_cmd cmd)
        return 0;
 }
 
+/* utility function that suggests a dma transfer size based on the conversion period 'ns' */
+static unsigned int suggest_transfer_size(struct comedi_cmd *cmd)
+{
+       unsigned int size = DMA_BUF_SIZE;
+       static const int sample_size = 2;       /*  size in bytes of one sample from board */
+       unsigned int fill_time = 300000000;     /*  target time in nanoseconds for filling dma buffer */
+       unsigned int max_size;  /*  maximum size we will allow for a transfer */
+
+       /*  make dma buffer fill in 0.3 seconds for timed modes */
+       switch (cmd->scan_begin_src) {
+       case TRIG_FOLLOW:       /*  not in burst mode */
+               if (cmd->convert_src == TRIG_TIMER)
+                       size = (fill_time / cmd->convert_arg) * sample_size;
+               break;
+       case TRIG_TIMER:
+               size = (fill_time / (cmd->scan_begin_arg * cmd->chanlist_len)) *
+                   sample_size;
+               break;
+       default:
+               size = DMA_BUF_SIZE;
+               break;
+       }
+
+       /*  set a minimum and maximum size allowed */
+       max_size = DMA_BUF_SIZE;
+       /*  if we are taking limited number of conversions, limit transfer size to that */
+       if (cmd->stop_src == TRIG_COUNT &&
+           cmd->stop_arg * cmd->chanlist_len * sample_size < max_size)
+               max_size = cmd->stop_arg * cmd->chanlist_len * sample_size;
+
+       if (size > max_size)
+               size = max_size;
+       if (size < sample_size)
+               size = sample_size;
+
+       return size;
+}
+
 /* sets up dma */
 static void setup_dma(struct comedi_device *dev, struct comedi_cmd cmd)
 {
@@ -1689,91 +1377,357 @@ static int das1800_do_wbits(struct comedi_device *dev,
        return 2;
 }
 
-/* loads counters with divisor1, divisor2 from private structure */
-static int das1800_set_frequency(struct comedi_device *dev)
+static int das1800_init_dma(struct comedi_device *dev, unsigned int dma0,
+                           unsigned int dma1)
 {
-       int err = 0;
-
-       /*  counter 1, mode 2 */
-       if (i8254_load(dev->iobase + DAS1800_COUNTER, 0, 1, devpriv->divisor1,
-                      2))
-               err++;
-       /*  counter 2, mode 2 */
-       if (i8254_load(dev->iobase + DAS1800_COUNTER, 0, 2, devpriv->divisor2,
-                      2))
-               err++;
-       if (err)
-               return -1;
+       unsigned long flags;
 
+       /*  need an irq to do dma */
+       if (dev->irq && dma0) {
+               /* encode dma0 and dma1 into 2 digit hexadecimal for switch */
+               switch ((dma0 & 0x7) | (dma1 << 4)) {
+               case 0x5:       /*  dma0 == 5 */
+                       devpriv->dma_bits |= DMA_CH5;
+                       break;
+               case 0x6:       /*  dma0 == 6 */
+                       devpriv->dma_bits |= DMA_CH6;
+                       break;
+               case 0x7:       /*  dma0 == 7 */
+                       devpriv->dma_bits |= DMA_CH7;
+                       break;
+               case 0x65:      /*  dma0 == 5, dma1 == 6 */
+                       devpriv->dma_bits |= DMA_CH5_CH6;
+                       break;
+               case 0x76:      /*  dma0 == 6, dma1 == 7 */
+                       devpriv->dma_bits |= DMA_CH6_CH7;
+                       break;
+               case 0x57:      /*  dma0 == 7, dma1 == 5 */
+                       devpriv->dma_bits |= DMA_CH7_CH5;
+                       break;
+               default:
+                       dev_err(dev->hw_dev, " only supports dma channels 5 through 7\n"
+                               " Dual dma only allows the following combinations:\n"
+                               " dma 5,6 / 6,7 / or 7,5\n");
+                       return -EINVAL;
+                       break;
+               }
+               if (request_dma(dma0, dev->driver->driver_name)) {
+                       dev_err(dev->hw_dev, "failed to allocate dma channel %i\n",
+                               dma0);
+                       return -EINVAL;
+               }
+               devpriv->dma0 = dma0;
+               devpriv->dma_current = dma0;
+               if (dma1) {
+                       if (request_dma(dma1, dev->driver->driver_name)) {
+                               dev_err(dev->hw_dev, "failed to allocate dma channel %i\n",
+                                       dma1);
+                               return -EINVAL;
+                       }
+                       devpriv->dma1 = dma1;
+               }
+               devpriv->ai_buf0 = kmalloc(DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA);
+               if (devpriv->ai_buf0 == NULL)
+                       return -ENOMEM;
+               devpriv->dma_current_buf = devpriv->ai_buf0;
+               if (dma1) {
+                       devpriv->ai_buf1 =
+                           kmalloc(DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA);
+                       if (devpriv->ai_buf1 == NULL)
+                               return -ENOMEM;
+               }
+               flags = claim_dma_lock();
+               disable_dma(devpriv->dma0);
+               set_dma_mode(devpriv->dma0, DMA_MODE_READ);
+               if (dma1) {
+                       disable_dma(devpriv->dma1);
+                       set_dma_mode(devpriv->dma1, DMA_MODE_READ);
+               }
+               release_dma_lock(flags);
+       }
        return 0;
 }
 
-/* converts requested conversion timing to timing compatible with
- * hardware, used only when card is in 'burst mode'
- */
-static unsigned int burst_convert_arg(unsigned int convert_arg, int round_mode)
+static int das1800_probe(struct comedi_device *dev)
 {
-       unsigned int micro_sec;
+       int id;
+       int board;
 
-       /*  in burst mode, the maximum conversion time is 64 microseconds */
-       if (convert_arg > 64000)
-               convert_arg = 64000;
+       id = (inb(dev->iobase + DAS1800_DIGITAL) >> 4) & 0xf;   /* get id bits */
+       board = ((struct das1800_board *)dev->board_ptr) - das1800_boards;
 
-       /*  the conversion time must be an integral number of microseconds */
-       switch (round_mode) {
-       case TRIG_ROUND_NEAREST:
-       default:
-               micro_sec = (convert_arg + 500) / 1000;
+       switch (id) {
+       case 0x3:
+               if (board == das1801st_da || board == das1802st_da ||
+                   board == das1701st_da || board == das1702st_da) {
+                       dev_dbg(dev->hw_dev, "Board model: %s\n",
+                               das1800_boards[board].name);
+                       return board;
+               }
+               printk
+                   (" Board model (probed, not recommended): das-1800st-da series\n");
+               return das1801st;
                break;
-       case TRIG_ROUND_DOWN:
-               micro_sec = convert_arg / 1000;
+       case 0x4:
+               if (board == das1802hr_da || board == das1702hr_da) {
+                       dev_dbg(dev->hw_dev, "Board model: %s\n",
+                               das1800_boards[board].name);
+                       return board;
+               }
+               printk
+                   (" Board model (probed, not recommended): das-1802hr-da\n");
+               return das1802hr;
                break;
-       case TRIG_ROUND_UP:
-               micro_sec = (convert_arg - 1) / 1000 + 1;
+       case 0x5:
+               if (board == das1801ao || board == das1802ao ||
+                   board == das1701ao || board == das1702ao) {
+                       dev_dbg(dev->hw_dev, "Board model: %s\n",
+                               das1800_boards[board].name);
+                       return board;
+               }
+               printk
+                   (" Board model (probed, not recommended): das-1800ao series\n");
+               return das1801ao;
+               break;
+       case 0x6:
+               if (board == das1802hr || board == das1702hr) {
+                       dev_dbg(dev->hw_dev, "Board model: %s\n",
+                               das1800_boards[board].name);
+                       return board;
+               }
+               printk
+                   (" Board model (probed, not recommended): das-1802hr\n");
+               return das1802hr;
+               break;
+       case 0x7:
+               if (board == das1801st || board == das1802st ||
+                   board == das1701st || board == das1702st) {
+                       dev_dbg(dev->hw_dev, "Board model: %s\n",
+                               das1800_boards[board].name);
+                       return board;
+               }
+               printk
+                   (" Board model (probed, not recommended): das-1800st series\n");
+               return das1801st;
+               break;
+       case 0x8:
+               if (board == das1801hc || board == das1802hc) {
+                       dev_dbg(dev->hw_dev, "Board model: %s\n",
+                               das1800_boards[board].name);
+                       return board;
+               }
+               printk
+                   (" Board model (probed, not recommended): das-1800hc series\n");
+               return das1801hc;
+               break;
+       default:
+               printk
+                   (" Board model: probe returned 0x%x (unknown, please report)\n",
+                    id);
+               return board;
                break;
        }
-
-       /*  return number of nanoseconds */
-       return micro_sec * 1000;
+       return -1;
 }
 
-/* utility function that suggests a dma transfer size based on the conversion period 'ns' */
-static unsigned int suggest_transfer_size(struct comedi_cmd *cmd)
+static int das1800_attach(struct comedi_device *dev,
+                         struct comedi_devconfig *it)
 {
-       unsigned int size = DMA_BUF_SIZE;
-       static const int sample_size = 2;       /*  size in bytes of one sample from board */
-       unsigned int fill_time = 300000000;     /*  target time in nanoseconds for filling dma buffer */
-       unsigned int max_size;  /*  maximum size we will allow for a transfer */
+       struct comedi_subdevice *s;
+       unsigned long iobase = it->options[0];
+       unsigned int irq = it->options[1];
+       unsigned int dma0 = it->options[2];
+       unsigned int dma1 = it->options[3];
+       unsigned long iobase2;
+       int board;
+       int retval;
 
-       /*  make dma buffer fill in 0.3 seconds for timed modes */
-       switch (cmd->scan_begin_src) {
-       case TRIG_FOLLOW:       /*  not in burst mode */
-               if (cmd->convert_src == TRIG_TIMER)
-                       size = (fill_time / cmd->convert_arg) * sample_size;
+       /* allocate and initialize dev->private */
+       if (alloc_private(dev, sizeof(struct das1800_private)) < 0)
+               return -ENOMEM;
+
+       printk(KERN_DEBUG "comedi%d: %s: io 0x%lx", dev->minor,
+              dev->driver->driver_name, iobase);
+       if (irq) {
+               printk(KERN_CONT ", irq %u", irq);
+               if (dma0) {
+                       printk(KERN_CONT ", dma %u", dma0);
+                       if (dma1)
+                               printk(KERN_CONT " and %u", dma1);
+               }
+       }
+       printk(KERN_CONT "\n");
+
+       if (iobase == 0) {
+               dev_err(dev->hw_dev, "io base address required\n");
+               return -EINVAL;
+       }
+
+       /* check if io addresses are available */
+       if (!request_region(iobase, DAS1800_SIZE, dev->driver->driver_name)) {
+               printk
+                   (" I/O port conflict: failed to allocate ports 0x%lx to 0x%lx\n",
+                    iobase, iobase + DAS1800_SIZE - 1);
+               return -EIO;
+       }
+       dev->iobase = iobase;
+
+       board = das1800_probe(dev);
+       if (board < 0) {
+               dev_err(dev->hw_dev, "unable to determine board type\n");
+               return -ENODEV;
+       }
+
+       dev->board_ptr = das1800_boards + board;
+       dev->board_name = thisboard->name;
+
+       /*  if it is an 'ao' board with fancy analog out then we need extra io ports */
+       if (thisboard->ao_ability == 2) {
+               iobase2 = iobase + IOBASE2;
+               if (!request_region(iobase2, DAS1800_SIZE,
+                                   dev->driver->driver_name)) {
+                       printk
+                           (" I/O port conflict: failed to allocate ports 0x%lx to 0x%lx\n",
+                            iobase2, iobase2 + DAS1800_SIZE - 1);
+                       return -EIO;
+               }
+               devpriv->iobase2 = iobase2;
+       }
+
+       /* grab our IRQ */
+       if (irq) {
+               if (request_irq(irq, das1800_interrupt, 0,
+                               dev->driver->driver_name, dev)) {
+                       dev_dbg(dev->hw_dev, "unable to allocate irq %u\n",
+                               irq);
+                       return -EINVAL;
+               }
+       }
+       dev->irq = irq;
+
+       /*  set bits that tell card which irq to use */
+       switch (irq) {
+       case 0:
                break;
-       case TRIG_TIMER:
-               size = (fill_time / (cmd->scan_begin_arg * cmd->chanlist_len)) *
-                   sample_size;
+       case 3:
+               devpriv->irq_dma_bits |= 0x8;
+               break;
+       case 5:
+               devpriv->irq_dma_bits |= 0x10;
+               break;
+       case 7:
+               devpriv->irq_dma_bits |= 0x18;
+               break;
+       case 10:
+               devpriv->irq_dma_bits |= 0x28;
+               break;
+       case 11:
+               devpriv->irq_dma_bits |= 0x30;
+               break;
+       case 15:
+               devpriv->irq_dma_bits |= 0x38;
                break;
        default:
-               size = DMA_BUF_SIZE;
+               dev_err(dev->hw_dev, "irq out of range\n");
+               return -EINVAL;
                break;
        }
 
-       /*  set a minimum and maximum size allowed */
-       max_size = DMA_BUF_SIZE;
-       /*  if we are taking limited number of conversions, limit transfer size to that */
-       if (cmd->stop_src == TRIG_COUNT &&
-           cmd->stop_arg * cmd->chanlist_len * sample_size < max_size)
-               max_size = cmd->stop_arg * cmd->chanlist_len * sample_size;
+       retval = das1800_init_dma(dev, dma0, dma1);
+       if (retval < 0)
+               return retval;
 
-       if (size > max_size)
-               size = max_size;
-       if (size < sample_size)
-               size = sample_size;
+       if (devpriv->ai_buf0 == NULL) {
+               devpriv->ai_buf0 =
+                   kmalloc(FIFO_SIZE * sizeof(uint16_t), GFP_KERNEL);
+               if (devpriv->ai_buf0 == NULL)
+                       return -ENOMEM;
+       }
 
-       return size;
-}
+       if (alloc_subdevices(dev, 4) < 0)
+               return -ENOMEM;
+
+       /* analog input subdevice */
+       s = dev->subdevices + 0;
+       dev->read_subdev = s;
+       s->type = COMEDI_SUBD_AI;
+       s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_GROUND | SDF_CMD_READ;
+       if (thisboard->common)
+               s->subdev_flags |= SDF_COMMON;
+       s->n_chan = thisboard->qram_len;
+       s->len_chanlist = thisboard->qram_len;
+       s->maxdata = (1 << thisboard->resolution) - 1;
+       s->range_table = thisboard->range_ai;
+       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 */
+       s = dev->subdevices + 1;
+       if (thisboard->ao_ability == 1) {
+               s->type = COMEDI_SUBD_AO;
+               s->subdev_flags = SDF_WRITABLE;
+               s->n_chan = thisboard->ao_n_chan;
+               s->maxdata = (1 << thisboard->resolution) - 1;
+               s->range_table = &range_ao_1;
+               s->insn_write = das1800_ao_winsn;
+       } else {
+               s->type = COMEDI_SUBD_UNUSED;
+       }
+
+       /* di */
+       s = dev->subdevices + 2;
+       s->type = COMEDI_SUBD_DI;
+       s->subdev_flags = SDF_READABLE;
+       s->n_chan = 4;
+       s->maxdata = 1;
+       s->range_table = &range_digital;
+       s->insn_bits = das1800_di_rbits;
+
+       /* do */
+       s = dev->subdevices + 3;
+       s->type = COMEDI_SUBD_DO;
+       s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+       s->n_chan = thisboard->do_n_chan;
+       s->maxdata = 1;
+       s->range_table = &range_digital;
+       s->insn_bits = das1800_do_wbits;
+
+       das1800_cancel(dev, dev->read_subdev);
+
+       /*  initialize digital out channels */
+       outb(devpriv->do_bits, dev->iobase + DAS1800_DIGITAL);
+
+       /*  initialize analog out channels */
+       if (thisboard->ao_ability == 1) {
+               /*  select 'update' dac channel for baseAddress + 0x0 */
+               outb(DAC(thisboard->ao_n_chan - 1),
+                    dev->iobase + DAS1800_SELECT);
+               outw(devpriv->ao_update_bits, dev->iobase + DAS1800_DAC);
+       }
+
+       return 0;
+};
+
+static void das1800_detach(struct comedi_device *dev)
+{
+       if (dev->iobase)
+               release_region(dev->iobase, DAS1800_SIZE);
+       if (dev->irq)
+               free_irq(dev->irq, dev);
+       if (dev->private) {
+               if (devpriv->iobase2)
+                       release_region(devpriv->iobase2, DAS1800_SIZE);
+               if (devpriv->dma0)
+                       free_dma(devpriv->dma0);
+               if (devpriv->dma1)
+                       free_dma(devpriv->dma1);
+               kfree(devpriv->ai_buf0);
+               kfree(devpriv->ai_buf1);
+       }
+};
 
 static struct comedi_driver das1800_driver = {
        .driver_name    = "das1800",