staging: comedi: drivers: use comedi_fc.h cmdtest helpers
[pandora-kernel.git] / drivers / staging / comedi / drivers / me4000.c
index ee028a3..22db35d 100644 (file)
@@ -35,13 +35,7 @@ Supports:
     - Digital I/O
     - Counter
 
-Configuration Options:
-
-    [0] - PCI bus number (optional)
-    [1] - PCI slot number (optional)
-
-    If bus/slot is not specified, the first available PCI
-    device will be used.
+Configuration Options: not applicable, uses PCI auto config
 
 The firmware required by these boards is available in the
 comedi_nonfree_firmware tarball available from
@@ -58,7 +52,9 @@ broken.
 #include <linux/list.h>
 #include <linux/spinlock.h>
 
-#include "me4000.h"
+#include "comedi_fc.h"
+#include "8253.h"
+
 #if 0
 /* file removed due to GPL incompatibility */
 #include "me4000_fw.h"
@@ -80,6 +76,151 @@ broken.
 #define PCI_DEVICE_ID_MEILHAUS_ME4680S 0x4682
 #define PCI_DEVICE_ID_MEILHAUS_ME4680IS        0x4683
 
+/*
+ * ME4000 Register map and bit defines
+ */
+#define ME4000_AO_CHAN(x)                      ((x) * 0x18)
+
+#define ME4000_AO_CTRL_REG(x)                  (0x00 + ME4000_AO_CHAN(x))
+#define ME4000_AO_CTRL_BIT_MODE_0              (1 << 0)
+#define ME4000_AO_CTRL_BIT_MODE_1              (1 << 1)
+#define ME4000_AO_CTRL_MASK_MODE               (3 << 0)
+#define ME4000_AO_CTRL_BIT_STOP                        (1 << 2)
+#define ME4000_AO_CTRL_BIT_ENABLE_FIFO         (1 << 3)
+#define ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG      (1 << 4)
+#define ME4000_AO_CTRL_BIT_EX_TRIG_EDGE                (1 << 5)
+#define ME4000_AO_CTRL_BIT_IMMEDIATE_STOP      (1 << 7)
+#define ME4000_AO_CTRL_BIT_ENABLE_DO           (1 << 8)
+#define ME4000_AO_CTRL_BIT_ENABLE_IRQ          (1 << 9)
+#define ME4000_AO_CTRL_BIT_RESET_IRQ           (1 << 10)
+#define ME4000_AO_STATUS_REG(x)                        (0x04 + ME4000_AO_CHAN(x))
+#define ME4000_AO_STATUS_BIT_FSM               (1 << 0)
+#define ME4000_AO_STATUS_BIT_FF                        (1 << 1)
+#define ME4000_AO_STATUS_BIT_HF                        (1 << 2)
+#define ME4000_AO_STATUS_BIT_EF                        (1 << 3)
+#define ME4000_AO_FIFO_REG(x)                  (0x08 + ME4000_AO_CHAN(x))
+#define ME4000_AO_SINGLE_REG(x)                        (0x0c + ME4000_AO_CHAN(x))
+#define ME4000_AO_TIMER_REG(x)                 (0x10 + ME4000_AO_CHAN(x))
+#define ME4000_AI_CTRL_REG                     0x74
+#define ME4000_AI_STATUS_REG                   0x74
+#define ME4000_AI_CTRL_BIT_MODE_0              (1 << 0)
+#define ME4000_AI_CTRL_BIT_MODE_1              (1 << 1)
+#define ME4000_AI_CTRL_BIT_MODE_2              (1 << 2)
+#define ME4000_AI_CTRL_BIT_SAMPLE_HOLD         (1 << 3)
+#define ME4000_AI_CTRL_BIT_IMMEDIATE_STOP      (1 << 4)
+#define ME4000_AI_CTRL_BIT_STOP                        (1 << 5)
+#define ME4000_AI_CTRL_BIT_CHANNEL_FIFO                (1 << 6)
+#define ME4000_AI_CTRL_BIT_DATA_FIFO           (1 << 7)
+#define ME4000_AI_CTRL_BIT_FULLSCALE           (1 << 8)
+#define ME4000_AI_CTRL_BIT_OFFSET              (1 << 9)
+#define ME4000_AI_CTRL_BIT_EX_TRIG_ANALOG      (1 << 10)
+#define ME4000_AI_CTRL_BIT_EX_TRIG             (1 << 11)
+#define ME4000_AI_CTRL_BIT_EX_TRIG_FALLING     (1 << 12)
+#define ME4000_AI_CTRL_BIT_EX_IRQ              (1 << 13)
+#define ME4000_AI_CTRL_BIT_EX_IRQ_RESET                (1 << 14)
+#define ME4000_AI_CTRL_BIT_LE_IRQ              (1 << 15)
+#define ME4000_AI_CTRL_BIT_LE_IRQ_RESET                (1 << 16)
+#define ME4000_AI_CTRL_BIT_HF_IRQ              (1 << 17)
+#define ME4000_AI_CTRL_BIT_HF_IRQ_RESET                (1 << 18)
+#define ME4000_AI_CTRL_BIT_SC_IRQ              (1 << 19)
+#define ME4000_AI_CTRL_BIT_SC_IRQ_RESET                (1 << 20)
+#define ME4000_AI_CTRL_BIT_SC_RELOAD           (1 << 21)
+#define ME4000_AI_STATUS_BIT_EF_CHANNEL                (1 << 22)
+#define ME4000_AI_STATUS_BIT_HF_CHANNEL                (1 << 23)
+#define ME4000_AI_STATUS_BIT_FF_CHANNEL                (1 << 24)
+#define ME4000_AI_STATUS_BIT_EF_DATA           (1 << 25)
+#define ME4000_AI_STATUS_BIT_HF_DATA           (1 << 26)
+#define ME4000_AI_STATUS_BIT_FF_DATA           (1 << 27)
+#define ME4000_AI_STATUS_BIT_LE                        (1 << 28)
+#define ME4000_AI_STATUS_BIT_FSM               (1 << 29)
+#define ME4000_AI_CTRL_BIT_EX_TRIG_BOTH                (1 << 31)
+#define ME4000_AI_CHANNEL_LIST_REG             0x78
+#define ME4000_AI_LIST_INPUT_SINGLE_ENDED      (0 << 5)
+#define ME4000_AI_LIST_INPUT_DIFFERENTIAL      (1 << 5)
+#define ME4000_AI_LIST_RANGE_BIPOLAR_10                (0 << 6)
+#define ME4000_AI_LIST_RANGE_BIPOLAR_2_5       (1 << 6)
+#define ME4000_AI_LIST_RANGE_UNIPOLAR_10       (2 << 6)
+#define ME4000_AI_LIST_RANGE_UNIPOLAR_2_5      (3 << 6)
+#define ME4000_AI_LIST_LAST_ENTRY              (1 << 8)
+#define ME4000_AI_DATA_REG                     0x7c
+#define ME4000_AI_CHAN_TIMER_REG               0x80
+#define ME4000_AI_CHAN_PRE_TIMER_REG           0x84
+#define ME4000_AI_SCAN_TIMER_LOW_REG           0x88
+#define ME4000_AI_SCAN_TIMER_HIGH_REG          0x8c
+#define ME4000_AI_SCAN_PRE_TIMER_LOW_REG       0x90
+#define ME4000_AI_SCAN_PRE_TIMER_HIGH_REG      0x94
+#define ME4000_AI_START_REG                    0x98
+#define ME4000_IRQ_STATUS_REG                  0x9c
+#define ME4000_IRQ_STATUS_BIT_EX               (1 << 0)
+#define ME4000_IRQ_STATUS_BIT_LE               (1 << 1)
+#define ME4000_IRQ_STATUS_BIT_AI_HF            (1 << 2)
+#define ME4000_IRQ_STATUS_BIT_AO_0_HF          (1 << 3)
+#define ME4000_IRQ_STATUS_BIT_AO_1_HF          (1 << 4)
+#define ME4000_IRQ_STATUS_BIT_AO_2_HF          (1 << 5)
+#define ME4000_IRQ_STATUS_BIT_AO_3_HF          (1 << 6)
+#define ME4000_IRQ_STATUS_BIT_SC               (1 << 7)
+#define ME4000_DIO_PORT_0_REG                  0xa0
+#define ME4000_DIO_PORT_1_REG                  0xa4
+#define ME4000_DIO_PORT_2_REG                  0xa8
+#define ME4000_DIO_PORT_3_REG                  0xac
+#define ME4000_DIO_DIR_REG                     0xb0
+#define ME4000_AO_LOADSETREG_XX                        0xb4
+#define ME4000_DIO_CTRL_REG                    0xb8
+#define ME4000_DIO_CTRL_BIT_MODE_0             (1 << 0)
+#define ME4000_DIO_CTRL_BIT_MODE_1             (1 << 1)
+#define ME4000_DIO_CTRL_BIT_MODE_2             (1 << 2)
+#define ME4000_DIO_CTRL_BIT_MODE_3             (1 << 3)
+#define ME4000_DIO_CTRL_BIT_MODE_4             (1 << 4)
+#define ME4000_DIO_CTRL_BIT_MODE_5             (1 << 5)
+#define ME4000_DIO_CTRL_BIT_MODE_6             (1 << 6)
+#define ME4000_DIO_CTRL_BIT_MODE_7             (1 << 7)
+#define ME4000_DIO_CTRL_BIT_FUNCTION_0         (1 << 8)
+#define ME4000_DIO_CTRL_BIT_FUNCTION_1         (1 << 9)
+#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_0                (1 << 10)
+#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_1                (1 << 11)
+#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_2                (1 << 12)
+#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_3                (1 << 13)
+#define ME4000_AO_DEMUX_ADJUST_REG             0xbc
+#define ME4000_AO_DEMUX_ADJUST_VALUE           0x4c
+#define ME4000_AI_SAMPLE_COUNTER_REG           0xc0
+
+/*
+ * PLX Register map and bit defines
+ */
+#define PLX_INTCSR                             0x4c
+#define PLX_INTCSR_LOCAL_INT1_EN               (1 << 0)
+#define PLX_INTCSR_LOCAL_INT1_POL              (1 << 1)
+#define PLX_INTCSR_LOCAL_INT1_STATE            (1 << 2)
+#define PLX_INTCSR_LOCAL_INT2_EN               (1 << 3)
+#define PLX_INTCSR_LOCAL_INT2_POL              (1 << 4)
+#define PLX_INTCSR_LOCAL_INT2_STATE            (1 << 5)
+#define PLX_INTCSR_PCI_INT_EN                  (1 << 6)
+#define PLX_INTCSR_SOFT_INT                    (1 << 7)
+#define PLX_ICR                                        0x50
+#define PLX_ICR_BIT_EEPROM_CLOCK_SET           (1 << 24)
+#define PLX_ICR_BIT_EEPROM_CHIP_SELECT         (1 << 25)
+#define PLX_ICR_BIT_EEPROM_WRITE               (1 << 26)
+#define PLX_ICR_BIT_EEPROM_READ                        (1 << 27)
+#define PLX_ICR_BIT_EEPROM_VALID               (1 << 28)
+#define PLX_ICR_MASK_EEPROM                    (0x1f << 24)
+
+#define EEPROM_DELAY                           1
+
+#define ME4000_AI_FIFO_COUNT                   2048
+
+#define ME4000_AI_MIN_TICKS                    66
+#define ME4000_AI_MIN_SAMPLE_TIME              2000
+#define ME4000_AI_BASE_FREQUENCY               (unsigned int) 33E6
+
+#define ME4000_AI_CHANNEL_LIST_COUNT           1024
+
+struct me4000_info {
+       unsigned long plx_regbase;
+       unsigned long timer_regbase;
+
+       unsigned int ao_readback[4];
+};
+
 struct me4000_board {
        const char *name;
        unsigned short device_id;
@@ -212,16 +353,6 @@ static const struct me4000_board me4000_boards[] = {
        },
 };
 
