staging: comedi: ni_labpc: cleanup the boardinfo
[firefly-linux-kernel-4.4.55.git] / drivers / staging / comedi / drivers / ni_labpc.c
index f957b8859b3d05873305e053a25cce3f8ed3aed7..05751467e4223f4f9cefbaf7148d794baeda59c8 100644 (file)
@@ -215,55 +215,6 @@ enum scan_mode {
        MODE_MULT_CHAN_DOWN,
 };
 
-static int labpc_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
-static irqreturn_t labpc_interrupt(int irq, void *d);
-static int labpc_drain_fifo(struct comedi_device *dev);
-#ifdef CONFIG_ISA_DMA_API
-static void labpc_drain_dma(struct comedi_device *dev);
-static void handle_isa_dma(struct comedi_device *dev);
-#endif
-static void labpc_drain_dregs(struct comedi_device *dev);
-static int labpc_ai_cmdtest(struct comedi_device *dev,
-                           struct comedi_subdevice *s, struct comedi_cmd *cmd);
-static int labpc_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
-static int labpc_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
-                         struct comedi_insn *insn, unsigned int *data);
-static int labpc_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
-                         struct comedi_insn *insn, unsigned int *data);
-static int labpc_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
-                         struct comedi_insn *insn, unsigned int *data);
-static int labpc_calib_read_insn(struct comedi_device *dev,
-                                struct comedi_subdevice *s,
-                                struct comedi_insn *insn, unsigned int *data);
-static int labpc_calib_write_insn(struct comedi_device *dev,
-                                 struct comedi_subdevice *s,
-                                 struct comedi_insn *insn, unsigned int *data);
-static int labpc_eeprom_read_insn(struct comedi_device *dev,
-                                 struct comedi_subdevice *s,
-                                 struct comedi_insn *insn, unsigned int *data);
-static int labpc_eeprom_write_insn(struct comedi_device *dev,
-                                  struct comedi_subdevice *s,
-                                  struct comedi_insn *insn,
-                                  unsigned int *data);
-static void labpc_adc_timing(struct comedi_device *dev, struct comedi_cmd *cmd,
-                            enum scan_mode scan_mode);
-#ifdef CONFIG_ISA_DMA_API
-static unsigned int labpc_suggest_transfer_size(const struct comedi_cmd *cmd);
-#endif
-static int labpc_dio_mem_callback(int dir, int port, int data,
-                                 unsigned long arg);
-static void labpc_serial_out(struct comedi_device *dev, unsigned int value,
-                            unsigned int num_bits);
-static unsigned int labpc_serial_in(struct comedi_device *dev);
-static unsigned int labpc_eeprom_read(struct comedi_device *dev,
-                                     unsigned int address);
-static unsigned int labpc_eeprom_read_status(struct comedi_device *dev);
-static int labpc_eeprom_write(struct comedi_device *dev,
-                                      unsigned int address,
-                                      unsigned int value);
-static void write_caldac(struct comedi_device *dev, unsigned int channel,
-                        unsigned int value);
-
 /* analog input ranges */
 #define NUM_LABPC_PLUS_AI_RANGES 16
 /* indicates unipolar ranges */
@@ -420,435 +371,362 @@ static inline void labpc_writeb(unsigned int byte, unsigned long address)
        writeb(byte, (void __iomem *)address);
 }
 
-static const struct labpc_board_struct labpc_boards[] = {
-       {
-        .name = "lab-pc-1200",
-        .ai_speed = 10000,
-        .bustype = isa_bustype,
-        .register_layout = labpc_1200_layout,
-        .has_ao = 1,
-        .ai_range_table = &range_labpc_1200_ai,
-        .ai_range_code = labpc_1200_ai_gain_bits,
-        .ai_range_is_unipolar = labpc_1200_is_unipolar,
-        .ai_scan_up = 1,
-        .memory_mapped_io = 0,
-        },
+static const struct labpc_boardinfo labpc_boards[] = {
        {
-        .name = "lab-pc-1200ai",
-        .ai_speed = 10000,
-        .bustype = isa_bustype,
-        .register_layout = labpc_1200_layout,
-        .has_ao = 0,
-        .ai_range_table = &range_labpc_1200_ai,
-        .ai_range_code = labpc_1200_ai_gain_bits,
-        .ai_range_is_unipolar = labpc_1200_is_unipolar,
-        .ai_scan_up = 1,
-        .memory_mapped_io = 0,
-        },
-       {
-        .name = "lab-pc+",
-        .ai_speed = 12000,
-        .bustype = isa_bustype,
-        .register_layout = labpc_plus_layout,
-        .has_ao = 1,
-        .ai_range_table = &range_labpc_plus_ai,
-        .ai_range_code = labpc_plus_ai_gain_bits,
-        .ai_range_is_unipolar = labpc_plus_is_unipolar,
-        .ai_scan_up = 0,
-        .memory_mapped_io = 0,
-        },
+               .name                   = "lab-pc-1200",
+               .ai_speed               = 10000,
+               .bustype                = isa_bustype,
+               .register_layout        = labpc_1200_layout,
+               .has_ao                 = 1,
+               .ai_range_table         = &range_labpc_1200_ai,
+               .ai_range_code          = labpc_1200_ai_gain_bits,
+               .ai_range_is_unipolar   = labpc_1200_is_unipolar,
+               .ai_scan_up             = 1,
+       }, {
+               .name                   = "lab-pc-1200ai",
+               .ai_speed               = 10000,
+               .bustype                = isa_bustype,
+               .register_layout        = labpc_1200_layout,
+               .ai_range_table         = &range_labpc_1200_ai,
+               .ai_range_code          = labpc_1200_ai_gain_bits,
+               .ai_range_is_unipolar   = labpc_1200_is_unipolar,
+               .ai_scan_up             = 1,
+       }, {
+               .name                   = "lab-pc+",
+               .ai_speed               = 12000,
+               .bustype                = isa_bustype,
+               .register_layout        = labpc_plus_layout,
+               .has_ao                 = 1,
+               .ai_range_table         = &range_labpc_plus_ai,
+               .ai_range_code          = labpc_plus_ai_gain_bits,
+               .ai_range_is_unipolar   = labpc_plus_is_unipolar,
+       },
 #ifdef CONFIG_COMEDI_PCI_DRIVERS
        {
-        .name = "pci-1200",
-        .device_id = 0x161,
-        .ai_speed = 10000,
-        .bustype = pci_bustype,
-        .register_layout = labpc_1200_layout,
-        .has_ao = 1,
-        .ai_range_table = &range_labpc_1200_ai,
-        .ai_range_code = labpc_1200_ai_gain_bits,
-        .ai_range_is_unipolar = labpc_1200_is_unipolar,
-        .ai_scan_up = 1,
-        .memory_mapped_io = 1,
-        },
-/* dummy entry so pci board works when comedi_config is passed driver name */
-       {
-        .name = DRV_NAME,
-        .bustype = pci_bustype,
-        },
+               .name                   = "pci-1200",
+               .device_id              = 0x161,
+               .ai_speed               = 10000,
+               .bustype                = pci_bustype,
+               .register_layout        = labpc_1200_layout,
+               .has_ao                 = 1,
+               .ai_range_table         = &range_labpc_1200_ai,
+               .ai_range_code          = labpc_1200_ai_gain_bits,
+               .ai_range_is_unipolar   = labpc_1200_is_unipolar,
+               .ai_scan_up             = 1,
+               .memory_mapped_io       = 1,
+       },
 #endif
 };
 
-/*
- * Useful for shorthand access to the particular board structure
- */
-#define thisboard ((struct labpc_board_struct *)dev->board_ptr)
-
 /* size in bytes of dma buffer */
 static const int dma_buffer_size = 0xff00;
 /* 2 bytes per sample */
 static const int sample_size = 2;
 
-static inline int labpc_counter_load(struct comedi_device *dev,
-                                    unsigned long base_address,
-                                    unsigned int counter_number,
-                                    unsigned int count, unsigned int mode)
+static void labpc_clear_adc_fifo(const struct comedi_device *dev)
 {
-       if (thisboard->memory_mapped_io)
-               return i8254_mm_load((void __iomem *)base_address, 0,
-                                    counter_number, count, mode);
-       else
-               return i8254_load(base_address, 0, counter_number, count, mode);
+       struct labpc_private *devpriv = dev->private;
+
+       devpriv->write_byte(0x1, dev->iobase + ADC_CLEAR_REG);
+       devpriv->read_byte(dev->iobase + ADC_FIFO_REG);
+       devpriv->read_byte(dev->iobase + ADC_FIFO_REG);
 }
 