-/*-----------------------------------------------------------------------------
-  Meilhaus function prototypes
-  ---------------------------------------------------------------------------*/
-static int xilinx_download(struct comedi_device *dev);
-static int reset_board(struct comedi_device *dev);
-
-static int ai_write_chanlist(struct comedi_device *dev,
-                            struct comedi_subdevice *s,
-                            struct comedi_cmd *cmd);
-
 static const struct comedi_lrange me4000_ai_range = {
        4,
        {
@@ -232,98 +363,6 @@ static const struct comedi_lrange me4000_ai_range = {
         }
 };
 
-static const struct comedi_lrange me4000_ao_range = {
-       1,
-       {
-        BIP_RANGE(10),
-        }
-};
-
-static int me4000_probe(struct comedi_device *dev, struct comedi_devconfig *it)
-{
-       struct me4000_info *info;
-       struct pci_dev *pci_device = NULL;
-       int result, i;
-       const struct me4000_board *board;
-
-       /* Allocate private memory */
-       result = alloc_private(dev, sizeof(*info));
-       if (result)
-               return result;
-       info = dev->private;
-
-       /*
-        * Probe the device to determine what device in the series it is.
-        */
-       for_each_pci_dev(pci_device) {
-               if (pci_device->vendor == PCI_VENDOR_ID_MEILHAUS) {
-                       for (i = 0; i < ARRAY_SIZE(me4000_boards); i++) {
-                               if (me4000_boards[i].device_id ==
-                                   pci_device->device) {
-                                       /*
-                                        * Was a particular
-                                        * bus/slot requested?
-                                        */
-                                       if ((it->options[0] != 0)
-                                           || (it->options[1] != 0)) {
-                                               /*
-                                                * Are we on the wrong
-                                                * bus/slot?
-                                                */
-                                               if (pci_device->bus->number !=
-                                                   it->options[0]
-                                                   ||
-                                                   PCI_SLOT(pci_device->devfn)
-                                                   != it->options[1]) {
-                                                       continue;
-                                               }
-                                       }
-                                       dev->board_ptr = me4000_boards + i;
-                                       board = comedi_board(dev);
-                                       info->pci_dev_p = pci_device;
-                                       goto found;
-                               }
-                       }
-               }
-       }
-       return -ENODEV;
-
-found:
-       dev->board_name = board->name;
-
-       result = comedi_pci_enable(pci_device, dev->board_name);
-       if (result)
-               return result;
-
-       info->plx_regbase = pci_resource_start(pci_device, 1);
-       if (!info->plx_regbase)
-               return -ENODEV;
-
-       dev->iobase = pci_resource_start(pci_device, 2);
-       if (!dev->iobase)
-               return -ENODEV;
-
-       info->timer_regbase = pci_resource_start(pci_device, 3);
-       if (!info->timer_regbase)
-               return -ENODEV;
-
-       info->program_regbase = pci_resource_start(pci_device, 5);
-       if (!info->program_regbase)
-               return -ENODEV;
-
-       dev->irq = pci_device->irq;
-
-       result = xilinx_download(dev);
-       if (result)
-               return result;
-
-       result = reset_board(dev);
-       if (result)
-               return result;
-
-       return 0;
-}
-
 #define FIRMWARE_NOT_AVAILABLE 1
 #if FIRMWARE_NOT_AVAILABLE
 extern unsigned char *xilinx_firm;
@@ -331,12 +370,17 @@ extern unsigned char *xilinx_firm;
 
 static int xilinx_download(struct comedi_device *dev)
 {
+       struct pci_dev *pcidev = comedi_to_pci_dev(dev);
        struct me4000_info *info = dev->private;
+       unsigned long xilinx_iobase = pci_resource_start(pcidev, 5);
        u32 value = 0;
        wait_queue_head_t queue;
        int idx = 0;
        int size = 0;
 
+       if (!xilinx_iobase)
+               return -ENODEV;
+
        init_waitqueue_head(&queue);
 
        /*
@@ -351,14 +395,12 @@ static int xilinx_download(struct comedi_device *dev)
        outl(value, info->plx_regbase + PLX_ICR);
 
        /* Init Xilinx with CS1 */
-       inb(info->program_regbase + 0xC8);
+       inb(xilinx_iobase + 0xC8);
 
        /* Wait until /INIT pin is set */
        udelay(20);
        if (!(inl(info->plx_regbase + PLX_INTCSR) & 0x20)) {
-               printk(KERN_ERR
-                      "comedi%d: me4000: xilinx_download(): "
-                      "Can't init Xilinx\n", dev->minor);
+               dev_err(dev->class_dev, "Can't init Xilinx\n");
                return -EIO;
        }
 
@@ -367,8 +409,8 @@ static int xilinx_download(struct comedi_device *dev)
        value &= ~0x100;
        outl(value, info->plx_regbase + PLX_ICR);
        if (FIRMWARE_NOT_AVAILABLE) {
-               comedi_error(dev, "xilinx firmware unavailable "
-                            "due to licensing, aborting");
+               dev_err(dev->class_dev,
+                       "xilinx firmware unavailable due to licensing, aborting");
                return -EIO;
        } else {
                /* Download Xilinx firmware */
@@ -377,15 +419,14 @@ static int xilinx_download(struct comedi_device *dev)
                udelay(10);
 
                for (idx = 0; idx < size; idx++) {
-                       outb(xilinx_firm[16 + idx], info->program_regbase);
+                       outb(xilinx_firm[16 + idx], xilinx_iobase);
                        udelay(10);
 
                        /* Check if BUSY flag is low */
                        if (inl(info->plx_regbase + PLX_ICR) & 0x20) {
-                               printk(KERN_ERR
-                                      "comedi%d: me4000: xilinx_download(): "
-                                      "Xilinx is still busy (idx = %d)\n",
-                                      dev->minor, idx);
+                               dev_err(dev->class_dev,
+                                       "Xilinx is still busy (idx = %d)\n",
+                                       idx);
                                return -EIO;
                        }
                }
@@ -394,12 +435,8 @@ static int xilinx_download(struct comedi_device *dev)
        /* If done flag is high download was successful */
        if (inl(info->plx_regbase + PLX_ICR) & 0x4) {
        } else {
-               printk(KERN_ERR
-                      "comedi%d: me4000: xilinx_download(): "
-                      "DONE flag is not set\n", dev->minor);
-               printk(KERN_ERR
-                      "comedi%d: me4000: xilinx_download(): "
-                      "Download not successful\n", dev->minor);
+               dev_err(dev->class_dev, "DONE flag is not set\n");
+               dev_err(dev->class_dev, "Download not successful\n");
                return -EIO;
        }
 
@@ -411,7 +448,7 @@ static int xilinx_download(struct comedi_device *dev)
        return 0;
 }
 
-static int reset_board(struct comedi_device *dev)
+static void me4000_reset(struct comedi_device *dev)
 {
        struct me4000_info *info = dev->private;
        unsigned long val;
@@ -450,8 +487,6 @@ static int reset_board(struct comedi_device *dev)
         */
        if (!(inl(dev->iobase + ME4000_DIO_DIR_REG) & 0x1))
                outl(0x1, dev->iobase + ME4000_DIO_CTRL_REG);
-
-       return 0;
 }
 
 /*=============================================================================
@@ -474,9 +509,8 @@ static int me4000_ai_insn_read(struct comedi_device *dev,
        if (insn->n == 0) {
                return 0;
        } else if (insn->n > 1) {
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_ai_insn_read(): "
-                      "Invalid instruction length %d\n", dev->minor, insn->n);
+               dev_err(dev->class_dev, "Invalid instruction length %d\n",
+                       insn->n);
                return -EINVAL;
        }
 
@@ -494,9 +528,7 @@ static int me4000_ai_insn_read(struct comedi_device *dev,
                entry |= ME4000_AI_LIST_RANGE_BIPOLAR_10;
                break;
        default:
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_ai_insn_read(): "
-                      "Invalid range specified\n", dev->minor);
+               dev_err(dev->class_dev, "Invalid range specified\n");
                return -EINVAL;
        }
 
@@ -504,9 +536,8 @@ static int me4000_ai_insn_read(struct comedi_device *dev,
        case AREF_GROUND:
        case AREF_COMMON:
                if (chan >= thisboard->ai_nchan) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_insn_read(): "
-                              "Analog input is not available\n", dev->minor);
+                       dev_err(dev->class_dev,
+                               "Analog input is not available\n");
                        return -EINVAL;
                }
                entry |= ME4000_AI_LIST_INPUT_SINGLE_ENDED | chan;
@@ -514,25 +545,20 @@ static int me4000_ai_insn_read(struct comedi_device *dev,
 
        case AREF_DIFF:
                if (rang == 0 || rang == 1) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_insn_read(): "
-                              "Range must be bipolar when aref = diff\n",
-                              dev->minor);
+                       dev_err(dev->class_dev,
+                               "Range must be bipolar when aref = diff\n");
                        return -EINVAL;
                }
 
                if (chan >= thisboard->ai_diff_nchan) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_insn_read(): "
-                              "Analog input is not available\n", dev->minor);
+                       dev_err(dev->class_dev,
+                               "Analog input is not available\n");
                        return -EINVAL;
                }
                entry |= ME4000_AI_LIST_INPUT_DIFFERENTIAL | chan;
                break;
        default:
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_ai_insn_read(): "
-                      "Invalid aref specified\n", dev->minor);
+               dev_err(dev->class_dev, "Invalid aref specified\n");
                return -EINVAL;
        }
 
@@ -568,9 +594,7 @@ static int me4000_ai_insn_read(struct comedi_device *dev,
        udelay(10);
        if (!(inl(dev->iobase + ME4000_AI_STATUS_REG) &
             ME4000_AI_STATUS_BIT_EF_DATA)) {
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_ai_insn_read(): "
-                      "Value not available after wait\n", dev->minor);
+               dev_err(dev->class_dev, "Value not available after wait\n");
                return -EIO;
        }
 
@@ -606,25 +630,19 @@ static int ai_check_chanlist(struct comedi_device *dev,
 
        /* Check whether a channel list is available */
        if (!cmd->chanlist_len) {
-               printk(KERN_ERR
-                      "comedi%d: me4000: ai_check_chanlist(): "
-                      "No channel list available\n", dev->minor);
+               dev_err(dev->class_dev, "No channel list available\n");
                return -EINVAL;
        }
 
        /* Check the channel list size */
        if (cmd->chanlist_len > ME4000_AI_CHANNEL_LIST_COUNT) {
-               printk(KERN_ERR
-                      "comedi%d: me4000: ai_check_chanlist(): "
-                      "Channel list is to large\n", dev->minor);
+               dev_err(dev->class_dev, "Channel list is to large\n");
                return -EINVAL;
        }
 
        /* Check the pointer */
        if (!cmd->chanlist) {
-               printk(KERN_ERR
-                      "comedi%d: me4000: ai_check_chanlist(): "
-                      "NULL pointer to channel list\n", dev->minor);
+               dev_err(dev->class_dev, "NULL pointer to channel list\n");
                return -EFAULT;
        }
 
@@ -632,10 +650,8 @@ static int ai_check_chanlist(struct comedi_device *dev,
        aref = CR_AREF(cmd->chanlist[0]);
        for (i = 0; i < cmd->chanlist_len; i++) {
                if (CR_AREF(cmd->chanlist[i]) != aref) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: ai_check_chanlist(): "
-                              "Mode is not equal for all entries\n",
-                              dev->minor);
+                       dev_err(dev->class_dev,
+                               "Mode is not equal for all entries\n");
                        return -EINVAL;
                }
        }
@@ -645,18 +661,16 @@ static int ai_check_chanlist(struct comedi_device *dev,
                for (i = 0; i < cmd->chanlist_len; i++) {
                        if (CR_CHAN(cmd->chanlist[i]) >=
                            thisboard->ai_diff_nchan) {
-                               printk(KERN_ERR
-                                      "comedi%d: me4000: ai_check_chanlist():"
-                                      " Channel number to high\n", dev->minor);
+                               dev_err(dev->class_dev,
+                                       "Channel number to high\n");
                                return -EINVAL;
                        }
                }
        } else {
                for (i = 0; i < cmd->chanlist_len; i++) {
                        if (CR_CHAN(cmd->chanlist[i]) >= thisboard->ai_nchan) {
-                               printk(KERN_ERR
-                                      "comedi%d: me4000: ai_check_chanlist(): "
-                                      "Channel number to high\n", dev->minor);
+                               dev_err(dev->class_dev,
+                                       "Channel number to high\n");
                                return -EINVAL;
                        }
                }
@@ -667,11 +681,8 @@ static int ai_check_chanlist(struct comedi_device *dev,
                for (i = 0; i < cmd->chanlist_len; i++) {
                        if (CR_RANGE(cmd->chanlist[i]) != 1 &&
                            CR_RANGE(cmd->chanlist[i]) != 2) {
-                               printk(KERN_ERR
-                                      "comedi%d: me4000: ai_check_chanlist(): "
-                                      "Bipolar is not selected in "
-                                      "differential mode\n",
-                                      dev->minor);
+                               dev_err(dev->class_dev,
+                                      "Bipolar is not selected in differential mode\n");
                                return -EINVAL;
                        }
                }
@@ -751,6 +762,42 @@ static void ai_write_timer(struct comedi_device *dev,
        outl(chan_ticks - 1, dev->iobase + ME4000_AI_CHAN_TIMER_REG);
 }
 
+static int ai_write_chanlist(struct comedi_device *dev,
+                            struct comedi_subdevice *s, struct comedi_cmd *cmd)
+{
+       unsigned int entry;
+       unsigned int chan;
+       unsigned int rang;
+       unsigned int aref;
+       int i;
+
+       for (i = 0; i < cmd->chanlist_len; i++) {
+               chan = CR_CHAN(cmd->chanlist[i]);
+               rang = CR_RANGE(cmd->chanlist[i]);
+               aref = CR_AREF(cmd->chanlist[i]);
+
+               entry = chan;
+
+               if (rang == 0)
+                       entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_2_5;
+               else if (rang == 1)
+                       entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_10;
+               else if (rang == 2)
+                       entry |= ME4000_AI_LIST_RANGE_BIPOLAR_2_5;
+               else
+                       entry |= ME4000_AI_LIST_RANGE_BIPOLAR_10;
+
+               if (aref == SDF_DIFF)
+                       entry |= ME4000_AI_LIST_INPUT_DIFFERENTIAL;
+               else
+                       entry |= ME4000_AI_LIST_INPUT_SINGLE_ENDED;
+
+               outl(entry, dev->iobase + ME4000_AI_CHANNEL_LIST_REG);
+       }
+
+       return 0;
+}
+
 static int ai_prepare(struct comedi_device *dev,
                      struct comedi_subdevice *s,
                      struct comedi_cmd *cmd,
@@ -818,42 +865,6 @@ static int ai_prepare(struct comedi_device *dev,
        return 0;
 }
 
-static int ai_write_chanlist(struct comedi_device *dev,
-                            struct comedi_subdevice *s, struct comedi_cmd *cmd)
-{
-       unsigned int entry;
-       unsigned int chan;
-       unsigned int rang;
-       unsigned int aref;
-       int i;
-
-       for (i = 0; i < cmd->chanlist_len; i++) {
-               chan = CR_CHAN(cmd->chanlist[i]);
-               rang = CR_RANGE(cmd->chanlist[i]);
-               aref = CR_AREF(cmd->chanlist[i]);
-
-               entry = chan;
-
-               if (rang == 0)
-                       entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_2_5;
-               else if (rang == 1)
-                       entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_10;
-               else if (rang == 2)
-                       entry |= ME4000_AI_LIST_RANGE_BIPOLAR_2_5;
-               else
-                       entry |= ME4000_AI_LIST_RANGE_BIPOLAR_10;
-
-               if (aref == SDF_DIFF)
-                       entry |= ME4000_AI_LIST_INPUT_DIFFERENTIAL;
-               else
-                       entry |= ME4000_AI_LIST_INPUT_SINGLE_ENDED;
-
-               outl(entry, dev->iobase + ME4000_AI_CHANNEL_LIST_REG);
-       }
-
-       return 0;
-}
-
 static int me4000_ai_do_cmd(struct comedi_device *dev,
                            struct comedi_subdevice *s)
 {
@@ -885,18 +896,6 @@ static int me4000_ai_do_cmd(struct comedi_device *dev,
        return 0;
 }
 
-/*
- * me4000_ai_do_cmd_test():
- *
- * The demo cmd.c in ./comedilib/demo specifies 6 return values:
- * - success
- * - invalid source
- * - source conflict
- * - invalid argument
- * - argument conflict
- * - invalid chanlist
- * So I tried to adopt this scheme.
- */
 static int me4000_ai_do_cmd_test(struct comedi_device *dev,
                                 struct comedi_subdevice *s,
                                 struct comedi_cmd *cmd)
@@ -913,91 +912,29 @@ static int me4000_ai_do_cmd_test(struct comedi_device *dev,
        /* Round the timer arguments */
        ai_round_cmd_args(dev, s, cmd, &init_ticks, &scan_ticks, &chan_ticks);
 
-       /*
-        * Stage 1. Check if the trigger sources are generally valid.
-        */
-       switch (cmd->start_src) {
-       case TRIG_NOW:
-       case TRIG_EXT:
-               break;
-       case TRIG_ANY:
-               cmd->start_src &= TRIG_NOW | TRIG_EXT;
-               err++;
-               break;
-       default:
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                      "Invalid start source\n", dev->minor);
-               cmd->start_src = TRIG_NOW;
-               err++;
-       }
-       switch (cmd->scan_begin_src) {
-       case TRIG_FOLLOW:
-       case TRIG_TIMER:
-       case TRIG_EXT:
-               break;
-       case TRIG_ANY:
-               cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT;
-               err++;
-               break;
-       default:
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                      "Invalid scan begin source\n", dev->minor);
-               cmd->scan_begin_src = TRIG_FOLLOW;
-               err++;
-       }
-       switch (cmd->convert_src) {
-       case TRIG_TIMER:
-       case TRIG_EXT:
-               break;
-       case TRIG_ANY:
-               cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
-               err++;
-               break;
-       default:
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                      "Invalid convert source\n", dev->minor);
-               cmd->convert_src = TRIG_TIMER;
-               err++;
-       }
-       switch (cmd->scan_end_src) {
-       case TRIG_NONE:
-       case TRIG_COUNT:
-               break;
-       case TRIG_ANY:
-               cmd->scan_end_src &= TRIG_NONE | TRIG_COUNT;
-               err++;
-               break;
-       default:
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                      "Invalid scan end source\n", dev->minor);
-               cmd->scan_end_src = TRIG_NONE;
-               err++;
-       }
-       switch (cmd->stop_src) {
-       case TRIG_NONE:
-       case TRIG_COUNT:
-               break;
-       case TRIG_ANY:
-               cmd->stop_src &= TRIG_NONE | TRIG_COUNT;
-               err++;
-               break;
-       default:
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                      "Invalid stop source\n", dev->minor);
-               cmd->stop_src = TRIG_NONE;
-               err++;
-       }
+       /* Step 1 : check if triggers are trivially valid */
+
+       err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+       err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+                                       TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT);
+       err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
+       err |= cfc_check_trigger_src(&cmd->scan_end_src,
+                                       TRIG_NONE | TRIG_COUNT);
+       err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE | TRIG_COUNT);
+
        if (err)
                return 1;
 