-int labpc_common_attach(struct comedi_device *dev, unsigned long iobase,
-                       unsigned int irq, unsigned int dma_chan)
+static int labpc_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
+                         struct comedi_insn *insn, unsigned int *data)
 {
+       const struct labpc_boardinfo *thisboard = comedi_board(dev);
        struct labpc_private *devpriv = dev->private;
-       struct comedi_subdevice *s;
-       int i;
-       unsigned long isr_flags;
-#ifdef CONFIG_ISA_DMA_API
-       unsigned long dma_flags;
-#endif
-       short lsb, msb;
-       int ret;
-
-       dev_info(dev->class_dev, "ni_labpc: %s\n", thisboard->name);
-       if (iobase == 0) {
-               dev_err(dev->class_dev, "io base address is zero!\n");
-               return -EINVAL;
-       }
-       /*  request io regions for isa boards */
-       if (thisboard->bustype == isa_bustype) {
-               /* check if io addresses are available */
-               if (!request_region(iobase, LABPC_SIZE, DRV_NAME)) {
-                       dev_err(dev->class_dev, "I/O port conflict\n");
-                       return -EIO;
-               }
-       }
-       dev->iobase = iobase;
+       int i, n;
+       int chan, range;
+       int lsb, msb;
+       int timeout = 1000;
+       unsigned long flags;
 
-       if (thisboard->memory_mapped_io) {
-               devpriv->read_byte = labpc_readb;
-               devpriv->write_byte = labpc_writeb;
-       } else {
-               devpriv->read_byte = labpc_inb;
-               devpriv->write_byte = labpc_outb;
-       }
-       /* initialize board's command registers */
-       devpriv->write_byte(devpriv->command1_bits, dev->iobase + COMMAND1_REG);
+       /*  disable timed conversions */
+       spin_lock_irqsave(&dev->spinlock, flags);
+       devpriv->command2_bits &= ~SWTRIG_BIT & ~HWTRIG_BIT & ~PRETRIG_BIT;
        devpriv->write_byte(devpriv->command2_bits, dev->iobase + COMMAND2_REG);
+       spin_unlock_irqrestore(&dev->spinlock, flags);
+
+       /*  disable interrupt generation and dma */
+       devpriv->command3_bits = 0;
        devpriv->write_byte(devpriv->command3_bits, dev->iobase + COMMAND3_REG);
-       devpriv->write_byte(devpriv->command4_bits, dev->iobase + COMMAND4_REG);
+
+       /* set gain and channel */
+       devpriv->command1_bits = 0;
+       chan = CR_CHAN(insn->chanspec);
+       range = CR_RANGE(insn->chanspec);
+       devpriv->command1_bits |= thisboard->ai_range_code[range];
+       /* munge channel bits for differential/scan disabled mode */
+       if (CR_AREF(insn->chanspec) == AREF_DIFF)
+               chan *= 2;
+       devpriv->command1_bits |= ADC_CHAN_BITS(chan);
+       devpriv->write_byte(devpriv->command1_bits, dev->iobase + COMMAND1_REG);
+
+       /* setup command6 register for 1200 boards */
        if (thisboard->register_layout == labpc_1200_layout) {
-               devpriv->write_byte(devpriv->command5_bits,
-                                   dev->iobase + COMMAND5_REG);
+               /*  reference inputs to ground or common? */
+               if (CR_AREF(insn->chanspec) != AREF_GROUND)
+                       devpriv->command6_bits |= ADC_COMMON_BIT;
+               else
+                       devpriv->command6_bits &= ~ADC_COMMON_BIT;
+               /* bipolar or unipolar range? */
+               if (thisboard->ai_range_is_unipolar[range])
+                       devpriv->command6_bits |= ADC_UNIP_BIT;
+               else
+                       devpriv->command6_bits &= ~ADC_UNIP_BIT;
+               /* don't interrupt on fifo half full */
+               devpriv->command6_bits &= ~ADC_FHF_INTR_EN_BIT;
+               /* don't enable interrupt on counter a1 terminal count? */
+               devpriv->command6_bits &= ~A1_INTR_EN_BIT;
+               /* write to register */
                devpriv->write_byte(devpriv->command6_bits,
                                    dev->iobase + COMMAND6_REG);
        }
+       /* setup command4 register */
+       devpriv->command4_bits = 0;
+       devpriv->command4_bits |= EXT_CONVERT_DISABLE_BIT;
+       /* single-ended/differential */
+       if (CR_AREF(insn->chanspec) == AREF_DIFF)
+               devpriv->command4_bits |= ADC_DIFF_BIT;
+       devpriv->write_byte(devpriv->command4_bits, dev->iobase + COMMAND4_REG);
 
-       /* grab our IRQ */
-       if (irq) {
-               isr_flags = 0;
-               if (thisboard->bustype == pci_bustype
-                   || thisboard->bustype == pcmcia_bustype)
-                       isr_flags |= IRQF_SHARED;
-               if (request_irq(irq, labpc_interrupt, isr_flags,
-                               DRV_NAME, dev)) {
-                       dev_err(dev->class_dev, "unable to allocate irq %u\n",
-                               irq);
-                       return -EINVAL;
+       /*
+        * initialize pacer counter output to make sure it doesn't
+        * cause any problems
+        */
+       devpriv->write_byte(INIT_A0_BITS, dev->iobase + COUNTER_A_CONTROL_REG);
+
+       labpc_clear_adc_fifo(dev);
+
+       for (n = 0; n < insn->n; n++) {
+               /* trigger conversion */
+               devpriv->write_byte(0x1, dev->iobase + ADC_CONVERT_REG);
+
+               for (i = 0; i < timeout; i++) {
+                       if (devpriv->read_byte(dev->iobase +
+                                              STATUS1_REG) & DATA_AVAIL_BIT)
+                               break;
+                       udelay(1);
+               }
+               if (i == timeout) {
+                       comedi_error(dev, "timeout");
+                       return -ETIME;
                }
+               lsb = devpriv->read_byte(dev->iobase + ADC_FIFO_REG);
+               msb = devpriv->read_byte(dev->iobase + ADC_FIFO_REG);
+               data[n] = (msb << 8) | lsb;
        }
-       dev->irq = irq;
+
+       return n;
+}
 
 #ifdef CONFIG_ISA_DMA_API
-       /* grab dma channel */
-       if (dma_chan > 3) {
-               dev_err(dev->class_dev, "invalid dma channel %u\n", dma_chan);
-               return -EINVAL;
-       } else if (dma_chan) {
-               /* allocate dma buffer */
-               devpriv->dma_buffer = kmalloc(dma_buffer_size,
-                                             GFP_KERNEL | GFP_DMA);
-               if (devpriv->dma_buffer == NULL)
-                       return -ENOMEM;
+/* utility function that suggests a dma transfer size in bytes */
+static unsigned int labpc_suggest_transfer_size(const struct comedi_cmd *cmd)
+{
+       unsigned int size;
+       unsigned int freq;
 
-               if (request_dma(dma_chan, DRV_NAME)) {
-                       dev_err(dev->class_dev,
-                               "failed to allocate dma channel %u\n",
-                               dma_chan);
-                       return -EINVAL;
-               }
-               devpriv->dma_chan = dma_chan;
-               dma_flags = claim_dma_lock();
-               disable_dma(devpriv->dma_chan);
-               set_dma_mode(devpriv->dma_chan, DMA_MODE_READ);
-               release_dma_lock(dma_flags);
-       }
+       if (cmd->convert_src == TRIG_TIMER)
+               freq = 1000000000 / cmd->convert_arg;
+       /* return some default value */
+       else
+               freq = 0xffffffff;
+
+       /* make buffer fill in no more than 1/3 second */
+       size = (freq / 3) * sample_size;
+
+       /* set a minimum and maximum size allowed */
+       if (size > dma_buffer_size)
+               size = dma_buffer_size - dma_buffer_size % sample_size;
+       else if (size < sample_size)
+               size = sample_size;
+
+       return size;
+}
 #endif
 
-       dev->board_name = thisboard->name;
+static int labpc_use_continuous_mode(const struct comedi_cmd *cmd,
+                                    enum scan_mode mode)
+{
+       if (mode == MODE_SINGLE_CHAN)
+               return 1;
 
-       ret = comedi_alloc_subdevices(dev, 5);
-       if (ret)
-               return ret;
+       if (cmd->scan_begin_src == TRIG_FOLLOW)
+               return 1;
 
-       /* analog input subdevice */
-       s = &dev->subdevices[0];
-       dev->read_subdev = s;
-       s->type = COMEDI_SUBD_AI;
-       s->subdev_flags =
-           SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF | SDF_CMD_READ;
-       s->n_chan = 8;
-       s->len_chanlist = 8;
-       s->maxdata = (1 << 12) - 1;     /* 12 bit resolution */
-       s->range_table = thisboard->ai_range_table;
-       s->do_cmd = labpc_ai_cmd;
-       s->do_cmdtest = labpc_ai_cmdtest;
-       s->insn_read = labpc_ai_rinsn;
-       s->cancel = labpc_cancel;
+       return 0;
+}
 
-       /* analog output */
-       s = &dev->subdevices[1];
-       if (thisboard->has_ao) {
-               /*
-                * Could provide command support, except it only has a
-                * one sample hardware buffer for analog output and no
-                * underrun flag.
-                */
-               s->type = COMEDI_SUBD_AO;
-               s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_GROUND;
-               s->n_chan = NUM_AO_CHAN;
-               s->maxdata = (1 << 12) - 1;     /*  12 bit resolution */
-               s->range_table = &range_labpc_ao;
-               s->insn_read = labpc_ao_rinsn;
-               s->insn_write = labpc_ao_winsn;
-               /* initialize analog outputs to a known value */
-               for (i = 0; i < s->n_chan; i++) {
-                       devpriv->ao_value[i] = s->maxdata / 2;
-                       lsb = devpriv->ao_value[i] & 0xff;
-                       msb = (devpriv->ao_value[i] >> 8) & 0xff;
-                       devpriv->write_byte(lsb, dev->iobase + DAC_LSB_REG(i));
-                       devpriv->write_byte(msb, dev->iobase + DAC_MSB_REG(i));
-               }
-       } else {
-               s->type = COMEDI_SUBD_UNUSED;
-       }
-
-       /* 8255 dio */
-       s = &dev->subdevices[2];
-       /*  if board uses io memory we have to give a custom callback
-        * function to the 8255 driver */
-       if (thisboard->memory_mapped_io)
-               subdev_8255_init(dev, s, labpc_dio_mem_callback,
-                                (unsigned long)(dev->iobase + DIO_BASE_REG));
-       else
-               subdev_8255_init(dev, s, NULL, dev->iobase + DIO_BASE_REG);
-
-       /*  calibration subdevices for boards that have one */
-       s = &dev->subdevices[3];
-       if (thisboard->register_layout == labpc_1200_layout) {
-               s->type = COMEDI_SUBD_CALIB;
-               s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
-               s->n_chan = 16;
-               s->maxdata = 0xff;
-               s->insn_read = labpc_calib_read_insn;
-               s->insn_write = labpc_calib_write_insn;
-
-               for (i = 0; i < s->n_chan; i++)
-                       write_caldac(dev, i, s->maxdata / 2);
-       } else
-               s->type = COMEDI_SUBD_UNUSED;
-
-       /* EEPROM */
-       s = &dev->subdevices[4];
-       if (thisboard->register_layout == labpc_1200_layout) {
-               s->type = COMEDI_SUBD_MEMORY;
-               s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
-               s->n_chan = EEPROM_SIZE;
-               s->maxdata = 0xff;
-               s->insn_read = labpc_eeprom_read_insn;
-               s->insn_write = labpc_eeprom_write_insn;
-
-               for (i = 0; i < EEPROM_SIZE; i++)
-                       devpriv->eeprom_data[i] = labpc_eeprom_read(dev, i);
-       } else
-               s->type = COMEDI_SUBD_UNUSED;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(labpc_common_attach);
-
-static const struct labpc_board_struct *
-labpc_pci_find_boardinfo(struct pci_dev *pcidev)
+static unsigned int labpc_ai_convert_period(const struct comedi_cmd *cmd,
+                                           enum scan_mode mode)
 {
-       unsigned int device_id = pcidev->device;
-       unsigned int n;
+       if (cmd->convert_src != TRIG_TIMER)
+               return 0;
 
-       for (n = 0; n < ARRAY_SIZE(labpc_boards); n++) {
-               const struct labpc_board_struct *board = &labpc_boards[n];
-               if (board->bustype == pci_bustype &&
-                   board->device_id == device_id)
-                       return board;
-       }
-       return NULL;
+       if (mode == MODE_SINGLE_CHAN && cmd->scan_begin_src == TRIG_TIMER)
+               return cmd->scan_begin_arg;
+
+       return cmd->convert_arg;
 }
 
-static int labpc_auto_attach(struct comedi_device *dev,
-                                      unsigned long context_unused)
+static void labpc_set_ai_convert_period(struct comedi_cmd *cmd,
+                                       enum scan_mode mode, unsigned int ns)
 {
-       struct pci_dev *pcidev = comedi_to_pci_dev(dev);
-       struct labpc_private *devpriv;
-       unsigned long iobase;
-       unsigned int irq;
-       int ret;
-
-       if (!IS_ENABLED(CONFIG_COMEDI_PCI_DRIVERS))
-               return -ENODEV;
-
-       devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
-       if (!devpriv)
-               return -ENOMEM;
-       dev->private = devpriv;
+       if (cmd->convert_src != TRIG_TIMER)
+               return;
 
-       dev->board_ptr = labpc_pci_find_boardinfo(pcidev);
-       if (!dev->board_ptr)
-               return -ENODEV;
-       devpriv->mite = mite_alloc(pcidev);
-       if (!devpriv->mite)
-               return -ENOMEM;
-       ret = mite_setup(devpriv->mite);
-       if (ret < 0)
-               return ret;
-       iobase = (unsigned long)devpriv->mite->daq_io_addr;
-       irq = mite_irq(devpriv->mite);
-       return labpc_common_attach(dev, iobase, irq, 0);
+       if (mode == MODE_SINGLE_CHAN &&
+           cmd->scan_begin_src == TRIG_TIMER) {
+               cmd->scan_begin_arg = ns;
+               if (cmd->convert_arg > cmd->scan_begin_arg)
+                       cmd->convert_arg = cmd->scan_begin_arg;
+       } else
+               cmd->convert_arg = ns;
 }
 
-static int labpc_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+static unsigned int labpc_ai_scan_period(const struct comedi_cmd *cmd,
+                                       enum scan_mode mode)
 {
-       struct labpc_private *devpriv;
-       unsigned long iobase = 0;
-       unsigned int irq = 0;
-       unsigned int dma_chan = 0;
-
-       devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
-       if (!devpriv)
-               return -ENOMEM;
-       dev->private = devpriv;
+       if (cmd->scan_begin_src != TRIG_TIMER)
+               return 0;
 
-       /* get base address, irq etc. based on bustype */
-       switch (thisboard->bustype) {
-       case isa_bustype:
-#ifdef CONFIG_ISA_DMA_API
-               iobase = it->options[0];
-               irq = it->options[1];
-               dma_chan = it->options[2];
-#else
-               dev_err(dev->class_dev,
-                       "ni_labpc driver has not been built with ISA DMA support.\n");
-               return -EINVAL;
-#endif
-               break;
-       case pci_bustype:
-#ifdef CONFIG_COMEDI_PCI_DRIVERS
-               dev_err(dev->class_dev,
-                       "manual configuration of PCI board '%s' is not supported\n",
-                       thisboard->name);
-               return -EINVAL;
-#else
-               dev_err(dev->class_dev,
-                       "ni_labpc driver has not been built with PCI support.\n");
-               return -EINVAL;
-#endif
-               break;
-       default:
-               dev_err(dev->class_dev,
-                       "ni_labpc: bug! couldn't determine board type\n");
-               return -EINVAL;
-               break;
-       }
+       if (mode == MODE_SINGLE_CHAN && cmd->convert_src == TRIG_TIMER)
+               return 0;
 
-       return labpc_common_attach(dev, iobase, irq, dma_chan);
+       return cmd->scan_begin_arg;
 }
 
-void labpc_common_detach(struct comedi_device *dev)
+static void labpc_set_ai_scan_period(struct comedi_cmd *cmd,
+                                    enum scan_mode mode, unsigned int ns)
 {
-       struct labpc_private *devpriv = dev->private;
-       struct comedi_subdevice *s;
-
-       if (!thisboard)
+       if (cmd->scan_begin_src != TRIG_TIMER)
                return;
-       if (dev->subdevices) {
-               s = &dev->subdevices[2];
-               subdev_8255_cleanup(dev, s);
-       }
-#ifdef CONFIG_ISA_DMA_API
-       /* only free stuff if it has been allocated by _attach */
-       kfree(devpriv->dma_buffer);
-       if (devpriv->dma_chan)
-               free_dma(devpriv->dma_chan);
-#endif
-       if (dev->irq)
-               free_irq(dev->irq, dev);
-       if (thisboard->bustype == isa_bustype && dev->iobase)
-               release_region(dev->iobase, LABPC_SIZE);
-#ifdef CONFIG_COMEDI_PCI_DRIVERS
-       if (devpriv->mite) {
-               mite_unsetup(devpriv->mite);
-               mite_free(devpriv->mite);
-       }
-#endif
-};
-EXPORT_SYMBOL_GPL(labpc_common_detach);
 
-static void labpc_clear_adc_fifo(const struct comedi_device *dev)
-{
-       struct labpc_private *devpriv = dev->private;
+       if (mode == MODE_SINGLE_CHAN && cmd->convert_src == TRIG_TIMER)
+               return;
 
-       devpriv->write_byte(0x1, dev->iobase + ADC_CLEAR_REG);
-       devpriv->read_byte(dev->iobase + ADC_FIFO_REG);
-       devpriv->read_byte(dev->iobase + ADC_FIFO_REG);
+       cmd->scan_begin_arg = ns;
 }
 
-static int labpc_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+/* figures out what counter values to use based on command */
+static void labpc_adc_timing(struct comedi_device *dev, struct comedi_cmd *cmd,
+                            enum scan_mode mode)
 {
        struct labpc_private *devpriv = dev->private;
-       unsigned long flags;
-
-       spin_lock_irqsave(&dev->spinlock, flags);
-       devpriv->command2_bits &= ~SWTRIG_BIT & ~HWTRIG_BIT & ~PRETRIG_BIT;
-       devpriv->write_byte(devpriv->command2_bits, dev->iobase + COMMAND2_REG);
-       spin_unlock_irqrestore(&dev->spinlock, flags);
-
-       devpriv->command3_bits = 0;
-       devpriv->write_byte(devpriv->command3_bits, dev->iobase + COMMAND3_REG);
-
-       return 0;
-}
-
-static enum scan_mode labpc_ai_scan_mode(const struct comedi_cmd *cmd)
-{
-       if (cmd->chanlist_len == 1)
-               return MODE_SINGLE_CHAN;
-
-       /* chanlist may be NULL during cmdtest. */
-       if (cmd->chanlist == NULL)
-               return MODE_MULT_CHAN_UP;
-
-       if (CR_CHAN(cmd->chanlist[0]) == CR_CHAN(cmd->chanlist[1]))
-               return MODE_SINGLE_CHAN_INTERVAL;
+       /* max value for 16 bit counter in mode 2 */
+       const int max_counter_value = 0x10000;
+       /* min value for 16 bit counter in mode 2 */
+       const int min_counter_value = 2;
+       unsigned int base_period;
+       unsigned int scan_period;
+       unsigned int convert_period;
 
-       if (CR_CHAN(cmd->chanlist[0]) < CR_CHAN(cmd->chanlist[1]))
-               return MODE_MULT_CHAN_UP;
+       /*
+        * if both convert and scan triggers are TRIG_TIMER, then they
+        * both rely on counter b0
+        */
+       convert_period = labpc_ai_convert_period(cmd, mode);
+       scan_period = labpc_ai_scan_period(cmd, mode);
+       if (convert_period && scan_period) {
+               /*
+                * pick the lowest b0 divisor value we can (for maximum input
+                * clock speed on convert and scan counters)
+                */
+               devpriv->divisor_b0 = (scan_period - 1) /
+                   (LABPC_TIMER_BASE * max_counter_value) + 1;
+               if (devpriv->divisor_b0 < min_counter_value)
+                       devpriv->divisor_b0 = min_counter_value;
+               if (devpriv->divisor_b0 > max_counter_value)
+                       devpriv->divisor_b0 = max_counter_value;
 
-       if (CR_CHAN(cmd->chanlist[0]) > CR_CHAN(cmd->chanlist[1]))
-               return MODE_MULT_CHAN_DOWN;
+               base_period = LABPC_TIMER_BASE * devpriv->divisor_b0;
 
-       pr_err("ni_labpc: bug! cannot determine AI scan mode\n");
-       return 0;
+               /*  set a0 for conversion frequency and b1 for scan frequency */
+               switch (cmd->flags & TRIG_ROUND_MASK) {
+               default:
+               case TRIG_ROUND_NEAREST:
+                       devpriv->divisor_a0 =
+                           (convert_period + (base_period / 2)) / base_period;
+                       devpriv->divisor_b1 =
+                           (scan_period + (base_period / 2)) / base_period;
+                       break;
+               case TRIG_ROUND_UP:
+                       devpriv->divisor_a0 =
+                           (convert_period + (base_period - 1)) / base_period;
+                       devpriv->divisor_b1 =
+                           (scan_period + (base_period - 1)) / base_period;
+                       break;
+               case TRIG_ROUND_DOWN:
+                       devpriv->divisor_a0 = convert_period / base_period;
+                       devpriv->divisor_b1 = scan_period / base_period;
+                       break;
+               }
+               /*  make sure a0 and b1 values are acceptable */
+               if (devpriv->divisor_a0 < min_counter_value)
+                       devpriv->divisor_a0 = min_counter_value;
+               if (devpriv->divisor_a0 > max_counter_value)
+                       devpriv->divisor_a0 = max_counter_value;
+               if (devpriv->divisor_b1 < min_counter_value)
+                       devpriv->divisor_b1 = min_counter_value;
+               if (devpriv->divisor_b1 > max_counter_value)
+                       devpriv->divisor_b1 = max_counter_value;
+               /*  write corrected timings to command */
+               labpc_set_ai_convert_period(cmd, mode,
+                                           base_period * devpriv->divisor_a0);
+               labpc_set_ai_scan_period(cmd, mode,
+                                        base_period * devpriv->divisor_b1);
+               /*
+                * if only one TRIG_TIMER is used, we can employ the generic
+                * cascaded timing functions
+                */
+       } else if (scan_period) {
+               /*
+                * calculate cascaded counter values
+                * that give desired scan timing
+                */
+               i8253_cascade_ns_to_timer_2div(LABPC_TIMER_BASE,
+                                              &(devpriv->divisor_b1),
+                                              &(devpriv->divisor_b0),
+                                              &scan_period,
+                                              cmd->flags & TRIG_ROUND_MASK);
+               labpc_set_ai_scan_period(cmd, mode, scan_period);
+       } else if (convert_period) {
+               /*
+                * calculate cascaded counter values
+                * that give desired conversion timing
+                */
+               i8253_cascade_ns_to_timer_2div(LABPC_TIMER_BASE,
+                                              &(devpriv->divisor_a0),
+                                              &(devpriv->divisor_b0),
+                                              &convert_period,
+                                              cmd->flags & TRIG_ROUND_MASK);
+               labpc_set_ai_convert_period(cmd, mode, convert_period);
+       }
+}
+
+static enum scan_mode labpc_ai_scan_mode(const struct comedi_cmd *cmd)
+{
+       if (cmd->chanlist_len == 1)
+               return MODE_SINGLE_CHAN;
+
+       /* chanlist may be NULL during cmdtest. */
+       if (cmd->chanlist == NULL)
+               return MODE_MULT_CHAN_UP;
+
+       if (CR_CHAN(cmd->chanlist[0]) == CR_CHAN(cmd->chanlist[1]))
+               return MODE_SINGLE_CHAN_INTERVAL;
+
+       if (CR_CHAN(cmd->chanlist[0]) < CR_CHAN(cmd->chanlist[1]))
+               return MODE_MULT_CHAN_UP;
+
+       if (CR_CHAN(cmd->chanlist[0]) > CR_CHAN(cmd->chanlist[1]))
+               return MODE_MULT_CHAN_DOWN;
+
+       pr_err("ni_labpc: bug! cannot determine AI scan mode\n");
+       return 0;
 }
 
 static int labpc_ai_chanlist_invalid(const struct comedi_device *dev,
@@ -923,72 +801,10 @@ static int labpc_ai_chanlist_invalid(const struct comedi_device *dev,
        return 0;
 }
 
-static int labpc_use_continuous_mode(const struct comedi_cmd *cmd,
-                                    enum scan_mode mode)
-{
-       if (mode == MODE_SINGLE_CHAN)
-               return 1;
-
-       if (cmd->scan_begin_src == TRIG_FOLLOW)
-               return 1;
-
-       return 0;
-}
-
-static unsigned int labpc_ai_convert_period(const struct comedi_cmd *cmd,
-                                           enum scan_mode mode)
-{
-       if (cmd->convert_src != TRIG_TIMER)
-               return 0;
-
-       if (mode == MODE_SINGLE_CHAN && cmd->scan_begin_src == TRIG_TIMER)
-               return cmd->scan_begin_arg;
-
-       return cmd->convert_arg;
-}
-
-static void labpc_set_ai_convert_period(struct comedi_cmd *cmd,
-                                       enum scan_mode mode, unsigned int ns)
-{
-       if (cmd->convert_src != TRIG_TIMER)
-               return;
-
-       if (mode == MODE_SINGLE_CHAN &&
-           cmd->scan_begin_src == TRIG_TIMER) {
-               cmd->scan_begin_arg = ns;
-               if (cmd->convert_arg > cmd->scan_begin_arg)
-                       cmd->convert_arg = cmd->scan_begin_arg;
-       } else
-               cmd->convert_arg = ns;
-}
-
-static unsigned int labpc_ai_scan_period(const struct comedi_cmd *cmd,
-                                       enum scan_mode mode)
-{
-       if (cmd->scan_begin_src != TRIG_TIMER)
-               return 0;
-
-       if (mode == MODE_SINGLE_CHAN && cmd->convert_src == TRIG_TIMER)
-               return 0;
-
-       return cmd->scan_begin_arg;
-}
-
-static void labpc_set_ai_scan_period(struct comedi_cmd *cmd,
-                                    enum scan_mode mode, unsigned int ns)
-{
-       if (cmd->scan_begin_src != TRIG_TIMER)
-               return;
-
-       if (mode == MODE_SINGLE_CHAN && cmd->convert_src == TRIG_TIMER)
-               return;
-
-       cmd->scan_begin_arg = ns;
-}
-
 static int labpc_ai_cmdtest(struct comedi_device *dev,
                            struct comedi_subdevice *s, struct comedi_cmd *cmd)
 {
+       const struct labpc_boardinfo *thisboard = comedi_board(dev);
        int err = 0;
        int tmp, tmp2;
        unsigned int stop_mask;
@@ -1084,8 +900,23 @@ static int labpc_ai_cmdtest(struct comedi_device *dev,
        return 0;
 }
 
+static inline int labpc_counter_load(struct comedi_device *dev,
+                                    unsigned long base_address,
+                                    unsigned int counter_number,
+                                    unsigned int count, unsigned int mode)
+{
+       const struct labpc_boardinfo *thisboard = comedi_board(dev);
+
+       if (thisboard->memory_mapped_io)
+               return i8254_mm_load((void __iomem *)base_address, 0,
+                                    counter_number, count, mode);
+       else
+               return i8254_load(base_address, 0, counter_number, count, mode);
+}
+
 static int labpc_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 {
+       const struct labpc_boardinfo *thisboard = comedi_board(dev);
        struct labpc_private *devpriv = dev->private;
        int channel, range, aref;
 #ifdef CONFIG_ISA_DMA_API
@@ -1352,95 +1183,89 @@ static int labpc_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
        return 0;
 }
 
-/* interrupt service routine */
-static irqreturn_t labpc_interrupt(int irq, void *d)
+static int labpc_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
 {
-       struct comedi_device *dev = d;
        struct labpc_private *devpriv = dev->private;
-       struct comedi_subdevice *s = dev->read_subdev;
-       struct comedi_async *async;
-       struct comedi_cmd *cmd;
+       unsigned long flags;
 
-       if (dev->attached == 0) {
-               comedi_error(dev, "premature interrupt");
-               return IRQ_HANDLED;
-       }
+       spin_lock_irqsave(&dev->spinlock, flags);
+       devpriv->command2_bits &= ~SWTRIG_BIT & ~HWTRIG_BIT & ~PRETRIG_BIT;
+       devpriv->write_byte(devpriv->command2_bits, dev->iobase + COMMAND2_REG);
+       spin_unlock_irqrestore(&dev->spinlock, flags);
 
-       async = s->async;
-       cmd = &async->cmd;
-       async->events = 0;
+       devpriv->command3_bits = 0;
+       devpriv->write_byte(devpriv->command3_bits, dev->iobase + COMMAND3_REG);
 
-       /* read board status */
-       devpriv->status1_bits = devpriv->read_byte(dev->iobase + STATUS1_REG);
-       if (thisboard->register_layout == labpc_1200_layout)
-               devpriv->status2_bits =
-                   devpriv->read_byte(dev->iobase + STATUS2_REG);
+       return 0;
+}
 
-       if ((devpriv->status1_bits & (DMATC_BIT | TIMER_BIT | OVERFLOW_BIT |
-                                     OVERRUN_BIT | DATA_AVAIL_BIT)) == 0
-           && (devpriv->status2_bits & A1_TC_BIT) == 0
-           && (devpriv->status2_bits & FNHF_BIT)) {
-               return IRQ_NONE;
-       }
+#ifdef CONFIG_ISA_DMA_API
+static void labpc_drain_dma(struct comedi_device *dev)
+{
+       struct labpc_private *devpriv = dev->private;
+       struct comedi_subdevice *s = dev->read_subdev;
+       struct comedi_async *async = s->async;
+       int status;
+       unsigned long flags;
+       unsigned int max_points, num_points, residue, leftover;
+       int i;
 
-       if (devpriv->status1_bits & OVERRUN_BIT) {
-               /* clear error interrupt */
-               devpriv->write_byte(0x1, dev->iobase + ADC_CLEAR_REG);
-               async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
-               comedi_event(dev, s);
-               comedi_error(dev, "overrun");
-               return IRQ_HANDLED;
-       }
+       status = devpriv->status1_bits;
 
-#ifdef CONFIG_ISA_DMA_API
-       if (devpriv->current_transfer == isa_dma_transfer) {
-               /*
-                * if a dma terminal count of external stop trigger
-                * has occurred
-                */
-               if (devpriv->status1_bits & DMATC_BIT ||
-                   (thisboard->register_layout == labpc_1200_layout
-                    && devpriv->status2_bits & A1_TC_BIT)) {
-                       handle_isa_dma(dev);
-               }
-       } else
-#endif
-               labpc_drain_fifo(dev);
+       flags = claim_dma_lock();
+       disable_dma(devpriv->dma_chan);
+       /* clear flip-flop to make sure 2-byte registers for
+        * count and address get set correctly */
+       clear_dma_ff(devpriv->dma_chan);
 
-       if (devpriv->status1_bits & TIMER_BIT) {
-               comedi_error(dev, "handled timer interrupt?");
-               /*  clear it */
-               devpriv->write_byte(0x1, dev->iobase + TIMER_CLEAR_REG);
+       /*  figure out how many points to read */
+       max_points = devpriv->dma_transfer_size / sample_size;
+       /* 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_chan) / sample_size;
+       num_points = max_points - residue;
+       if (devpriv->count < num_points && async->cmd.stop_src == TRIG_COUNT)
+               num_points = devpriv->count;
+
+       /*  figure out how many points will be stored next time */
+       leftover = 0;
+       if (async->cmd.stop_src != TRIG_COUNT) {
+               leftover = devpriv->dma_transfer_size / sample_size;
+       } else if (devpriv->count > num_points) {
+               leftover = devpriv->count - num_points;
+               if (leftover > max_points)
+                       leftover = max_points;
        }
 
-       if (devpriv->status1_bits & OVERFLOW_BIT) {
-               /*  clear error interrupt */
-               devpriv->write_byte(0x1, dev->iobase + ADC_CLEAR_REG);
-               async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
-               comedi_event(dev, s);
-               comedi_error(dev, "overflow");
-               return IRQ_HANDLED;
-       }
-       /*  handle external stop trigger */
-       if (cmd->stop_src == TRIG_EXT) {
-               if (devpriv->status2_bits & A1_TC_BIT) {
-                       labpc_drain_dregs(dev);
-                       labpc_cancel(dev, s);
-                       async->events |= COMEDI_CB_EOA;
-               }
-       }
+       /* write data to comedi buffer */
+       for (i = 0; i < num_points; i++)
+               cfc_write_to_buffer(s, devpriv->dma_buffer[i]);
 
-       /* TRIG_COUNT end of acquisition */
-       if (cmd->stop_src == TRIG_COUNT) {
-               if (devpriv->count == 0) {
-                       labpc_cancel(dev, s);
-                       async->events |= COMEDI_CB_EOA;
-               }
-       }
+       if (async->cmd.stop_src == TRIG_COUNT)
+               devpriv->count -= num_points;
 
-       comedi_event(dev, s);
-       return IRQ_HANDLED;
+       /*  set address and count for next transfer */
+       set_dma_addr(devpriv->dma_chan, virt_to_bus(devpriv->dma_buffer));
+       set_dma_count(devpriv->dma_chan, leftover * sample_size);
+       release_dma_lock(flags);
+
+       async->events |= COMEDI_CB_BLOCK;
+}
+
+static void handle_isa_dma(struct comedi_device *dev)
+{
+       struct labpc_private *devpriv = dev->private;
+
+       labpc_drain_dma(dev);
+
+       enable_dma(devpriv->dma_chan);
+
+       /*  clear dma tc interrupt */
+       devpriv->write_byte(0x1, dev->iobase + DMATC_CLEAR_REG);
 }
+#endif
 
 /* read all available samples from ai fifo */
 static int labpc_drain_fifo(struct comedi_device *dev)
@@ -1478,204 +1303,139 @@ static int labpc_drain_fifo(struct comedi_device *dev)
        return 0;
 }
 
-#ifdef CONFIG_ISA_DMA_API
-static void labpc_drain_dma(struct comedi_device *dev)
+/* makes sure all data acquired by board is transferred to comedi (used
+ * when acquisition is terminated by stop_src == TRIG_EXT). */
+static void labpc_drain_dregs(struct comedi_device *dev)
 {
+#ifdef CONFIG_ISA_DMA_API
        struct labpc_private *devpriv = dev->private;
-       struct comedi_subdevice *s = dev->read_subdev;
-       struct comedi_async *async = s->async;
-       int status;
-       unsigned long flags;
-       unsigned int max_points, num_points, residue, leftover;
-       int i;
 
-       status = devpriv->status1_bits;
+       if (devpriv->current_transfer == isa_dma_transfer)
+               labpc_drain_dma(dev);
+#endif
 
-       flags = claim_dma_lock();
-       disable_dma(devpriv->dma_chan);
-       /* clear flip-flop to make sure 2-byte registers for
-        * count and address get set correctly */
-       clear_dma_ff(devpriv->dma_chan);
+       labpc_drain_fifo(dev);
+}
 
-       /*  figure out how many points to read */
-       max_points = devpriv->dma_transfer_size / sample_size;
-       /* 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_chan) / sample_size;
-       num_points = max_points - residue;
-       if (devpriv->count < num_points && async->cmd.stop_src == TRIG_COUNT)
-               num_points = devpriv->count;
+/* interrupt service routine */
+static irqreturn_t labpc_interrupt(int irq, void *d)
+{
+       struct comedi_device *dev = d;
+       const struct labpc_boardinfo *thisboard = comedi_board(dev);
+       struct labpc_private *devpriv = dev->private;
+       struct comedi_subdevice *s = dev->read_subdev;
+       struct comedi_async *async;
+       struct comedi_cmd *cmd;
 
-       /*  figure out how many points will be stored next time */
-       leftover = 0;
-       if (async->cmd.stop_src != TRIG_COUNT) {
-               leftover = devpriv->dma_transfer_size / sample_size;
-       } else if (devpriv->count > num_points) {
-               leftover = devpriv->count - num_points;
-               if (leftover > max_points)
-                       leftover = max_points;
+       if (!dev->attached) {
+               comedi_error(dev, "premature interrupt");
+               return IRQ_HANDLED;
        }
 
-       /* write data to comedi buffer */
-       for (i = 0; i < num_points; i++)
-               cfc_write_to_buffer(s, devpriv->dma_buffer[i]);
-
-       if (async->cmd.stop_src == TRIG_COUNT)
-               devpriv->count -= num_points;
-
-       /*  set address and count for next transfer */
-       set_dma_addr(devpriv->dma_chan, virt_to_bus(devpriv->dma_buffer));
-       set_dma_count(devpriv->dma_chan, leftover * sample_size);
-       release_dma_lock(flags);
-
-       async->events |= COMEDI_CB_BLOCK;
-}
+       async = s->async;
+       cmd = &async->cmd;
+       async->events = 0;
 
-static void handle_isa_dma(struct comedi_device *dev)
-{
-       struct labpc_private *devpriv = dev->private;
+       /* read board status */
+       devpriv->status1_bits = devpriv->read_byte(dev->iobase + STATUS1_REG);
+       if (thisboard->register_layout == labpc_1200_layout)
+               devpriv->status2_bits =
+                   devpriv->read_byte(dev->iobase + STATUS2_REG);
 
-       labpc_drain_dma(dev);
+       if ((devpriv->status1_bits & (DMATC_BIT | TIMER_BIT | OVERFLOW_BIT |
+                                     OVERRUN_BIT | DATA_AVAIL_BIT)) == 0
+           && (devpriv->status2_bits & A1_TC_BIT) == 0
+           && (devpriv->status2_bits & FNHF_BIT)) {
+               return IRQ_NONE;
+       }
 
-       enable_dma(devpriv->dma_chan);
+       if (devpriv->status1_bits & OVERRUN_BIT) {
+               /* clear error interrupt */
+               devpriv->write_byte(0x1, dev->iobase + ADC_CLEAR_REG);
+               async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+               comedi_event(dev, s);
+               comedi_error(dev, "overrun");
+               return IRQ_HANDLED;
+       }
 
-       /*  clear dma tc interrupt */
-       devpriv->write_byte(0x1, dev->iobase + DMATC_CLEAR_REG);
-}
+#ifdef CONFIG_ISA_DMA_API
+       if (devpriv->current_transfer == isa_dma_transfer) {
+               /*
+                * if a dma terminal count of external stop trigger
+                * has occurred
+                */
+               if (devpriv->status1_bits & DMATC_BIT ||
+                   (thisboard->register_layout == labpc_1200_layout
+                    && devpriv->status2_bits & A1_TC_BIT)) {
+                       handle_isa_dma(dev);
+               }
+       } else
 #endif
+               labpc_drain_fifo(dev);
 
-/* makes sure all data acquired by board is transferred to comedi (used
- * when acquisition is terminated by stop_src == TRIG_EXT). */
-static void labpc_drain_dregs(struct comedi_device *dev)
-{
-#ifdef CONFIG_ISA_DMA_API
-       struct labpc_private *devpriv = dev->private;
+       if (devpriv->status1_bits & TIMER_BIT) {
+               comedi_error(dev, "handled timer interrupt?");
+               /*  clear it */
+               devpriv->write_byte(0x1, dev->iobase + TIMER_CLEAR_REG);
+       }
 
-       if (devpriv->current_transfer == isa_dma_transfer)
-               labpc_drain_dma(dev);
-#endif
+       if (devpriv->status1_bits & OVERFLOW_BIT) {
+               /*  clear error interrupt */
+               devpriv->write_byte(0x1, dev->iobase + ADC_CLEAR_REG);
+               async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+               comedi_event(dev, s);
+               comedi_error(dev, "overflow");
+               return IRQ_HANDLED;
+       }
+       /*  handle external stop trigger */
+       if (cmd->stop_src == TRIG_EXT) {
+               if (devpriv->status2_bits & A1_TC_BIT) {
+                       labpc_drain_dregs(dev);
+                       labpc_cancel(dev, s);
+                       async->events |= COMEDI_CB_EOA;
+               }
+       }
 
-       labpc_drain_fifo(dev);
+       /* TRIG_COUNT end of acquisition */
+       if (cmd->stop_src == TRIG_COUNT) {
+               if (devpriv->count == 0) {
+                       labpc_cancel(dev, s);
+                       async->events |= COMEDI_CB_EOA;
+               }
+       }
+
+       comedi_event(dev, s);
+       return IRQ_HANDLED;
 }
 
-static int labpc_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
+/* analog output insn */
+static int labpc_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
                          struct comedi_insn *insn, unsigned int *data)
 {
+       const struct labpc_boardinfo *thisboard = comedi_board(dev);
        struct labpc_private *devpriv = dev->private;
-       int i, n;
-       int chan, range;
-       int lsb, msb;
-       int timeout = 1000;
+       int channel, range;
        unsigned long flags;
+       int lsb, msb;
 
-       /*  disable timed conversions */
+       channel = CR_CHAN(insn->chanspec);
+
+       /* turn off pacing of analog output channel */
+       /* note: hardware bug in daqcard-1200 means pacing cannot
+        * be independently enabled/disabled for its the two channels */
        spin_lock_irqsave(&dev->spinlock, flags);
-       devpriv->command2_bits &= ~SWTRIG_BIT & ~HWTRIG_BIT & ~PRETRIG_BIT;
+       devpriv->command2_bits &= ~DAC_PACED_BIT(channel);
        devpriv->write_byte(devpriv->command2_bits, dev->iobase + COMMAND2_REG);
        spin_unlock_irqrestore(&dev->spinlock, flags);
 
-       /*  disable interrupt generation and dma */
-       devpriv->command3_bits = 0;
-       devpriv->write_byte(devpriv->command3_bits, dev->iobase + COMMAND3_REG);
-
-       /* set gain and channel */
-       devpriv->command1_bits = 0;
-       chan = CR_CHAN(insn->chanspec);
-       range = CR_RANGE(insn->chanspec);
-       devpriv->command1_bits |= thisboard->ai_range_code[range];
-       /* munge channel bits for differential/scan disabled mode */
-       if (CR_AREF(insn->chanspec) == AREF_DIFF)
-               chan *= 2;
-       devpriv->command1_bits |= ADC_CHAN_BITS(chan);
-       devpriv->write_byte(devpriv->command1_bits, dev->iobase + COMMAND1_REG);
-
-       /* setup command6 register for 1200 boards */
+       /* set range */
        if (thisboard->register_layout == labpc_1200_layout) {
-               /*  reference inputs to ground or common? */
-               if (CR_AREF(insn->chanspec) != AREF_GROUND)
-                       devpriv->command6_bits |= ADC_COMMON_BIT;
-               else
-                       devpriv->command6_bits &= ~ADC_COMMON_BIT;
-               /* bipolar or unipolar range? */
-               if (thisboard->ai_range_is_unipolar[range])
-                       devpriv->command6_bits |= ADC_UNIP_BIT;
+               range = CR_RANGE(insn->chanspec);
+               if (range & AO_RANGE_IS_UNIPOLAR)
+                       devpriv->command6_bits |= DAC_UNIP_BIT(channel);
                else
-                       devpriv->command6_bits &= ~ADC_UNIP_BIT;
-               /* don't interrupt on fifo half full */
-               devpriv->command6_bits &= ~ADC_FHF_INTR_EN_BIT;
-               /* don't enable interrupt on counter a1 terminal count? */
-               devpriv->command6_bits &= ~A1_INTR_EN_BIT;
-               /* write to register */
-               devpriv->write_byte(devpriv->command6_bits,
-                                   dev->iobase + COMMAND6_REG);
-       }
-       /* setup command4 register */
-       devpriv->command4_bits = 0;
-       devpriv->command4_bits |= EXT_CONVERT_DISABLE_BIT;
-       /* single-ended/differential */
-       if (CR_AREF(insn->chanspec) == AREF_DIFF)
-               devpriv->command4_bits |= ADC_DIFF_BIT;
-       devpriv->write_byte(devpriv->command4_bits, dev->iobase + COMMAND4_REG);
-
-       /*
-        * initialize pacer counter output to make sure it doesn't
-        * cause any problems
-        */
-       devpriv->write_byte(INIT_A0_BITS, dev->iobase + COUNTER_A_CONTROL_REG);
-
-       labpc_clear_adc_fifo(dev);
-
-       for (n = 0; n < insn->n; n++) {
-               /* trigger conversion */
-               devpriv->write_byte(0x1, dev->iobase + ADC_CONVERT_REG);
-
-               for (i = 0; i < timeout; i++) {
-                       if (devpriv->read_byte(dev->iobase +
-                                              STATUS1_REG) & DATA_AVAIL_BIT)
-                               break;
-                       udelay(1);
-               }
-               if (i == timeout) {
-                       comedi_error(dev, "timeout");
-                       return -ETIME;
-               }
-               lsb = devpriv->read_byte(dev->iobase + ADC_FIFO_REG);
-               msb = devpriv->read_byte(dev->iobase + ADC_FIFO_REG);
-               data[n] = (msb << 8) | lsb;
-       }
-
-       return n;
-}
-
-/* analog output insn */
-static int labpc_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
-                         struct comedi_insn *insn, unsigned int *data)
-{
-       struct labpc_private *devpriv = dev->private;
-       int channel, range;
-       unsigned long flags;
-       int lsb, msb;
-
-       channel = CR_CHAN(insn->chanspec);
-
-       /* turn off pacing of analog output channel */
-       /* note: hardware bug in daqcard-1200 means pacing cannot
-        * be independently enabled/disabled for its the two channels */
-       spin_lock_irqsave(&dev->spinlock, flags);
-       devpriv->command2_bits &= ~DAC_PACED_BIT(channel);
-       devpriv->write_byte(devpriv->command2_bits, dev->iobase + COMMAND2_REG);
-       spin_unlock_irqrestore(&dev->spinlock, flags);
-
-       /* set range */
-       if (thisboard->register_layout == labpc_1200_layout) {
-               range = CR_RANGE(insn->chanspec);
-               if (range & AO_RANGE_IS_UNIPOLAR)
-                       devpriv->command6_bits |= DAC_UNIP_BIT(channel);
-               else
-                       devpriv->command6_bits &= ~DAC_UNIP_BIT(channel);
-               /*  write to register */
+                       devpriv->command6_bits &= ~DAC_UNIP_BIT(channel);
+               /*  write to register */
                devpriv->write_byte(devpriv->command6_bits,
                                    dev->iobase + COMMAND6_REG);
        }
@@ -1702,181 +1462,6 @@ static int labpc_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
        return 1;
 }
 
-static int labpc_calib_read_insn(struct comedi_device *dev,
-                                struct comedi_subdevice *s,
-                                struct comedi_insn *insn, unsigned int *data)
-{
-       struct labpc_private *devpriv = dev->private;
-
-       data[0] = devpriv->caldac[CR_CHAN(insn->chanspec)];
-
-       return 1;
-}
-
-static int labpc_calib_write_insn(struct comedi_device *dev,
-                                 struct comedi_subdevice *s,
-                                 struct comedi_insn *insn, unsigned int *data)
-{
-       int channel = CR_CHAN(insn->chanspec);
-
-       write_caldac(dev, channel, data[0]);
-       return 1;
-}
-
-static int labpc_eeprom_read_insn(struct comedi_device *dev,
-                                 struct comedi_subdevice *s,
-                                 struct comedi_insn *insn, unsigned int *data)
-{
-       struct labpc_private *devpriv = dev->private;
-
-       data[0] = devpriv->eeprom_data[CR_CHAN(insn->chanspec)];
-
-       return 1;
-}
-
-static int labpc_eeprom_write_insn(struct comedi_device *dev,
-                                  struct comedi_subdevice *s,
-                                  struct comedi_insn *insn, unsigned int *data)
-{
-       int channel = CR_CHAN(insn->chanspec);
-       int ret;
-
-       /*  only allow writes to user area of eeprom */
-       if (channel < 16 || channel > 127) {
-               dev_dbg(dev->class_dev,
-                       "eeprom writes are only allowed to channels 16 through 127 (the pointer and user areas)\n");
-               return -EINVAL;
-       }
-
-       ret = labpc_eeprom_write(dev, channel, data[0]);
-       if (ret < 0)
-               return ret;
-
-       return 1;
-}
-
-#ifdef CONFIG_ISA_DMA_API
-/* utility function that suggests a dma transfer size in bytes */
-static unsigned int labpc_suggest_transfer_size(const struct comedi_cmd *cmd)
-{
-       unsigned int size;
-       unsigned int freq;
-
-       if (cmd->convert_src == TRIG_TIMER)
-               freq = 1000000000 / cmd->convert_arg;
-       /* return some default value */
-       else
-               freq = 0xffffffff;
-
-       /* make buffer fill in no more than 1/3 second */
-       size = (freq / 3) * sample_size;
-
-       /* set a minimum and maximum size allowed */
-       if (size > dma_buffer_size)
-               size = dma_buffer_size - dma_buffer_size % sample_size;
-       else if (size < sample_size)
-               size = sample_size;
-
-       return size;
-}
-#endif
-
-/* figures out what counter values to use based on command */
-static void labpc_adc_timing(struct comedi_device *dev, struct comedi_cmd *cmd,
-                            enum scan_mode mode)
-{
-       struct labpc_private *devpriv = dev->private;
-       /* max value for 16 bit counter in mode 2 */
-       const int max_counter_value = 0x10000;
-       /* min value for 16 bit counter in mode 2 */
-       const int min_counter_value = 2;
-       unsigned int base_period;
-       unsigned int scan_period;
-       unsigned int convert_period;
-
-       /*
-        * if both convert and scan triggers are TRIG_TIMER, then they
-        * both rely on counter b0
-        */
-       convert_period = labpc_ai_convert_period(cmd, mode);
-       scan_period = labpc_ai_scan_period(cmd, mode);
-       if (convert_period && scan_period) {
-               /*
-                * pick the lowest b0 divisor value we can (for maximum input
-                * clock speed on convert and scan counters)
-                */
-               devpriv->divisor_b0 = (scan_period - 1) /
-                   (LABPC_TIMER_BASE * max_counter_value) + 1;
-               if (devpriv->divisor_b0 < min_counter_value)
-                       devpriv->divisor_b0 = min_counter_value;
-               if (devpriv->divisor_b0 > max_counter_value)
-                       devpriv->divisor_b0 = max_counter_value;
-
-               base_period = LABPC_TIMER_BASE * devpriv->divisor_b0;
-
-               /*  set a0 for conversion frequency and b1 for scan frequency */
-               switch (cmd->flags & TRIG_ROUND_MASK) {
-               default:
-               case TRIG_ROUND_NEAREST:
-                       devpriv->divisor_a0 =
-                           (convert_period + (base_period / 2)) / base_period;
-                       devpriv->divisor_b1 =
-                           (scan_period + (base_period / 2)) / base_period;
-                       break;
-               case TRIG_ROUND_UP:
-                       devpriv->divisor_a0 =
-                           (convert_period + (base_period - 1)) / base_period;
-                       devpriv->divisor_b1 =
-                           (scan_period + (base_period - 1)) / base_period;
-                       break;
-               case TRIG_ROUND_DOWN:
-                       devpriv->divisor_a0 = convert_period / base_period;
-                       devpriv->divisor_b1 = scan_period / base_period;
-                       break;
-               }
-               /*  make sure a0 and b1 values are acceptable */
-               if (devpriv->divisor_a0 < min_counter_value)
-                       devpriv->divisor_a0 = min_counter_value;
-               if (devpriv->divisor_a0 > max_counter_value)
-                       devpriv->divisor_a0 = max_counter_value;
-               if (devpriv->divisor_b1 < min_counter_value)
-                       devpriv->divisor_b1 = min_counter_value;
-               if (devpriv->divisor_b1 > max_counter_value)
-                       devpriv->divisor_b1 = max_counter_value;
-               /*  write corrected timings to command */
-               labpc_set_ai_convert_period(cmd, mode,
-                                           base_period * devpriv->divisor_a0);
-               labpc_set_ai_scan_period(cmd, mode,
-                                        base_period * devpriv->divisor_b1);
-               /*
-                * if only one TRIG_TIMER is used, we can employ the generic
-                * cascaded timing functions
-                */
-       } else if (scan_period) {
-               /*
-                * calculate cascaded counter values
-                * that give desired scan timing
-                */
-               i8253_cascade_ns_to_timer_2div(LABPC_TIMER_BASE,
-                                              &(devpriv->divisor_b1),
-                                              &(devpriv->divisor_b0),
-                                              &scan_period,
-                                              cmd->flags & TRIG_ROUND_MASK);
-               labpc_set_ai_scan_period(cmd, mode, scan_period);
-       } else if (convert_period) {
-               /*
-                * calculate cascaded counter values
-                * that give desired conversion timing
-                */
-               i8253_cascade_ns_to_timer_2div(LABPC_TIMER_BASE,
-                                              &(devpriv->divisor_a0),
-                                              &(devpriv->divisor_b0),
-                                              &convert_period,
-                                              cmd->flags & TRIG_ROUND_MASK);
-               labpc_set_ai_convert_period(cmd, mode, convert_period);
-       }
-}
-
 static int labpc_dio_mem_callback(int dir, int port, int data,
                                  unsigned long iobase)
 {
@@ -1944,184 +1529,549 @@ static unsigned int labpc_serial_in(struct comedi_device *dev)
        return value;
 }
 
-static unsigned int labpc_eeprom_read(struct comedi_device *dev,
-                                     unsigned int address)
-{
-       struct labpc_private *devpriv = dev->private;
-       unsigned int value;
-       /*  bits to tell eeprom to expect a read */
-       const int read_instruction = 0x3;
-       /*  8 bit write lengths to eeprom */
-       const int write_length = 8;
+static unsigned int labpc_eeprom_read(struct comedi_device *dev,
+                                     unsigned int address)
+{
+       struct labpc_private *devpriv = dev->private;
+       unsigned int value;
+       /*  bits to tell eeprom to expect a read */
+       const int read_instruction = 0x3;
+       /*  8 bit write lengths to eeprom */
+       const int write_length = 8;
+
+       /*  enable read/write to eeprom */
+       devpriv->command5_bits &= ~EEPROM_EN_BIT;
+       udelay(1);
+       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+       devpriv->command5_bits |= EEPROM_EN_BIT | EEPROM_WRITE_UNPROTECT_BIT;
+       udelay(1);
+       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+
+       /*  send read instruction */
+       labpc_serial_out(dev, read_instruction, write_length);
+       /*  send 8 bit address to read from */
+       labpc_serial_out(dev, address, write_length);
+       /*  read result */
+       value = labpc_serial_in(dev);
+
+       /*  disable read/write to eeprom */
+       devpriv->command5_bits &= ~EEPROM_EN_BIT & ~EEPROM_WRITE_UNPROTECT_BIT;
+       udelay(1);
+       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+
+       return value;
+}
+
+static unsigned int labpc_eeprom_read_status(struct comedi_device *dev)
+{
+       struct labpc_private *devpriv = dev->private;
+       unsigned int value;
+       const int read_status_instruction = 0x5;
+       const int write_length = 8;     /*  8 bit write lengths to eeprom */
+
+       /*  enable read/write to eeprom */
+       devpriv->command5_bits &= ~EEPROM_EN_BIT;
+       udelay(1);
+       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+       devpriv->command5_bits |= EEPROM_EN_BIT | EEPROM_WRITE_UNPROTECT_BIT;
+       udelay(1);
+       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+
+       /*  send read status instruction */
+       labpc_serial_out(dev, read_status_instruction, write_length);
+       /*  read result */
+       value = labpc_serial_in(dev);
+
+       /*  disable read/write to eeprom */
+       devpriv->command5_bits &= ~EEPROM_EN_BIT & ~EEPROM_WRITE_UNPROTECT_BIT;
+       udelay(1);
+       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+
+       return value;
+}
+
+static int labpc_eeprom_write(struct comedi_device *dev,
+                               unsigned int address, unsigned int value)
+{
+       struct labpc_private *devpriv = dev->private;
+       const int write_enable_instruction = 0x6;
+       const int write_instruction = 0x2;
+       const int write_length = 8;     /*  8 bit write lengths to eeprom */
+       const int write_in_progress_bit = 0x1;
+       const int timeout = 10000;
+       int i;
+
+       /*  make sure there isn't already a write in progress */
+       for (i = 0; i < timeout; i++) {
+               if ((labpc_eeprom_read_status(dev) & write_in_progress_bit) ==
+                   0)
+                       break;
+       }
+       if (i == timeout) {
+               comedi_error(dev, "eeprom write timed out");
+               return -ETIME;
+       }
+       /*  update software copy of eeprom */
+       devpriv->eeprom_data[address] = value;
+
+       /*  enable read/write to eeprom */
+       devpriv->command5_bits &= ~EEPROM_EN_BIT;
+       udelay(1);
+       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+       devpriv->command5_bits |= EEPROM_EN_BIT | EEPROM_WRITE_UNPROTECT_BIT;
+       udelay(1);
+       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+
+       /*  send write_enable instruction */
+       labpc_serial_out(dev, write_enable_instruction, write_length);
+       devpriv->command5_bits &= ~EEPROM_EN_BIT;
+       udelay(1);
+       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+
+       /*  send write instruction */
+       devpriv->command5_bits |= EEPROM_EN_BIT;
+       udelay(1);
+       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+       labpc_serial_out(dev, write_instruction, write_length);
+       /*  send 8 bit address to write to */
+       labpc_serial_out(dev, address, write_length);
+       /*  write value */
+       labpc_serial_out(dev, value, write_length);
+       devpriv->command5_bits &= ~EEPROM_EN_BIT;
+       udelay(1);
+       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+
+       /*  disable read/write to eeprom */
+       devpriv->command5_bits &= ~EEPROM_EN_BIT & ~EEPROM_WRITE_UNPROTECT_BIT;
+       udelay(1);
+       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+
+       return 0;
+}
+
+/* writes to 8 bit calibration dacs */
+static void write_caldac(struct comedi_device *dev, unsigned int channel,
+                        unsigned int value)
+{
+       struct labpc_private *devpriv = dev->private;
+
+       if (value == devpriv->caldac[channel])
+               return;
+       devpriv->caldac[channel] = value;
+
+       /*  clear caldac load bit and make sure we don't write to eeprom */
+       devpriv->command5_bits &=
+           ~CALDAC_LOAD_BIT & ~EEPROM_EN_BIT & ~EEPROM_WRITE_UNPROTECT_BIT;
+       udelay(1);
+       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+
+       /*  write 4 bit channel */
+       labpc_serial_out(dev, channel, 4);
+       /*  write 8 bit caldac value */
+       labpc_serial_out(dev, value, 8);
+
+       /*  set and clear caldac bit to load caldac value */
+       devpriv->command5_bits |= CALDAC_LOAD_BIT;
+       udelay(1);
+       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+       devpriv->command5_bits &= ~CALDAC_LOAD_BIT;
+       udelay(1);
+       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+}
+
+static int labpc_calib_write_insn(struct comedi_device *dev,
+                                 struct comedi_subdevice *s,
+                                 struct comedi_insn *insn, unsigned int *data)
+{
+       int channel = CR_CHAN(insn->chanspec);
+
+       write_caldac(dev, channel, data[0]);
+       return 1;
+}
+
+static int labpc_calib_read_insn(struct comedi_device *dev,
+                                struct comedi_subdevice *s,
+                                struct comedi_insn *insn, unsigned int *data)
+{
+       struct labpc_private *devpriv = dev->private;
+
+       data[0] = devpriv->caldac[CR_CHAN(insn->chanspec)];
+
+       return 1;
+}
+
+static int labpc_eeprom_write_insn(struct comedi_device *dev,
+                                  struct comedi_subdevice *s,
+                                  struct comedi_insn *insn, unsigned int *data)
+{
+       int channel = CR_CHAN(insn->chanspec);
+       int ret;
+
+       /*  only allow writes to user area of eeprom */
+       if (channel < 16 || channel > 127) {
+               dev_dbg(dev->class_dev,
+                       "eeprom writes are only allowed to channels 16 through 127 (the pointer and user areas)\n");
+               return -EINVAL;
+       }
+
+       ret = labpc_eeprom_write(dev, channel, data[0]);
+       if (ret < 0)
+               return ret;
+
+       return 1;
+}
+
+static int labpc_eeprom_read_insn(struct comedi_device *dev,
+                                 struct comedi_subdevice *s,
+                                 struct comedi_insn *insn, unsigned int *data)
+{
+       struct labpc_private *devpriv = dev->private;
+
+       data[0] = devpriv->eeprom_data[CR_CHAN(insn->chanspec)];
+
+       return 1;
+}
+
+int labpc_common_attach(struct comedi_device *dev, unsigned long iobase,
+                       unsigned int irq, unsigned int dma_chan)
+{
+       const struct labpc_boardinfo *thisboard = comedi_board(dev);
+       struct labpc_private *devpriv = dev->private;
+       struct comedi_subdevice *s;
+       int i;
+       unsigned long isr_flags;
+#ifdef CONFIG_ISA_DMA_API
+       unsigned long dma_flags;
+#endif
+       short lsb, msb;
+       int ret;
+
+       dev_info(dev->class_dev, "ni_labpc: %s\n", thisboard->name);
+       if (iobase == 0) {
+               dev_err(dev->class_dev, "io base address is zero!\n");
+               return -EINVAL;
+       }
+       /*  request io regions for isa boards */
+       if (thisboard->bustype == isa_bustype) {
+               /* check if io addresses are available */
+               if (!request_region(iobase, LABPC_SIZE, DRV_NAME)) {
+                       dev_err(dev->class_dev, "I/O port conflict\n");
+                       return -EIO;
+               }
+       }
+       dev->iobase = iobase;
+
+       if (thisboard->memory_mapped_io) {
+               devpriv->read_byte = labpc_readb;
+               devpriv->write_byte = labpc_writeb;
+       } else {
+               devpriv->read_byte = labpc_inb;
+               devpriv->write_byte = labpc_outb;
+       }
+       /* initialize board's command registers */
+       devpriv->write_byte(devpriv->command1_bits, dev->iobase + COMMAND1_REG);
+       devpriv->write_byte(devpriv->command2_bits, dev->iobase + COMMAND2_REG);
+       devpriv->write_byte(devpriv->command3_bits, dev->iobase + COMMAND3_REG);
+       devpriv->write_byte(devpriv->command4_bits, dev->iobase + COMMAND4_REG);
+       if (thisboard->register_layout == labpc_1200_layout) {
+               devpriv->write_byte(devpriv->command5_bits,
+                                   dev->iobase + COMMAND5_REG);
+               devpriv->write_byte(devpriv->command6_bits,
+                                   dev->iobase + COMMAND6_REG);
+       }
+
+       /* grab our IRQ */
+       if (irq) {
+               isr_flags = 0;
+               if (thisboard->bustype == pci_bustype
+                   || thisboard->bustype == pcmcia_bustype)
+                       isr_flags |= IRQF_SHARED;
+               if (request_irq(irq, labpc_interrupt, isr_flags,
+                               DRV_NAME, dev)) {
+                       dev_err(dev->class_dev, "unable to allocate irq %u\n",
+                               irq);
+                       return -EINVAL;
+               }
+       }
+       dev->irq = irq;
+
+#ifdef CONFIG_ISA_DMA_API
+       /* grab dma channel */
+       if (dma_chan > 3) {
+               dev_err(dev->class_dev, "invalid dma channel %u\n", dma_chan);
+               return -EINVAL;
+       } else if (dma_chan) {
+               /* allocate dma buffer */
+               devpriv->dma_buffer = kmalloc(dma_buffer_size,
+                                             GFP_KERNEL | GFP_DMA);
+               if (devpriv->dma_buffer == NULL)
+                       return -ENOMEM;
+
+               if (request_dma(dma_chan, DRV_NAME)) {
+                       dev_err(dev->class_dev,
+                               "failed to allocate dma channel %u\n",
+                               dma_chan);
+                       return -EINVAL;
+               }
+               devpriv->dma_chan = dma_chan;
+               dma_flags = claim_dma_lock();
+               disable_dma(devpriv->dma_chan);
+               set_dma_mode(devpriv->dma_chan, DMA_MODE_READ);
+               release_dma_lock(dma_flags);
+       }
+#endif
+
+       dev->board_name = thisboard->name;
+
+       ret = comedi_alloc_subdevices(dev, 5);
+       if (ret)
+               return ret;
+
+       /* analog input subdevice */
+       s = &dev->subdevices[0];
+       dev->read_subdev = s;
+       s->type = COMEDI_SUBD_AI;
+       s->subdev_flags =
+           SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF | SDF_CMD_READ;
+       s->n_chan = 8;
+       s->len_chanlist = 8;
+       s->maxdata = (1 << 12) - 1;     /* 12 bit resolution */
+       s->range_table = thisboard->ai_range_table;
+       s->do_cmd = labpc_ai_cmd;
+       s->do_cmdtest = labpc_ai_cmdtest;
+       s->insn_read = labpc_ai_rinsn;
+       s->cancel = labpc_cancel;
+
+       /* analog output */
+       s = &dev->subdevices[1];
+       if (thisboard->has_ao) {
+               /*
+                * Could provide command support, except it only has a
+                * one sample hardware buffer for analog output and no
+                * underrun flag.
+                */
+               s->type = COMEDI_SUBD_AO;
+               s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_GROUND;
+               s->n_chan = NUM_AO_CHAN;
+               s->maxdata = (1 << 12) - 1;     /*  12 bit resolution */
+               s->range_table = &range_labpc_ao;
+               s->insn_read = labpc_ao_rinsn;
+               s->insn_write = labpc_ao_winsn;
+               /* initialize analog outputs to a known value */
+               for (i = 0; i < s->n_chan; i++) {
+                       devpriv->ao_value[i] = s->maxdata / 2;
+                       lsb = devpriv->ao_value[i] & 0xff;
+                       msb = (devpriv->ao_value[i] >> 8) & 0xff;
+                       devpriv->write_byte(lsb, dev->iobase + DAC_LSB_REG(i));
+                       devpriv->write_byte(msb, dev->iobase + DAC_MSB_REG(i));
+               }
+       } else {
+               s->type = COMEDI_SUBD_UNUSED;
+       }
+
+       /* 8255 dio */
+       s = &dev->subdevices[2];
+       /*  if board uses io memory we have to give a custom callback
+        * function to the 8255 driver */
+       if (thisboard->memory_mapped_io)
+               subdev_8255_init(dev, s, labpc_dio_mem_callback,
+                                (unsigned long)(dev->iobase + DIO_BASE_REG));
+       else
+               subdev_8255_init(dev, s, NULL, dev->iobase + DIO_BASE_REG);
+
+       /*  calibration subdevices for boards that have one */
+       s = &dev->subdevices[3];
+       if (thisboard->register_layout == labpc_1200_layout) {
+               s->type = COMEDI_SUBD_CALIB;
+               s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+               s->n_chan = 16;
+               s->maxdata = 0xff;
+               s->insn_read = labpc_calib_read_insn;
+               s->insn_write = labpc_calib_write_insn;
 
-       /*  enable read/write to eeprom */
-       devpriv->command5_bits &= ~EEPROM_EN_BIT;
-       udelay(1);
-       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
-       devpriv->command5_bits |= EEPROM_EN_BIT | EEPROM_WRITE_UNPROTECT_BIT;
-       udelay(1);
-       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+               for (i = 0; i < s->n_chan; i++)
+                       write_caldac(dev, i, s->maxdata / 2);
+       } else
+               s->type = COMEDI_SUBD_UNUSED;
 
-       /*  send read instruction */
-       labpc_serial_out(dev, read_instruction, write_length);
-       /*  send 8 bit address to read from */
-       labpc_serial_out(dev, address, write_length);
-       /*  read result */
-       value = labpc_serial_in(dev);
+       /* EEPROM */
+       s = &dev->subdevices[4];
+       if (thisboard->register_layout == labpc_1200_layout) {
+               s->type = COMEDI_SUBD_MEMORY;
+               s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+               s->n_chan = EEPROM_SIZE;
+               s->maxdata = 0xff;
+               s->insn_read = labpc_eeprom_read_insn;
+               s->insn_write = labpc_eeprom_write_insn;
 
-       /*  disable read/write to eeprom */
-       devpriv->command5_bits &= ~EEPROM_EN_BIT & ~EEPROM_WRITE_UNPROTECT_BIT;
-       udelay(1);
-       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+               for (i = 0; i < EEPROM_SIZE; i++)
+                       devpriv->eeprom_data[i] = labpc_eeprom_read(dev, i);
+       } else
+               s->type = COMEDI_SUBD_UNUSED;
 
-       return value;
+       return 0;
 }
+EXPORT_SYMBOL_GPL(labpc_common_attach);
 
-static int labpc_eeprom_write(struct comedi_device *dev,
-                               unsigned int address, unsigned int value)
+static int labpc_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 {
-       struct labpc_private *devpriv = dev->private;
-       const int write_enable_instruction = 0x6;
-       const int write_instruction = 0x2;
-       const int write_length = 8;     /*  8 bit write lengths to eeprom */
-       const int write_in_progress_bit = 0x1;
-       const int timeout = 10000;
-       int i;
-
-       /*  make sure there isn't already a write in progress */
-       for (i = 0; i < timeout; i++) {
-               if ((labpc_eeprom_read_status(dev) & write_in_progress_bit) ==
-                   0)
-                       break;
-       }
-       if (i == timeout) {
-               comedi_error(dev, "eeprom write timed out");
-               return -ETIME;
-       }
-       /*  update software copy of eeprom */
-       devpriv->eeprom_data[address] = value;
+       const struct labpc_boardinfo *thisboard = comedi_board(dev);
+       struct labpc_private *devpriv;
+       unsigned long iobase = 0;
+       unsigned int irq = 0;
+       unsigned int dma_chan = 0;
 
-       /*  enable read/write to eeprom */
-       devpriv->command5_bits &= ~EEPROM_EN_BIT;
-       udelay(1);
-       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
-       devpriv->command5_bits |= EEPROM_EN_BIT | EEPROM_WRITE_UNPROTECT_BIT;
-       udelay(1);
-       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+       devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
+       if (!devpriv)
+               return -ENOMEM;
+       dev->private = devpriv;
 
-       /*  send write_enable instruction */
-       labpc_serial_out(dev, write_enable_instruction, write_length);
-       devpriv->command5_bits &= ~EEPROM_EN_BIT;
-       udelay(1);
-       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+       /* get base address, irq etc. based on bustype */
+       switch (thisboard->bustype) {
+       case isa_bustype:
+#ifdef CONFIG_ISA_DMA_API
+               iobase = it->options[0];
+               irq = it->options[1];
+               dma_chan = it->options[2];
+#else
+               dev_err(dev->class_dev,
+                       "ni_labpc driver has not been built with ISA DMA support.\n");
+               return -EINVAL;
+#endif
+               break;
+       case pci_bustype:
+#ifdef CONFIG_COMEDI_PCI_DRIVERS
+               dev_err(dev->class_dev,
+                       "manual configuration of PCI board '%s' is not supported\n",
+                       thisboard->name);
+               return -EINVAL;
+#else
+               dev_err(dev->class_dev,
+                       "ni_labpc driver has not been built with PCI support.\n");
+               return -EINVAL;
+#endif
+               break;
+       default:
+               dev_err(dev->class_dev,
+                       "ni_labpc: bug! couldn't determine board type\n");
+               return -EINVAL;
+               break;
+       }
 
-       /*  send write instruction */
-       devpriv->command5_bits |= EEPROM_EN_BIT;
-       udelay(1);
-       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
-       labpc_serial_out(dev, write_instruction, write_length);
-       /*  send 8 bit address to write to */
-       labpc_serial_out(dev, address, write_length);
-       /*  write value */
-       labpc_serial_out(dev, value, write_length);
-       devpriv->command5_bits &= ~EEPROM_EN_BIT;
-       udelay(1);
-       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+       return labpc_common_attach(dev, iobase, irq, dma_chan);
+}
 
-       /*  disable read/write to eeprom */
-       devpriv->command5_bits &= ~EEPROM_EN_BIT & ~EEPROM_WRITE_UNPROTECT_BIT;
-       udelay(1);
-       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+static const struct labpc_boardinfo *
+labpc_pci_find_boardinfo(struct pci_dev *pcidev)
+{
+       unsigned int device_id = pcidev->device;
+       unsigned int n;
 
-       return 0;
+       for (n = 0; n < ARRAY_SIZE(labpc_boards); n++) {
+               const struct labpc_boardinfo *board = &labpc_boards[n];
+               if (board->bustype == pci_bustype &&
+                   board->device_id == device_id)
+                       return board;
+       }
+       return NULL;
 }
 
-static unsigned int labpc_eeprom_read_status(struct comedi_device *dev)
+static int labpc_auto_attach(struct comedi_device *dev,
+                                      unsigned long context_unused)
 {
-       struct labpc_private *devpriv = dev->private;
-       unsigned int value;
-       const int read_status_instruction = 0x5;
-       const int write_length = 8;     /*  8 bit write lengths to eeprom */
+       struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+       struct labpc_private *devpriv;
+       unsigned long iobase;
+       unsigned int irq;
+       int ret;
 
-       /*  enable read/write to eeprom */
-       devpriv->command5_bits &= ~EEPROM_EN_BIT;
-       udelay(1);
-       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
-       devpriv->command5_bits |= EEPROM_EN_BIT | EEPROM_WRITE_UNPROTECT_BIT;
-       udelay(1);
-       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+       if (!IS_ENABLED(CONFIG_COMEDI_PCI_DRIVERS))
+               return -ENODEV;
 
-       /*  send read status instruction */
-       labpc_serial_out(dev, read_status_instruction, write_length);
-       /*  read result */
-       value = labpc_serial_in(dev);
+       ret = comedi_pci_enable(dev);
+       if (ret)
+               return ret;
 
-       /*  disable read/write to eeprom */
-       devpriv->command5_bits &= ~EEPROM_EN_BIT & ~EEPROM_WRITE_UNPROTECT_BIT;
-       udelay(1);
-       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+       devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
+       if (!devpriv)
+               return -ENOMEM;
+       dev->private = devpriv;
 
-       return value;
+       dev->board_ptr = labpc_pci_find_boardinfo(pcidev);
+       if (!dev->board_ptr)
+               return -ENODEV;
+       devpriv->mite = mite_alloc(pcidev);
+       if (!devpriv->mite)
+               return -ENOMEM;
+       ret = mite_setup(devpriv->mite);
+       if (ret < 0)
+               return ret;
+       iobase = (unsigned long)devpriv->mite->daq_io_addr;
+       irq = mite_irq(devpriv->mite);
+       return labpc_common_attach(dev, iobase, irq, 0);
 }
 
-/* writes to 8 bit calibration dacs */
-static void write_caldac(struct comedi_device *dev, unsigned int channel,
-                        unsigned int value)
+void labpc_common_detach(struct comedi_device *dev)
 {
+       const struct labpc_boardinfo *thisboard = comedi_board(dev);
        struct labpc_private *devpriv = dev->private;
+       struct comedi_subdevice *s;
 
-       if (value == devpriv->caldac[channel])
+       if (!thisboard)
                return;
-       devpriv->caldac[channel] = value;
-
-       /*  clear caldac load bit and make sure we don't write to eeprom */
-       devpriv->command5_bits &=
-           ~CALDAC_LOAD_BIT & ~EEPROM_EN_BIT & ~EEPROM_WRITE_UNPROTECT_BIT;
-       udelay(1);
-       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
-
-       /*  write 4 bit channel */
-       labpc_serial_out(dev, channel, 4);
-       /*  write 8 bit caldac value */
-       labpc_serial_out(dev, value, 8);
-
-       /*  set and clear caldac bit to load caldac value */
-       devpriv->command5_bits |= CALDAC_LOAD_BIT;
-       udelay(1);
-       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
-       devpriv->command5_bits &= ~CALDAC_LOAD_BIT;
-       udelay(1);
-       devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+       if (dev->subdevices) {
+               s = &dev->subdevices[2];
+               subdev_8255_cleanup(dev, s);
+       }
+#ifdef CONFIG_ISA_DMA_API
+       /* only free stuff if it has been allocated by _attach */
+       kfree(devpriv->dma_buffer);
+       if (devpriv->dma_chan)
+               free_dma(devpriv->dma_chan);
+#endif
+       if (dev->irq)
+               free_irq(dev->irq, dev);
+       if (thisboard->bustype == isa_bustype && dev->iobase)
+               release_region(dev->iobase, LABPC_SIZE);
+#ifdef CONFIG_COMEDI_PCI_DRIVERS
+       if (devpriv->mite) {
+               mite_unsetup(devpriv->mite);
+               mite_free(devpriv->mite);
+       }
+       if (thisboard->bustype == pci_bustype)
+               comedi_pci_disable(dev);
+#endif
 }
+EXPORT_SYMBOL_GPL(labpc_common_detach);
 
 static struct comedi_driver labpc_driver = {
-       .driver_name = DRV_NAME,
-       .module = THIS_MODULE,
-       .attach = labpc_attach,
-       .auto_attach = labpc_auto_attach,
-       .detach = labpc_common_detach,
-       .num_names = ARRAY_SIZE(labpc_boards),
-       .board_name = &labpc_boards[0].name,
-       .offset = sizeof(struct labpc_board_struct),
+       .driver_name    = DRV_NAME,
+       .module         = THIS_MODULE,
+       .attach         = labpc_attach,
+       .auto_attach    = labpc_auto_attach,
+       .detach         = labpc_common_detach,
+       .num_names      = ARRAY_SIZE(labpc_boards),
+       .board_name     = &labpc_boards[0].name,
+       .offset         = sizeof(struct labpc_boardinfo),
 };
 
 #ifdef CONFIG_COMEDI_PCI_DRIVERS
 static DEFINE_PCI_DEVICE_TABLE(labpc_pci_table) = {
-       {PCI_DEVICE(PCI_VENDOR_ID_NI, 0x161)},
-       {0}
+       { PCI_DEVICE(PCI_VENDOR_ID_NI, 0x161) },
+       { 0 }
 };
 MODULE_DEVICE_TABLE(pci, labpc_pci_table);
 
 static int labpc_pci_probe(struct pci_dev *dev,
-                                    const struct pci_device_id *ent)
+                          const struct pci_device_id *id)
 {
-       return comedi_pci_auto_config(dev, &labpc_driver);
+       return comedi_pci_auto_config(dev, &labpc_driver, id->driver_data);
 }
 
 static struct pci_driver labpc_pci_driver = {
-       .name = DRV_NAME,
-       .id_table = labpc_pci_table,
-       .probe = labpc_pci_probe,
+       .name           = DRV_NAME,
+       .id_table       = labpc_pci_table,
+       .probe          = labpc_pci_probe,
        .remove         = comedi_pci_auto_unconfig,
 };
 module_comedi_pci_driver(labpc_driver, labpc_pci_driver);