-       /*
-        * Stage 2. Check for trigger source conflicts.
-        */
+       /* Step 2a : make sure trigger sources are unique */
+
+       err |= cfc_check_trigger_is_unique(cmd->start_src);
+       err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+       err |= cfc_check_trigger_is_unique(cmd->convert_src);
+       err |= cfc_check_trigger_is_unique(cmd->scan_end_src);
+       err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+       /* Step 2b : and mutually compatible */
+
        if (cmd->start_src == TRIG_NOW &&
            cmd->scan_begin_src == TRIG_TIMER &&
            cmd->convert_src == TRIG_TIMER) {
@@ -1017,13 +954,7 @@ static int me4000_ai_do_cmd_test(struct comedi_device *dev,
                   cmd->scan_begin_src == TRIG_EXT &&
                   cmd->convert_src == TRIG_EXT) {
        } else {
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                      "Invalid start trigger combination\n", dev->minor);
-               cmd->start_src = TRIG_NOW;
-               cmd->scan_begin_src = TRIG_FOLLOW;
-               cmd->convert_src = TRIG_TIMER;
-               err++;
+               err |= -EINVAL;
        }
 
        if (cmd->stop_src == TRIG_NONE && cmd->scan_end_src == TRIG_NONE) {
@@ -1034,13 +965,9 @@ static int me4000_ai_do_cmd_test(struct comedi_device *dev,
        } else if (cmd->stop_src == TRIG_COUNT &&
                   cmd->scan_end_src == TRIG_COUNT) {
        } else {
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                      "Invalid stop trigger combination\n", dev->minor);
-               cmd->stop_src = TRIG_NONE;
-               cmd->scan_end_src = TRIG_NONE;
-               err++;
+               err |= -EINVAL;
        }
+
        if (err)
                return 2;
 
@@ -1048,30 +975,22 @@ static int me4000_ai_do_cmd_test(struct comedi_device *dev,
         * Stage 3. Check if arguments are generally valid.
         */
        if (cmd->chanlist_len < 1) {
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                      "No channel list\n", dev->minor);
+               dev_err(dev->class_dev, "No channel list\n");
                cmd->chanlist_len = 1;
                err++;
        }
        if (init_ticks < 66) {
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                      "Start arg to low\n", dev->minor);
+               dev_err(dev->class_dev, "Start arg to low\n");
                cmd->start_arg = 2000;
                err++;
        }
        if (scan_ticks && scan_ticks < 67) {
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                      "Scan begin arg to low\n", dev->minor);
+               dev_err(dev->class_dev, "Scan begin arg to low\n");
                cmd->scan_begin_arg = 2031;
                err++;
        }
        if (chan_ticks < 66) {
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                      "Convert arg to low\n", dev->minor);
+               dev_err(dev->class_dev, "Convert arg to low\n");
                cmd->convert_arg = 2000;
                err++;
        }
@@ -1088,23 +1007,17 @@ static int me4000_ai_do_cmd_test(struct comedi_device *dev,
 
                /* Check timer arguments */
                if (init_ticks < ME4000_AI_MIN_TICKS) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                              "Invalid start arg\n", dev->minor);
+                       dev_err(dev->class_dev, "Invalid start arg\n");
                        cmd->start_arg = 2000;  /*  66 ticks at least */
                        err++;
                }
                if (chan_ticks < ME4000_AI_MIN_TICKS) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                              "Invalid convert arg\n", dev->minor);
+                       dev_err(dev->class_dev, "Invalid convert arg\n");
                        cmd->convert_arg = 2000;        /*  66 ticks at least */
                        err++;
                }
                if (scan_ticks <= cmd->chanlist_len * chan_ticks) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                              "Invalid scan end arg\n", dev->minor);
+                       dev_err(dev->class_dev, "Invalid scan end arg\n");
 
                        /*  At least one tick more */
                        cmd->scan_end_arg = 2000 * cmd->chanlist_len + 31;
@@ -1116,16 +1029,12 @@ static int me4000_ai_do_cmd_test(struct comedi_device *dev,
 
                /* Check timer arguments */
                if (init_ticks < ME4000_AI_MIN_TICKS) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                              "Invalid start arg\n", dev->minor);
+                       dev_err(dev->class_dev, "Invalid start arg\n");
                        cmd->start_arg = 2000;  /*  66 ticks at least */
                        err++;
                }
                if (chan_ticks < ME4000_AI_MIN_TICKS) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                              "Invalid convert arg\n", dev->minor);
+                       dev_err(dev->class_dev, "Invalid convert arg\n");
                        cmd->convert_arg = 2000;        /*  66 ticks at least */
                        err++;
                }
@@ -1135,23 +1044,17 @@ static int me4000_ai_do_cmd_test(struct comedi_device *dev,
 
                /* Check timer arguments */
                if (init_ticks < ME4000_AI_MIN_TICKS) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                              "Invalid start arg\n", dev->minor);
+                       dev_err(dev->class_dev, "Invalid start arg\n");
                        cmd->start_arg = 2000;  /*  66 ticks at least */
                        err++;
                }
                if (chan_ticks < ME4000_AI_MIN_TICKS) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                              "Invalid convert arg\n", dev->minor);
+                       dev_err(dev->class_dev, "Invalid convert arg\n");
                        cmd->convert_arg = 2000;        /*  66 ticks at least */
                        err++;
                }
                if (scan_ticks <= cmd->chanlist_len * chan_ticks) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                              "Invalid scan end arg\n", dev->minor);
+                       dev_err(dev->class_dev, "Invalid scan end arg\n");
 
                        /*  At least one tick more */
                        cmd->scan_end_arg = 2000 * cmd->chanlist_len + 31;
@@ -1163,16 +1066,12 @@ static int me4000_ai_do_cmd_test(struct comedi_device *dev,
 
                /* Check timer arguments */
                if (init_ticks < ME4000_AI_MIN_TICKS) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                              "Invalid start arg\n", dev->minor);
+                       dev_err(dev->class_dev, "Invalid start arg\n");
                        cmd->start_arg = 2000;  /*  66 ticks at least */
                        err++;
                }
                if (chan_ticks < ME4000_AI_MIN_TICKS) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                              "Invalid convert arg\n", dev->minor);
+                       dev_err(dev->class_dev, "Invalid convert arg\n");
                        cmd->convert_arg = 2000;        /*  66 ticks at least */
                        err++;
                }
@@ -1182,16 +1081,12 @@ static int me4000_ai_do_cmd_test(struct comedi_device *dev,
 
                /* Check timer arguments */
                if (init_ticks < ME4000_AI_MIN_TICKS) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                              "Invalid start arg\n", dev->minor);
+                       dev_err(dev->class_dev, "Invalid start arg\n");
                        cmd->start_arg = 2000;  /*  66 ticks at least */
                        err++;
                }
                if (chan_ticks < ME4000_AI_MIN_TICKS) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                              "Invalid convert arg\n", dev->minor);
+                       dev_err(dev->class_dev, "Invalid convert arg\n");
                        cmd->convert_arg = 2000;        /*  66 ticks at least */
                        err++;
                }
@@ -1201,27 +1096,21 @@ static int me4000_ai_do_cmd_test(struct comedi_device *dev,
 
                /* Check timer arguments */
                if (init_ticks < ME4000_AI_MIN_TICKS) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                              "Invalid start arg\n", dev->minor);
+                       dev_err(dev->class_dev, "Invalid start arg\n");
                        cmd->start_arg = 2000;  /*  66 ticks at least */
                        err++;
                }
        }
        if (cmd->stop_src == TRIG_COUNT) {
                if (cmd->stop_arg == 0) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                              "Invalid stop arg\n", dev->minor);
+                       dev_err(dev->class_dev, "Invalid stop arg\n");
                        cmd->stop_arg = 1;
                        err++;
                }
        }
        if (cmd->scan_end_src == TRIG_COUNT) {
                if (cmd->scan_end_arg == 0) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_do_cmd_test(): "
-                              "Invalid scan end arg\n", dev->minor);
+                       dev_err(dev->class_dev, "Invalid scan end arg\n");
                        cmd->scan_end_arg = 1;
                        err++;
                }
@@ -1256,9 +1145,7 @@ static irqreturn_t me4000_ai_isr(int irq, void *dev_id)
 
        /* Check if irq number is right */
        if (irq != dev->irq) {
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_ai_isr(): "
-                      "Incorrect interrupt num: %d\n", dev->minor, irq);
+               dev_err(dev->class_dev, "Incorrect interrupt num: %d\n", irq);
                return IRQ_HANDLED;
        }
 
@@ -1283,9 +1170,7 @@ static irqreturn_t me4000_ai_isr(int irq, void *dev_id)
 
                        s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
 
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_isr(): "
-                              "FIFO overflow\n", dev->minor);
+                       dev_err(dev->class_dev, "FIFO overflow\n");
                } else if ((tmp & ME4000_AI_STATUS_BIT_FF_DATA)
                           && !(tmp & ME4000_AI_STATUS_BIT_HF_DATA)
                           && (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) {
@@ -1293,9 +1178,8 @@ static irqreturn_t me4000_ai_isr(int irq, void *dev_id)
 
                        c = ME4000_AI_FIFO_COUNT / 2;
                } else {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_isr(): "
-                              "Can't determine state of fifo\n", dev->minor);
+                       dev_err(dev->class_dev,
+                               "Can't determine state of fifo\n");
                        c = 0;
 
                        /*
@@ -1309,9 +1193,7 @@ static irqreturn_t me4000_ai_isr(int irq, void *dev_id)
 
                        s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
 
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_ai_isr(): "
-                              "Undefined FIFO state\n", dev->minor);
+                       dev_err(dev->class_dev, "Undefined FIFO state\n");
                }
 
                for (i = 0; i < c; i++) {
@@ -1331,9 +1213,7 @@ static irqreturn_t me4000_ai_isr(int irq, void *dev_id)
 
                                s->async->events |= COMEDI_CB_OVERFLOW;
 
-                               printk(KERN_ERR
-                                      "comedi%d: me4000: me4000_ai_isr(): "
-                                      "Buffer overflow\n", dev->minor);
+                               dev_err(dev->class_dev, "Buffer overflow\n");
 
                                break;
                        }
@@ -1367,9 +1247,7 @@ static irqreturn_t me4000_ai_isr(int irq, void *dev_id)
                        lval ^= 0x8000;
 
                        if (!comedi_buf_put(s->async, lval)) {
-                               printk(KERN_ERR
-                                      "comedi%d: me4000: me4000_ai_isr(): "
-                                      "Buffer overflow\n", dev->minor);
+                               dev_err(dev->class_dev, "Buffer overflow\n");
                                s->async->events |= COMEDI_CB_OVERFLOW;
                                break;
                        }
@@ -1406,30 +1284,23 @@ static int me4000_ao_insn_write(struct comedi_device *dev,
        if (insn->n == 0) {
                return 0;
        } else if (insn->n > 1) {
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_ao_insn_write(): "
-                      "Invalid instruction length %d\n", dev->minor, insn->n);
+               dev_err(dev->class_dev, "Invalid instruction length %d\n",
+                       insn->n);
                return -EINVAL;
        }
 
        if (chan >= thisboard->ao_nchan) {
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_ao_insn_write(): "
-                      "Invalid channel %d\n", dev->minor, insn->n);
+               dev_err(dev->class_dev, "Invalid channel %d\n", insn->n);
                return -EINVAL;
        }
 
        if (rang != 0) {
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_ao_insn_write(): "
-                      "Invalid range %d\n", dev->minor, insn->n);
+               dev_err(dev->class_dev, "Invalid range %d\n", insn->n);
                return -EINVAL;
        }
 
        if (aref != AREF_GROUND && aref != AREF_COMMON) {
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_ao_insn_write(): "
-                      "Invalid aref %d\n", dev->minor, insn->n);
+               dev_err(dev->class_dev, "Invalid aref %d\n", insn->n);
                return -EINVAL;
        }
 
@@ -1460,9 +1331,7 @@ static int me4000_ao_insn_read(struct comedi_device *dev,
        if (insn->n == 0) {
                return 0;
        } else if (insn->n > 1) {
-               printk
-                   ("comedi%d: me4000: me4000_ao_insn_read(): "
-                    "Invalid instruction length\n", dev->minor);
+               dev_err(dev->class_dev, "Invalid instruction length\n");
                return -EINVAL;
        }
 
@@ -1614,134 +1483,38 @@ static int me4000_dio_insn_config(struct comedi_device *dev,
   Counter section
   ===========================================================================*/
 
-static int cnt_reset(struct comedi_device *dev, unsigned int channel)
-{
-       struct me4000_info *info = dev->private;
-
-       switch (channel) {
-       case 0:
-               outb(0x30, info->timer_regbase + ME4000_CNT_CTRL_REG);
-               outb(0x00, info->timer_regbase + ME4000_CNT_COUNTER_0_REG);
-               outb(0x00, info->timer_regbase + ME4000_CNT_COUNTER_0_REG);
-               break;
-       case 1:
-               outb(0x70, info->timer_regbase + ME4000_CNT_CTRL_REG);
-               outb(0x00, info->timer_regbase + ME4000_CNT_COUNTER_1_REG);
-               outb(0x00, info->timer_regbase + ME4000_CNT_COUNTER_1_REG);
-               break;
-       case 2:
-               outb(0xB0, info->timer_regbase + ME4000_CNT_CTRL_REG);
-               outb(0x00, info->timer_regbase + ME4000_CNT_COUNTER_2_REG);
-               outb(0x00, info->timer_regbase + ME4000_CNT_COUNTER_2_REG);
-               break;
-       default:
-               printk(KERN_ERR
-                      "comedi%d: me4000: cnt_reset(): Invalid channel\n",
-                      dev->minor);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int cnt_config(struct comedi_device *dev, unsigned int channel,
-                     unsigned int mode)
-{
-       struct me4000_info *info = dev->private;
-       int tmp = 0;
-
-       switch (channel) {
-       case 0:
-               tmp |= ME4000_CNT_COUNTER_0;
-               break;
-       case 1:
-               tmp |= ME4000_CNT_COUNTER_1;
-               break;
-       case 2:
-               tmp |= ME4000_CNT_COUNTER_2;
-               break;
-       default:
-               printk(KERN_ERR
-                      "comedi%d: me4000: cnt_config(): Invalid channel\n",
-                      dev->minor);
-               return -EINVAL;
-       }
-
-       switch (mode) {
-       case 0:
-               tmp |= ME4000_CNT_MODE_0;
-               break;
-       case 1:
-               tmp |= ME4000_CNT_MODE_1;
-               break;
-       case 2:
-               tmp |= ME4000_CNT_MODE_2;
-               break;
-       case 3:
-               tmp |= ME4000_CNT_MODE_3;
-               break;
-       case 4:
-               tmp |= ME4000_CNT_MODE_4;
-               break;
-       case 5:
-               tmp |= ME4000_CNT_MODE_5;
-               break;
-       default:
-               printk(KERN_ERR
-                      "comedi%d: me4000: cnt_config(): Invalid counter mode\n",
-                      dev->minor);
-               return -EINVAL;
-       }
-
-       /* Write the control word */
-       tmp |= 0x30;
-       outb(tmp, info->timer_regbase + ME4000_CNT_CTRL_REG);
-
-       return 0;
-}
-
 static int me4000_cnt_insn_config(struct comedi_device *dev,
                                  struct comedi_subdevice *s,
-                                 struct comedi_insn *insn, unsigned int *data)
+                                 struct comedi_insn *insn,
+                                 unsigned int *data)
 {
-
+       struct me4000_info *info = dev->private;
        int err;
 
        switch (data[0]) {
        case GPCT_RESET:
-               if (insn->n != 1) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_cnt_insn_config(): "
-                              "Invalid instruction length%d\n",
-                              dev->minor, insn->n);
+               if (insn->n != 1)
                        return -EINVAL;
-               }
 
-               err = cnt_reset(dev, insn->chanspec);
+               err = i8254_load(info->timer_regbase, 0, insn->chanspec, 0,
+                               I8254_MODE0 | I8254_BINARY);
                if (err)
                        return err;
                break;
        case GPCT_SET_OPERATION:
-               if (insn->n != 2) {
-                       printk(KERN_ERR
-                              "comedi%d: me4000: me4000_cnt_insn_config(): "
-                              "Invalid instruction length%d\n",
-                              dev->minor, insn->n);
+               if (insn->n != 2)
                        return -EINVAL;
-               }
 
-               err = cnt_config(dev, insn->chanspec, data[1]);
+               err = i8254_set_mode(info->timer_regbase, 0, insn->chanspec,
+                               (data[1] << 1) | I8254_BINARY);
                if (err)
                        return err;
                break;
        default:
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_cnt_insn_config(): "
-                      "Invalid instruction\n", dev->minor);
                return -EINVAL;
        }
 
-       return 2;
+       return insn->n;
 }
 
 static int me4000_cnt_insn_read(struct comedi_device *dev,
@@ -1749,45 +1522,17 @@ static int me4000_cnt_insn_read(struct comedi_device *dev,
                                struct comedi_insn *insn, unsigned int *data)
 {
        struct me4000_info *info = dev->private;
-       unsigned short tmp;
 
        if (insn->n == 0)
                return 0;
 
        if (insn->n > 1) {
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_cnt_insn_read(): "
-                      "Invalid instruction length %d\n",
-                      dev->minor, insn->n);
+               dev_err(dev->class_dev, "Invalid instruction length %d\n",
+                       insn->n);
                return -EINVAL;
        }
 
-       switch (insn->chanspec) {
-       case 0:
-               tmp = inb(info->timer_regbase + ME4000_CNT_COUNTER_0_REG);
-               data[0] = tmp;
-               tmp = inb(info->timer_regbase + ME4000_CNT_COUNTER_0_REG);
-               data[0] |= tmp << 8;
-               break;
-       case 1:
-               tmp = inb(info->timer_regbase + ME4000_CNT_COUNTER_1_REG);
-               data[0] = tmp;
-               tmp = inb(info->timer_regbase + ME4000_CNT_COUNTER_1_REG);
-               data[0] |= tmp << 8;
-               break;
-       case 2:
-               tmp = inb(info->timer_regbase + ME4000_CNT_COUNTER_2_REG);
-               data[0] = tmp;
-               tmp = inb(info->timer_regbase + ME4000_CNT_COUNTER_2_REG);
-               data[0] |= tmp << 8;
-               break;
-       default:
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_cnt_insn_read(): "
-                      "Invalid channel %d\n",
-                      dev->minor, insn->chanspec);
-               return -EINVAL;
-       }
+       data[0] = i8254_read(info->timer_regbase, 0, insn->chanspec);
 
        return 1;
 }
@@ -1797,58 +1542,70 @@ static int me4000_cnt_insn_write(struct comedi_device *dev,
                                 struct comedi_insn *insn, unsigned int *data)
 {
        struct me4000_info *info = dev->private;
-       unsigned short tmp;
 
        if (insn->n == 0) {
                return 0;
        } else if (insn->n > 1) {
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_cnt_insn_write(): "
-                      "Invalid instruction length %d\n",
-                      dev->minor, insn->n);
+               dev_err(dev->class_dev, "Invalid instruction length %d\n",
+                       insn->n);
                return -EINVAL;
        }
 
-       switch (insn->chanspec) {
-       case 0:
-               tmp = data[0] & 0xFF;
-               outb(tmp, info->timer_regbase + ME4000_CNT_COUNTER_0_REG);
-               tmp = (data[0] >> 8) & 0xFF;
-               outb(tmp, info->timer_regbase + ME4000_CNT_COUNTER_0_REG);
-               break;
-       case 1:
-               tmp = data[0] & 0xFF;
-               outb(tmp, info->timer_regbase + ME4000_CNT_COUNTER_1_REG);
-               tmp = (data[0] >> 8) & 0xFF;
-               outb(tmp, info->timer_regbase + ME4000_CNT_COUNTER_1_REG);
-               break;
-       case 2:
-               tmp = data[0] & 0xFF;
-               outb(tmp, info->timer_regbase + ME4000_CNT_COUNTER_2_REG);
-               tmp = (data[0] >> 8) & 0xFF;
-               outb(tmp, info->timer_regbase + ME4000_CNT_COUNTER_2_REG);
-               break;
-       default:
-               printk(KERN_ERR
-                      "comedi%d: me4000: me4000_cnt_insn_write(): "
-                      "Invalid channel %d\n",
-                      dev->minor, insn->chanspec);
-               return -EINVAL;
-       }
+       i8254_write(info->timer_regbase, 0, insn->chanspec, data[0]);
 
        return 1;
 }
 
-static int me4000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+static const void *me4000_find_boardinfo(struct comedi_device *dev,
+                                        struct pci_dev *pcidev)
 {
        const struct me4000_board *thisboard;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(me4000_boards); i++) {
+               thisboard = &me4000_boards[i];
+               if (thisboard->device_id == pcidev->device)
+                       return thisboard;
+       }
+       return NULL;
+}
+
+static int me4000_attach_pci(struct comedi_device *dev,
+                            struct pci_dev *pcidev)
+{
+       const struct me4000_board *thisboard;
+       struct me4000_info *info;
        struct comedi_subdevice *s;
        int result;
 
-       result = me4000_probe(dev, it);
+       comedi_set_hw_dev(dev, &pcidev->dev);
+
+       thisboard = me4000_find_boardinfo(dev, pcidev);
+       if (!thisboard)
+               return -ENODEV;
+       dev->board_ptr = thisboard;
+       dev->board_name = thisboard->name;
+
+       result = alloc_private(dev, sizeof(*info));
        if (result)
                return result;
-       thisboard = comedi_board(dev);
+       info = dev->private;
+
+       result = comedi_pci_enable(pcidev, dev->board_name);
+       if (result)
+               return result;
+
+       info->plx_regbase = pci_resource_start(pcidev, 1);
+       dev->iobase = pci_resource_start(pcidev, 2);
+       info->timer_regbase = pci_resource_start(pcidev, 3);
+       if (!info->plx_regbase || !dev->iobase || !info->timer_regbase)
+               return -ENODEV;
+
+       result = xilinx_download(dev);
+       if (result)
+               return result;
+
+       me4000_reset(dev);
 
        result = comedi_alloc_subdevices(dev, 4);
        if (result)
@@ -1870,23 +1627,22 @@ static int me4000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
                s->range_table = &me4000_ai_range;
                s->insn_read = me4000_ai_insn_read;
 
-               if (dev->irq > 0) {
-                       if (request_irq(dev->irq, me4000_ai_isr,
-                                       IRQF_SHARED, "ME-4000", dev)) {
-                               printk
-                                   ("comedi%d: me4000: me4000_attach(): "
-                                    "Unable to allocate irq\n", dev->minor);
+               if (pcidev->irq > 0) {
+                       if (request_irq(pcidev->irq, me4000_ai_isr,
+                                       IRQF_SHARED, dev->board_name, dev)) {
+                               dev_warn(dev->class_dev,
+                                       "request_irq failed\n");
                        } else {
                                dev->read_subdev = s;
                                s->subdev_flags |= SDF_CMD_READ;
                                s->cancel = me4000_ai_cancel;
                                s->do_cmdtest = me4000_ai_do_cmd_test;
                                s->do_cmd = me4000_ai_do_cmd;
+
+                               dev->irq = pcidev->irq;
                        }
                } else {
-                       printk(KERN_WARNING
-                              "comedi%d: me4000: me4000_attach(): "
-                              "No interrupt available\n", dev->minor);
+                       dev_warn(dev->class_dev, "No interrupt available\n");
                }
        } else {
                s->type = COMEDI_SUBD_UNUSED;
@@ -1903,7 +1659,7 @@ static int me4000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
                s->subdev_flags = SDF_WRITEABLE | SDF_COMMON | SDF_GROUND;
                s->n_chan = thisboard->ao_nchan;
                s->maxdata = 0xFFFF;    /*  16 bit DAC */
-               s->range_table = &me4000_ao_range;
+               s->range_table = &range_bipolar10;
                s->insn_write = me4000_ao_insn_write;
                s->insn_read = me4000_ao_insn_read;
        } else {
@@ -1961,14 +1717,14 @@ static int me4000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 
 static void me4000_detach(struct comedi_device *dev)
 {
-       struct me4000_info *info = dev->private;
-
-       if (info) {
-               if (info->pci_dev_p) {
-                       reset_board(dev);
-                       if (info->plx_regbase)
-                               comedi_pci_disable(info->pci_dev_p);
-                       pci_dev_put(info->pci_dev_p);
+       struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+
+       if (dev->irq)
+               free_irq(dev->irq, dev);
+       if (pcidev) {
+               if (dev->iobase) {
+                       me4000_reset(dev);
+                       comedi_pci_disable(pcidev);
                }
        }
 }
@@ -1976,7 +1732,7 @@ static void me4000_detach(struct comedi_device *dev)
 static struct comedi_driver me4000_driver = {
        .driver_name    = "me4000",
        .module         = THIS_MODULE,
-       .attach         = me4000_attach,
+       .attach_pci     = me4000_attach_pci,
        .detach         = me4000_detach,
 };