Staging: comedi: add adv_pci1710 driver
authorMichal Dobes <dobes@tesnet.cz>
Thu, 12 Feb 2009 23:46:45 +0000 (15:46 -0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 3 Apr 2009 21:53:42 +0000 (14:53 -0700)
For Advantech cards:
PCI-1710, PCI-1710HG, PCI-1711, PCI-1713, PCI-1720,

From: Michal Dobes <dobes@tesnet.cz>
Cc: David Schleef <ds@schleef.org>
Cc: Frank Mori Hess <fmhess@users.sourceforge.net>
Cc: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/staging/comedi/drivers/adv_pci1710.c [new file with mode: 0644]

diff --git a/drivers/staging/comedi/drivers/adv_pci1710.c b/drivers/staging/comedi/drivers/adv_pci1710.c
new file mode 100644 (file)
index 0000000..edfcfd5
--- /dev/null
@@ -0,0 +1,1568 @@
+/*
+ * comedi/drivers/adv_pci1710.c
+ *
+ * Author: Michal Dobes <dobes@tesnet.cz>
+ *
+ * Thanks to ZhenGang Shang <ZhenGang.Shang@Advantech.com.cn>
+ * for testing and informations.
+ *
+ *  hardware driver for Advantech cards:
+ *   card:   PCI-1710, PCI-1710HG, PCI-1711, PCI-1713, PCI-1720, PCI-1731
+ *   driver: pci1710,  pci1710hg,  pci1711,  pci1713,  pci1720,  pci1731
+ *
+ * Options:
+ *  [0] - PCI bus number - if bus number and slot number are 0,
+ *                         then driver search for first unused card
+ *  [1] - PCI slot number
+ *
+*/
+/*
+Driver: adv_pci1710
+Description: Advantech PCI-1710, PCI-1710HG, PCI-1711, PCI-1713,
+             Advantech PCI-1720, PCI-1731
+Author: Michal Dobes <dobes@tesnet.cz>
+Devices: [Advantech] PCI-1710 (adv_pci1710), PCI-1710HG (pci1710hg),
+  PCI-1711 (adv_pci1710), PCI-1713, PCI-1720,
+  PCI-1731
+Status: works
+
+This driver supports AI, AO, DI and DO subdevices.
+AI subdevice supports cmd and insn interface,
+other subdevices support only insn interface.
+
+The PCI-1710 and PCI-1710HG have the same PCI device ID, so the
+driver cannot distinguish between them, as would be normal for a
+PCI driver.
+
+Configuration options:
+  [0] - PCI bus of device (optional)
+  [1] - PCI slot of device (optional)
+          If bus/slot is not specified, the first available PCI
+          device will be used.
+*/
+
+#include "../comedidev.h"
+
+#include "comedi_pci.h"
+
+#include "8253.h"
+#include "amcc_s5933.h"
+
+#define PCI171x_PARANOIDCHECK  /* if defined, then is used code which control correct channel number on every 12 bit sample */
+
+#undef PCI171X_EXTDEBUG
+
+#define DRV_NAME "adv_pci1710"
+
+#undef DPRINTK
+#ifdef PCI171X_EXTDEBUG
+#define DPRINTK(fmt, args...) rt_printk(fmt, ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+// hardware types of the cards
+#define TYPE_PCI171X   0
+#define TYPE_PCI1713   2
+#define TYPE_PCI1720   3
+
+#define IORANGE_171x   32
+#define IORANGE_1720   16
+
+#define PCI171x_AD_DATA         0      /* R:   A/D data */
+#define PCI171x_SOFTTRG         0      /* W:   soft trigger for A/D */
+#define PCI171x_RANGE   2      /* W:   A/D gain/range register */
+#define PCI171x_MUX     4      /* W:   A/D multiplexor control */
+#define PCI171x_STATUS  6      /* R:   status register */
+#define PCI171x_CONTROL         6      /* W:   control register */
+#define PCI171x_CLRINT  8      /* W:   clear interrupts request */
+#define PCI171x_CLRFIFO         9      /* W:   clear FIFO */
+#define PCI171x_DA1    10      /* W:   D/A register */
+#define PCI171x_DA2    12      /* W:   D/A register */
+#define PCI171x_DAREF  14      /* W:   D/A reference control */
+#define PCI171x_DI     16      /* R:   digi inputs */
+#define PCI171x_DO     16      /* R:   digi inputs */
+#define PCI171x_CNT0   24      /* R/W: 8254 couter 0 */
+#define PCI171x_CNT1   26      /* R/W: 8254 couter 1 */
+#define PCI171x_CNT2   28      /* R/W: 8254 couter 2 */
+#define PCI171x_CNTCTRL        30      /* W:   8254 counter control */
+
+// upper bits from status register (PCI171x_STATUS) (lower is same woth control reg)
+#define        Status_FE       0x0100  /* 1=FIFO is empty */
+#define Status_FH      0x0200  /* 1=FIFO is half full */
+#define Status_FF      0x0400  /* 1=FIFO is full, fatal error */
+#define Status_IRQ     0x0800  /* 1=IRQ occured */
+// bits from control register (PCI171x_CONTROL)
+#define Control_CNT0   0x0040  /* 1=CNT0 have external source, 0=have internal 100kHz source */
+#define Control_ONEFH  0x0020  /* 1=IRQ on FIFO is half full, 0=every sample */
+#define Control_IRQEN  0x0010  /* 1=enable IRQ */
+#define Control_GATE   0x0008  /* 1=enable external trigger GATE (8254?) */
+#define Control_EXT    0x0004  /* 1=external trigger source */
+#define Control_PACER  0x0002  /* 1=enable internal 8254 trigger source */
+#define Control_SW     0x0001  /* 1=enable software trigger source */
+// bits from counter control register (PCI171x_CNTCTRL)
+#define Counter_BCD     0x0001 /* 0 = binary counter, 1 = BCD counter */
+#define Counter_M0      0x0002 /* M0-M2 select modes 0-5 */
+#define Counter_M1      0x0004 /* 000 = mode 0, 010 = mode 2 ... */
+#define Counter_M2      0x0008
+#define Counter_RW0     0x0010 /* RW0/RW1 select read/write mode */
+#define Counter_RW1     0x0020
+#define Counter_SC0     0x0040 /* Select Counter. Only 00 or 11 may */
+#define Counter_SC1     0x0080 /* be used, 00 for CNT0, 11 for read-back command */
+
+#define PCI1720_DA0     0      /* W:   D/A register 0 */
+#define PCI1720_DA1     2      /* W:   D/A register 1 */
+#define PCI1720_DA2     4      /* W:   D/A register 2 */
+#define PCI1720_DA3     6      /* W:   D/A register 3 */
+#define PCI1720_RANGE   8      /* R/W: D/A range register */
+#define PCI1720_SYNCOUT         9      /* W:   D/A synchronized output register */
+#define PCI1720_SYNCONT        15      /* R/W: D/A synchronized control */
+
+// D/A synchronized control (PCI1720_SYNCONT)
+#define Syncont_SC0     1      /* set synchronous output mode */
+
+static const comedi_lrange range_pci1710_3 = { 9, {
+                       BIP_RANGE(5),
+                       BIP_RANGE(2.5),
+                       BIP_RANGE(1.25),
+                       BIP_RANGE(0.625),
+                       BIP_RANGE(10),
+                       UNI_RANGE(10),
+                       UNI_RANGE(5),
+                       UNI_RANGE(2.5),
+                       UNI_RANGE(1.25)
+       }
+};
+
+static const char range_codes_pci1710_3[] =
+       { 0x00, 0x01, 0x02, 0x03, 0x04, 0x10, 0x11, 0x12, 0x13 };
+
+static const comedi_lrange range_pci1710hg = { 12, {
+                       BIP_RANGE(5),
+                       BIP_RANGE(0.5),
+                       BIP_RANGE(0.05),
+                       BIP_RANGE(0.005),
+                       BIP_RANGE(10),
+                       BIP_RANGE(1),
+                       BIP_RANGE(0.1),
+                       BIP_RANGE(0.01),
+                       UNI_RANGE(10),
+                       UNI_RANGE(1),
+                       UNI_RANGE(0.1),
+                       UNI_RANGE(0.01)
+       }
+};
+
+static const char range_codes_pci1710hg[] =
+       { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12,
+               0x13 };
+
+static const comedi_lrange range_pci17x1 = { 5, {
+                       BIP_RANGE(10),
+                       BIP_RANGE(5),
+                       BIP_RANGE(2.5),
+                       BIP_RANGE(1.25),
+                       BIP_RANGE(0.625)
+       }
+};
+
+static const char range_codes_pci17x1[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
+
+static const comedi_lrange range_pci1720 = { 4, {
+                       UNI_RANGE(5),
+                       UNI_RANGE(10),
+                       BIP_RANGE(5),
+                       BIP_RANGE(10)
+       }
+};
+
+static const comedi_lrange range_pci171x_da = { 2, {
+                       UNI_RANGE(5),
+                       UNI_RANGE(10),
+       }
+};
+
+static int pci1710_attach(comedi_device * dev, comedi_devconfig * it);
+static int pci1710_detach(comedi_device * dev);
+
+typedef struct {
+       const char *name;       // board name
+       int device_id;
+       int iorange;            // I/O range len
+       char have_irq;          // 1=card support IRQ
+       char cardtype;          // 0=1710& co. 2=1713, ...
+       int n_aichan;           // num of A/D chans
+       int n_aichand;          // num of A/D chans in diff mode
+       int n_aochan;           // num of D/A chans
+       int n_dichan;           // num of DI chans
+       int n_dochan;           // num of DO chans
+       int n_counter;          // num of counters
+       int ai_maxdata;         // resolution of A/D
+       int ao_maxdata;         // resolution of D/A
+       const comedi_lrange *rangelist_ai;      // rangelist for A/D
+       const char *rangecode_ai;       // range codes for programming
+       const comedi_lrange *rangelist_ao;      // rangelist for D/A
+       unsigned int ai_ns_min; // max sample speed of card v ns
+       unsigned int fifo_half_size;    // size of FIFO/2
+} boardtype;
+
+static DEFINE_PCI_DEVICE_TABLE(pci1710_pci_table) = {
+       {PCI_VENDOR_ID_ADVANTECH, 0x1710, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_ADVANTECH, 0x1711, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_ADVANTECH, 0x1713, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_ADVANTECH, 0x1720, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_ADVANTECH, 0x1731, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0}
+};
+
+MODULE_DEVICE_TABLE(pci, pci1710_pci_table);
+
+static const boardtype boardtypes[] = {
+       {"pci1710", 0x1710,
+               IORANGE_171x, 1, TYPE_PCI171X,
+               16, 8, 2, 16, 16, 1, 0x0fff, 0x0fff,
+               &range_pci1710_3, range_codes_pci1710_3,
+               &range_pci171x_da,
+               10000, 2048},
+       {"pci1710hg", 0x1710,
+               IORANGE_171x, 1, TYPE_PCI171X,
+               16, 8, 2, 16, 16, 1, 0x0fff, 0x0fff,
+               &range_pci1710hg, range_codes_pci1710hg,
+               &range_pci171x_da,
+               10000, 2048},
+       {"pci1711", 0x1711,
+               IORANGE_171x, 1, TYPE_PCI171X,
+               16, 0, 2, 16, 16, 1, 0x0fff, 0x0fff,
+               &range_pci17x1, range_codes_pci17x1, &range_pci171x_da,
+               10000, 512},
+       {"pci1713", 0x1713,
+               IORANGE_171x, 1, TYPE_PCI1713,
+               32, 16, 0, 0, 0, 0, 0x0fff, 0x0000,
+               &range_pci1710_3, range_codes_pci1710_3, NULL,
+               10000, 2048},
+       {"pci1720", 0x1720,
+               IORANGE_1720, 0, TYPE_PCI1720,
+               0, 0, 4, 0, 0, 0, 0x0000, 0x0fff,
+               NULL, NULL, &range_pci1720,
+               0, 0},
+       {"pci1731", 0x1731,
+               IORANGE_171x, 1, TYPE_PCI171X,
+               16, 0, 0, 16, 16, 0, 0x0fff, 0x0000,
+               &range_pci17x1, range_codes_pci17x1, NULL,
+               10000, 512},
+       // dummy entry corresponding to driver name
+       {.name = DRV_NAME},
+};
+
+#define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
+
+static comedi_driver driver_pci1710 = {
+       .driver_name = DRV_NAME,
+       .module = THIS_MODULE,
+       .attach = pci1710_attach,
+       .detach = pci1710_detach,
+       .num_names = n_boardtypes,
+       .board_name = &boardtypes[0].name,
+       .offset = sizeof(boardtype),
+};
+
+typedef struct {
+       struct pci_dev *pcidev; // ptr to PCI device
+       char valid;             // card is usable
+       char neverending_ai;    // we do unlimited AI
+       unsigned int CntrlReg;  // Control register
+       unsigned int i8254_osc_base;    // frequence of onboard oscilator
+       unsigned int ai_do;     // what do AI? 0=nothing, 1 to 4 mode
+       unsigned int ai_act_scan;       // how many scans we finished
+       unsigned int ai_act_chan;       // actual position in actual scan
+       unsigned int ai_buf_ptr;        // data buffer ptr in samples
+       unsigned char ai_eos;   // 1=EOS wake up
+       unsigned char ai_et;
+       unsigned int ai_et_CntrlReg;
+       unsigned int ai_et_MuxVal;
+       unsigned int ai_et_div1, ai_et_div2;
+       unsigned int act_chanlist[32];  // list of scaned channel
+       unsigned char act_chanlist_len; // len of scanlist
+       unsigned char act_chanlist_pos; // actual position in MUX list
+       unsigned char da_ranges;        // copy of D/A outpit range register
+       unsigned int ai_scans;  // len of scanlist
+       unsigned int ai_n_chan; // how many channels is measured
+       unsigned int *ai_chanlist;      // actaul chanlist
+       unsigned int ai_flags;  // flaglist
+       unsigned int ai_data_len;       // len of data buffer
+       sampl_t *ai_data;       // data buffer
+       unsigned int ai_timer1; // timers
+       unsigned int ai_timer2;
+       sampl_t ao_data[4];     // data output buffer
+       unsigned int cnt0_write_wait;   // after a write, wait for update of the internal state
+} pci1710_private;
+
+#define devpriv ((pci1710_private *)dev->private)
+#define this_board ((const boardtype *)dev->board_ptr)
+
+/*
+==============================================================================
+*/
+
+static int check_channel_list(comedi_device * dev, comedi_subdevice * s,
+       unsigned int *chanlist, unsigned int n_chan);
+static void setup_channel_list(comedi_device * dev, comedi_subdevice * s,
+       unsigned int *chanlist, unsigned int n_chan, unsigned int seglen);
+static void start_pacer(comedi_device * dev, int mode, unsigned int divisor1,
+       unsigned int divisor2);
+static int pci1710_reset(comedi_device * dev);
+static int pci171x_ai_cancel(comedi_device * dev, comedi_subdevice * s);
+
+static const unsigned int muxonechan[] = { 0x0000, 0x0101, 0x0202, 0x0303, 0x0404, 0x0505, 0x0606, 0x0707,     // used for gain list programming
+       0x0808, 0x0909, 0x0a0a, 0x0b0b, 0x0c0c, 0x0d0d, 0x0e0e, 0x0f0f,
+       0x1010, 0x1111, 0x1212, 0x1313, 0x1414, 0x1515, 0x1616, 0x1717,
+       0x1818, 0x1919, 0x1a1a, 0x1b1b, 0x1c1c, 0x1d1d, 0x1e1e, 0x1f1f
+};
+
+/*
+==============================================================================
+*/
+static int pci171x_insn_read_ai(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data)
+{
+       int n, timeout;
+#ifdef PCI171x_PARANOIDCHECK
+       unsigned int idata;
+#endif
+
+       DPRINTK("adv_pci1710 EDBG: BGN: pci171x_insn_read_ai(...)\n");
+       devpriv->CntrlReg &= Control_CNT0;
+       devpriv->CntrlReg |= Control_SW;        // set software trigger
+       outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
+       outb(0, dev->iobase + PCI171x_CLRFIFO);
+       outb(0, dev->iobase + PCI171x_CLRINT);
+
+       setup_channel_list(dev, s, &insn->chanspec, 1, 1);
+
+       DPRINTK("adv_pci1710 A ST=%4x IO=%x\n",
+               inw(dev->iobase + PCI171x_STATUS),
+               dev->iobase + PCI171x_STATUS);
+       for (n = 0; n < insn->n; n++) {
+               outw(0, dev->iobase + PCI171x_SOFTTRG); /* start conversion */
+               DPRINTK("adv_pci1710 B n=%d ST=%4x\n", n,
+                       inw(dev->iobase + PCI171x_STATUS));
+               //comedi_udelay(1);
+               DPRINTK("adv_pci1710 C n=%d ST=%4x\n", n,
+                       inw(dev->iobase + PCI171x_STATUS));
+               timeout = 100;
+               while (timeout--) {
+                       if (!(inw(dev->iobase + PCI171x_STATUS) & Status_FE))
+                               goto conv_finish;
+                       if (!(timeout % 10))
+                               DPRINTK("adv_pci1710 D n=%d tm=%d ST=%4x\n", n,
+                                       timeout,
+                                       inw(dev->iobase + PCI171x_STATUS));
+               }
+               comedi_error(dev, "A/D insn timeout");
+               outb(0, dev->iobase + PCI171x_CLRFIFO);
+               outb(0, dev->iobase + PCI171x_CLRINT);
+               data[n] = 0;
+               DPRINTK("adv_pci1710 EDBG: END: pci171x_insn_read_ai(...) n=%d\n", n);
+               return -ETIME;
+
+             conv_finish:
+#ifdef PCI171x_PARANOIDCHECK
+               idata = inw(dev->iobase + PCI171x_AD_DATA);
+               if (this_board->cardtype != TYPE_PCI1713)
+                       if ((idata & 0xf000) != devpriv->act_chanlist[0]) {
+                               comedi_error(dev, "A/D insn data droput!");
+                               return -ETIME;
+                       }
+               data[n] = idata & 0x0fff;
+#else
+               data[n] = inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff;
+#endif
+
+       }
+
+       outb(0, dev->iobase + PCI171x_CLRFIFO);
+       outb(0, dev->iobase + PCI171x_CLRINT);
+
+       DPRINTK("adv_pci1710 EDBG: END: pci171x_insn_read_ai(...) n=%d\n", n);
+       return n;
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_insn_write_ao(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data)
+{
+       int n, chan, range, ofs;
+
+       chan = CR_CHAN(insn->chanspec);
+       range = CR_RANGE(insn->chanspec);
+       if (chan) {
+               devpriv->da_ranges &= 0xfb;
+               devpriv->da_ranges |= (range << 2);
+               outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
+               ofs = PCI171x_DA2;
+       } else {
+               devpriv->da_ranges &= 0xfe;
+               devpriv->da_ranges |= range;
+               outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
+               ofs = PCI171x_DA1;
+       }
+
+       for (n = 0; n < insn->n; n++)
+               outw(data[n], dev->iobase + ofs);
+
+       devpriv->ao_data[chan] = data[n];
+
+       return n;
+
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_insn_read_ao(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data)
+{
+       int n, chan;
+
+       chan = CR_CHAN(insn->chanspec);
+       for (n = 0; n < insn->n; n++)
+               data[n] = devpriv->ao_data[chan];
+
+       return n;
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_insn_bits_di(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data)
+{
+       data[1] = inw(dev->iobase + PCI171x_DI);
+
+       return 2;
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_insn_bits_do(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data)
+{
+       if (data[0]) {
+               s->state &= ~data[0];
+               s->state |= (data[0] & data[1]);
+               outw(s->state, dev->iobase + PCI171x_DO);
+       }
+       data[1] = s->state;
+
+       return 2;
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_insn_counter_read(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data)
+{
+       unsigned int msb, lsb, ccntrl;
+       int i;
+
+       ccntrl = 0xD2;          /* count only */
+       for (i = 0; i < insn->n; i++) {
+               outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
+
+               lsb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
+               msb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
+
+               data[0] = lsb | (msb << 8);
+       }
+
+       return insn->n;
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_insn_counter_write(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data)
+{
+       uint msb, lsb, ccntrl, status;
+
+       lsb = data[0] & 0x00FF;
+       msb = (data[0] & 0xFF00) >> 8;
+
+       /* write lsb, then msb */
+       outw(lsb, dev->iobase + PCI171x_CNT0);
+       outw(msb, dev->iobase + PCI171x_CNT0);
+
+       if (devpriv->cnt0_write_wait) {
+               /* wait for the new count to be loaded */
+               ccntrl = 0xE2;
+               do {
+                       outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
+                       status = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
+               } while (status & 0x40);
+       }
+
+       return insn->n;
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_insn_counter_config(comedi_device * dev,
+       comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+{
+#ifdef unused
+       /* This doesn't work like a normal Comedi counter config */
+       uint ccntrl = 0;
+
+       devpriv->cnt0_write_wait = data[0] & 0x20;
+
+       /* internal or external clock? */
+       if (!(data[0] & 0x10)) {        /* internal */
+               devpriv->CntrlReg &= ~Control_CNT0;
+       } else {
+               devpriv->CntrlReg |= Control_CNT0;
+       }
+       outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
+
+       if (data[0] & 0x01)
+               ccntrl |= Counter_M0;
+       if (data[0] & 0x02)
+               ccntrl |= Counter_M1;
+       if (data[0] & 0x04)
+               ccntrl |= Counter_M2;
+       if (data[0] & 0x08)
+               ccntrl |= Counter_BCD;
+       ccntrl |= Counter_RW0;  /* set read/write mode */
+       ccntrl |= Counter_RW1;
+       outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
+#endif
+
+       return 1;
+}
+
+/*
+==============================================================================
+*/
+static int pci1720_insn_write_ao(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data)
+{
+       int n, rangereg, chan;
+
+       chan = CR_CHAN(insn->chanspec);
+       rangereg = devpriv->da_ranges & (~(0x03 << (chan << 1)));
+       rangereg |= (CR_RANGE(insn->chanspec) << (chan << 1));
+       if (rangereg != devpriv->da_ranges) {
+               outb(rangereg, dev->iobase + PCI1720_RANGE);
+               devpriv->da_ranges = rangereg;
+       }
+
+       for (n = 0; n < insn->n; n++) {
+               outw(data[n], dev->iobase + PCI1720_DA0 + (chan << 1));
+               outb(0, dev->iobase + PCI1720_SYNCOUT); // update outputs
+       }
+
+       devpriv->ao_data[chan] = data[n];
+
+       return n;
+}
+
+/*
+==============================================================================
+*/
+static void interrupt_pci1710_every_sample(void *d)
+{
+       comedi_device *dev = d;
+       comedi_subdevice *s = dev->subdevices + 0;
+       int m;
+#ifdef PCI171x_PARANOIDCHECK
+       sampl_t sampl;
+#endif
+
+       DPRINTK("adv_pci1710 EDBG: BGN: interrupt_pci1710_every_sample(...)\n");
+       m = inw(dev->iobase + PCI171x_STATUS);
+       if (m & Status_FE) {
+               rt_printk("comedi%d: A/D FIFO empty (%4x)\n", dev->minor, m);
+               pci171x_ai_cancel(dev, s);
+               s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+               comedi_event(dev, s);
+               return;
+       }
+       if (m & Status_FF) {
+               rt_printk
+                       ("comedi%d: A/D FIFO Full status (Fatal Error!) (%4x)\n",
+                       dev->minor, m);
+               pci171x_ai_cancel(dev, s);
+               s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+               comedi_event(dev, s);
+               return;
+       }
+
+       outb(0, dev->iobase + PCI171x_CLRINT);  // clear our INT request
+
+       DPRINTK("FOR ");
+       for (; !(inw(dev->iobase + PCI171x_STATUS) & Status_FE);) {
+#ifdef PCI171x_PARANOIDCHECK
+               sampl = inw(dev->iobase + PCI171x_AD_DATA);
+               DPRINTK("%04x:", sampl);
+               if (this_board->cardtype != TYPE_PCI1713)
+                       if ((sampl & 0xf000) !=
+                               devpriv->act_chanlist[s->async->cur_chan]) {
+                               rt_printk
+                                       ("comedi: A/D data dropout: received data from channel %d, expected %d!\n",
+                                       (sampl & 0xf000) >> 12,
+                                       (devpriv->act_chanlist[s->async->
+                                                       cur_chan] & 0xf000) >>
+                                       12);
+                               pci171x_ai_cancel(dev, s);
+                               s->async->events |=
+                                       COMEDI_CB_EOA | COMEDI_CB_ERROR;
+                               comedi_event(dev, s);
+                               return;
+                       }
+               DPRINTK("%8d %2d %8d~", s->async->buf_int_ptr,
+                       s->async->cur_chan, s->async->buf_int_count);
+               comedi_buf_put(s->async, sampl & 0x0fff);
+#else
+               comedi_buf_put(s->async,
+                       inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff);
+#endif
+               ++s->async->cur_chan;
+
+               if (s->async->cur_chan >= devpriv->ai_n_chan) {
+                       s->async->cur_chan = 0;
+               }
+
+               if (s->async->cur_chan == 0) {  // one scan done
+                       devpriv->ai_act_scan++;
+                       DPRINTK("adv_pci1710 EDBG: EOS1 bic %d bip %d buc %d bup %d\n", s->async->buf_int_count, s->async->buf_int_ptr, s->async->buf_user_count, s->async->buf_user_ptr);
+                       DPRINTK("adv_pci1710 EDBG: EOS2\n");
+                       if ((!devpriv->neverending_ai) && (devpriv->ai_act_scan >= devpriv->ai_scans)) {        // all data sampled
+                               pci171x_ai_cancel(dev, s);
+                               s->async->events |= COMEDI_CB_EOA;
+                               comedi_event(dev, s);
+                               return;
+                       }
+               }
+       }
+
+       outb(0, dev->iobase + PCI171x_CLRINT);  // clear our INT request
+       DPRINTK("adv_pci1710 EDBG: END: interrupt_pci1710_every_sample(...)\n");
+
+       comedi_event(dev, s);
+}
+
+/*
+==============================================================================
+*/
+static int move_block_from_fifo(comedi_device * dev, comedi_subdevice * s,
+       int n, int turn)
+{
+       int i, j;
+#ifdef PCI171x_PARANOIDCHECK
+       int sampl;
+#endif
+       DPRINTK("adv_pci1710 EDBG: BGN: move_block_from_fifo(...,%d,%d)\n", n,
+               turn);
+       j = s->async->cur_chan;
+       for (i = 0; i < n; i++) {
+#ifdef PCI171x_PARANOIDCHECK
+               sampl = inw(dev->iobase + PCI171x_AD_DATA);
+               if (this_board->cardtype != TYPE_PCI1713)
+                       if ((sampl & 0xf000) != devpriv->act_chanlist[j]) {
+                               rt_printk
+                                       ("comedi%d: A/D  FIFO data dropout: received data from channel %d, expected %d! (%d/%d/%d/%d/%d/%4x)\n",
+                                       dev->minor, (sampl & 0xf000) >> 12,
+                                       (devpriv->
+                                               act_chanlist[j] & 0xf000) >> 12,
+                                       i, j, devpriv->ai_act_scan, n, turn,
+                                       sampl);
+                               pci171x_ai_cancel(dev, s);
+                               s->async->events |=
+                                       COMEDI_CB_EOA | COMEDI_CB_ERROR;
+                               comedi_event(dev, s);
+                               return 1;
+                       }
+               comedi_buf_put(s->async, sampl & 0x0fff);
+#else
+               comedi_buf_put(s->async,
+                       inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff);
+#endif
+               j++;
+               if (j >= devpriv->ai_n_chan) {
+                       j = 0;
+                       devpriv->ai_act_scan++;
+               }
+       }
+       DPRINTK("adv_pci1710 EDBG: END: move_block_from_fifo(...)\n");
+       return 0;
+}
+
+/*
+==============================================================================
+*/
+static void interrupt_pci1710_half_fifo(void *d)
+{
+       comedi_device *dev = d;
+       comedi_subdevice *s = dev->subdevices + 0;
+       int m, samplesinbuf;
+
+       DPRINTK("adv_pci1710 EDBG: BGN: interrupt_pci1710_half_fifo(...)\n");
+       m = inw(dev->iobase + PCI171x_STATUS);
+       if (!(m & Status_FH)) {
+               rt_printk("comedi%d: A/D FIFO not half full! (%4x)\n",
+                       dev->minor, m);
+               pci171x_ai_cancel(dev, s);
+               s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+               comedi_event(dev, s);
+               return;
+       }
+       if (m & Status_FF) {
+               rt_printk
+                       ("comedi%d: A/D FIFO Full status (Fatal Error!) (%4x)\n",
+                       dev->minor, m);
+               pci171x_ai_cancel(dev, s);
+               s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+               comedi_event(dev, s);
+               return;
+       }
+
+       samplesinbuf = this_board->fifo_half_size;
+       if (samplesinbuf * sizeof(sampl_t) >= devpriv->ai_data_len) {
+               m = devpriv->ai_data_len / sizeof(sampl_t);
+               if (move_block_from_fifo(dev, s, m, 0))
+                       return;
+               samplesinbuf -= m;
+       }
+
+       if (samplesinbuf) {
+               if (move_block_from_fifo(dev, s, samplesinbuf, 1))
+                       return;
+       }
+
+       if (!devpriv->neverending_ai)
+               if (devpriv->ai_act_scan >= devpriv->ai_scans) {        /* all data sampled */
+                       pci171x_ai_cancel(dev, s);
+                       s->async->events |= COMEDI_CB_EOA;
+                       comedi_event(dev, s);
+                       return;
+               }
+       outb(0, dev->iobase + PCI171x_CLRINT);  // clear our INT request
+       DPRINTK("adv_pci1710 EDBG: END: interrupt_pci1710_half_fifo(...)\n");
+
+       comedi_event(dev, s);
+}
+
+/*
+==============================================================================
+*/
+static irqreturn_t interrupt_service_pci1710(int irq, void *d PT_REGS_ARG)
+{
+       comedi_device *dev = d;
+
+       DPRINTK("adv_pci1710 EDBG: BGN: interrupt_service_pci1710(%d,...)\n",
+               irq);
+       if (!dev->attached)     // is device attached?
+               return IRQ_NONE;        // no, exit
+
+       if (!(inw(dev->iobase + PCI171x_STATUS) & Status_IRQ))  // is this interrupt from our board?
+               return IRQ_NONE;        // no, exit
+
+       DPRINTK("adv_pci1710 EDBG: interrupt_service_pci1710() ST: %4x\n",
+               inw(dev->iobase + PCI171x_STATUS));
+
+       if (devpriv->ai_et) {   // Switch from initial TRIG_EXT to TRIG_xxx.
+               devpriv->ai_et = 0;
+               devpriv->CntrlReg &= Control_CNT0;
+               devpriv->CntrlReg |= Control_SW;        // set software trigger
+               outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
+               devpriv->CntrlReg = devpriv->ai_et_CntrlReg;
+               outb(0, dev->iobase + PCI171x_CLRFIFO);
+               outb(0, dev->iobase + PCI171x_CLRINT);
+               outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
+               outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
+               // start pacer
+               start_pacer(dev, 1, devpriv->ai_et_div1, devpriv->ai_et_div2);
+               return IRQ_HANDLED;
+       }
+       if (devpriv->ai_eos) {  // We use FIFO half full INT or not?
+               interrupt_pci1710_every_sample(d);
+       } else {
+               interrupt_pci1710_half_fifo(d);
+       }
+       DPRINTK("adv_pci1710 EDBG: END: interrupt_service_pci1710(...)\n");
+       return IRQ_HANDLED;
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_ai_docmd_and_mode(int mode, comedi_device * dev,
+       comedi_subdevice * s)
+{
+       unsigned int divisor1, divisor2;
+       unsigned int seglen;
+
+       DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_docmd_and_mode(%d,...)\n",
+               mode);
+       start_pacer(dev, -1, 0, 0);     // stop pacer
+
+       seglen = check_channel_list(dev, s, devpriv->ai_chanlist,
+               devpriv->ai_n_chan);
+       if (seglen < 1)
+               return -EINVAL;
+       setup_channel_list(dev, s, devpriv->ai_chanlist,
+               devpriv->ai_n_chan, seglen);
+
+       outb(0, dev->iobase + PCI171x_CLRFIFO);
+       outb(0, dev->iobase + PCI171x_CLRINT);
+
+       devpriv->ai_do = mode;
+
+       devpriv->ai_act_scan = 0;
+       s->async->cur_chan = 0;
+       devpriv->ai_buf_ptr = 0;
+       devpriv->neverending_ai = 0;
+
+       devpriv->CntrlReg &= Control_CNT0;
+       if ((devpriv->ai_flags & TRIG_WAKE_EOS)) {      // don't we want wake up every scan?            devpriv->ai_eos=1;
+               devpriv->ai_eos = 1;
+       } else {
+               devpriv->CntrlReg |= Control_ONEFH;
+               devpriv->ai_eos = 0;
+       }
+
+       if ((devpriv->ai_scans == 0) || (devpriv->ai_scans == -1)) {
+               devpriv->neverending_ai = 1;
+       }                       //well, user want neverending
+       else {
+               devpriv->neverending_ai = 0;
+       }
+       switch (mode) {
+       case 1:
+       case 2:
+               if (devpriv->ai_timer1 < this_board->ai_ns_min)
+                       devpriv->ai_timer1 = this_board->ai_ns_min;
+               devpriv->CntrlReg |= Control_PACER | Control_IRQEN;
+               if (mode == 2) {
+                       devpriv->ai_et_CntrlReg = devpriv->CntrlReg;
+                       devpriv->CntrlReg &=
+                               ~(Control_PACER | Control_ONEFH | Control_GATE);
+                       devpriv->CntrlReg |= Control_EXT;
+                       devpriv->ai_et = 1;
+               } else {
+                       devpriv->ai_et = 0;
+               }
+               i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
+                       &divisor2, &devpriv->ai_timer1,
+                       devpriv->ai_flags & TRIG_ROUND_MASK);
+               DPRINTK("adv_pci1710 EDBG: OSC base=%u div1=%u div2=%u timer=%u\n", devpriv->i8254_osc_base, divisor1, divisor2, devpriv->ai_timer1);
+               outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
+               if (mode != 2) {
+                       // start pacer
+                       start_pacer(dev, mode, divisor1, divisor2);
+               } else {
+                       devpriv->ai_et_div1 = divisor1;
+                       devpriv->ai_et_div2 = divisor2;
+               }
+               break;
+       case 3:
+               devpriv->CntrlReg |= Control_EXT | Control_IRQEN;
+               outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
+               break;
+       }
+
+       DPRINTK("adv_pci1710 EDBG: END: pci171x_ai_docmd_and_mode(...)\n");
+       return 0;
+}
+
+#ifdef PCI171X_EXTDEBUG
+/*
+==============================================================================
+*/
+static void pci171x_cmdtest_out(int e, comedi_cmd * cmd)
+{
+       rt_printk("adv_pci1710 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
+               cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
+       rt_printk("adv_pci1710 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
+               cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
+       rt_printk("adv_pci1710 e=%d stopsrc=%x scanend=%x\n", e, cmd->stop_src,
+               cmd->scan_end_src);
+       rt_printk("adv_pci1710 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n",
+               e, cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
+}
+#endif
+
+/*
+==============================================================================
+*/
+static int pci171x_ai_cmdtest(comedi_device * dev, comedi_subdevice * s,
+       comedi_cmd * cmd)
+{
+       int err = 0;
+       int tmp, divisor1, divisor2;
+
+       DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...)\n");
+#ifdef PCI171X_EXTDEBUG
+       pci171x_cmdtest_out(-1, cmd);
+#endif
+       /* step 1: make sure trigger sources are trivially valid */
+
+       tmp = cmd->start_src;
+       cmd->start_src &= TRIG_NOW | TRIG_EXT;
+       if (!cmd->start_src || tmp != cmd->start_src)
+               err++;
+
+       tmp = cmd->scan_begin_src;
+       cmd->scan_begin_src &= TRIG_FOLLOW;
+       if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+               err++;
+
+       tmp = cmd->convert_src;
+       cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
+       if (!cmd->convert_src || tmp != cmd->convert_src)
+               err++;
+
+       tmp = cmd->scan_end_src;
+       cmd->scan_end_src &= TRIG_COUNT;
+       if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+               err++;
+
+       tmp = cmd->stop_src;
+       cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+       if (!cmd->stop_src || tmp != cmd->stop_src)
+               err++;
+
+       if (err) {
+#ifdef PCI171X_EXTDEBUG
+               pci171x_cmdtest_out(1, cmd);
+#endif
+               DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=1\n", err);
+               return 1;
+       }
+
+       /* step 2: make sure trigger sources are unique and mutually compatible */
+
+       if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT) {
+               cmd->start_src = TRIG_NOW;
+               err++;
+       }
+
+       if (cmd->scan_begin_src != TRIG_FOLLOW) {
+               cmd->scan_begin_src = TRIG_FOLLOW;
+               err++;
+       }
+
+       if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
+               err++;
+
+       if (cmd->scan_end_src != TRIG_COUNT) {
+               cmd->scan_end_src = TRIG_COUNT;
+               err++;
+       }
+
+       if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
+               err++;
+
+       if (err) {
+#ifdef PCI171X_EXTDEBUG
+               pci171x_cmdtest_out(2, cmd);
+#endif
+               DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=2\n", err);
+               return 2;
+       }
+
+       /* step 3: make sure arguments are trivially compatible */
+
+       if (cmd->start_arg != 0) {
+               cmd->start_arg = 0;
+               err++;
+       }
+
+       if (cmd->scan_begin_arg != 0) {
+               cmd->scan_begin_arg = 0;
+               err++;
+       }
+
+       if (cmd->convert_src == TRIG_TIMER) {
+               if (cmd->convert_arg < this_board->ai_ns_min) {
+                       cmd->convert_arg = this_board->ai_ns_min;
+                       err++;
+               }
+       } else {                /* TRIG_FOLLOW */
+               if (cmd->convert_arg != 0) {
+                       cmd->convert_arg = 0;
+                       err++;
+               }
+       }
+
+       if (!cmd->chanlist_len) {
+               cmd->chanlist_len = 1;
+               err++;
+       }
+       if (cmd->chanlist_len > this_board->n_aichan) {
+               cmd->chanlist_len = this_board->n_aichan;
+               err++;
+       }
+       if (cmd->scan_end_arg != cmd->chanlist_len) {
+               cmd->scan_end_arg = cmd->chanlist_len;
+               err++;
+       }
+       if (cmd->stop_src == TRIG_COUNT) {
+               if (!cmd->stop_arg) {
+                       cmd->stop_arg = 1;
+                       err++;
+               }
+       } else {                /* TRIG_NONE */
+               if (cmd->stop_arg != 0) {
+                       cmd->stop_arg = 0;
+                       err++;
+               }
+       }
+
+       if (err) {
+#ifdef PCI171X_EXTDEBUG
+               pci171x_cmdtest_out(3, cmd);
+#endif
+               DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=3\n", err);
+               return 3;
+       }
+
+       /* step 4: fix up any arguments */
+
+       if (cmd->convert_src == TRIG_TIMER) {
+               tmp = cmd->convert_arg;
+               i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
+                       &divisor2, &cmd->convert_arg,
+                       cmd->flags & TRIG_ROUND_MASK);
+               if (cmd->convert_arg < this_board->ai_ns_min)
+                       cmd->convert_arg = this_board->ai_ns_min;
+               if (tmp != cmd->convert_arg)
+                       err++;
+       }
+
+       if (err) {
+               DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=4\n", err);
+               return 4;
+       }
+
+       /* step 5: complain about special chanlist considerations */
+
+       if (cmd->chanlist) {
+               if (!check_channel_list(dev, s, cmd->chanlist,
+                               cmd->chanlist_len))
+                       return 5;       // incorrect channels list
+       }
+
+       DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) ret=0\n");
+       return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_ai_cmd(comedi_device * dev, comedi_subdevice * s)
+{
+       comedi_cmd *cmd = &s->async->cmd;
+
+       DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmd(...)\n");
+       devpriv->ai_n_chan = cmd->chanlist_len;
+       devpriv->ai_chanlist = cmd->chanlist;
+       devpriv->ai_flags = cmd->flags;
+       devpriv->ai_data_len = s->async->prealloc_bufsz;
+       devpriv->ai_data = s->async->prealloc_buf;
+       devpriv->ai_timer1 = 0;
+       devpriv->ai_timer2 = 0;
+
+       if (cmd->stop_src == TRIG_COUNT) {
+               devpriv->ai_scans = cmd->stop_arg;
+       } else {
+               devpriv->ai_scans = 0;
+       }
+
+       if (cmd->scan_begin_src == TRIG_FOLLOW) {       // mode 1, 2, 3
+               if (cmd->convert_src == TRIG_TIMER) {   // mode 1 and 2
+                       devpriv->ai_timer1 = cmd->convert_arg;
+                       return pci171x_ai_docmd_and_mode(cmd->start_src ==
+                               TRIG_EXT ? 2 : 1, dev, s);
+               }
+               if (cmd->convert_src == TRIG_EXT) {     // mode 3
+                       return pci171x_ai_docmd_and_mode(3, dev, s);
+               }
+       }
+
+       return -1;
+}
+
+/*
+==============================================================================
+ Check if channel list from user is builded correctly
+ If it's ok, then program scan/gain logic.
+ This works for all cards.
+*/
+static int check_channel_list(comedi_device * dev, comedi_subdevice * s,
+       unsigned int *chanlist, unsigned int n_chan)
+{
+       unsigned int chansegment[32];
+       unsigned int i, nowmustbechan, seglen, segpos;
+
+       DPRINTK("adv_pci1710 EDBG:  check_channel_list(...,%d)\n", n_chan);
+       /* correct channel and range number check itself comedi/range.c */
+       if (n_chan < 1) {
+               comedi_error(dev, "range/channel list is empty!");
+               return 0;
+       }
+
+       if (n_chan > 1) {
+               chansegment[0] = chanlist[0];   // first channel is everytime ok
+               for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {    // build part of chanlist
+                       // rt_printk("%d. %d %d\n",i,CR_CHAN(chanlist[i]),CR_RANGE(chanlist[i]));
+                       if (chanlist[0] == chanlist[i])
+                               break;  // we detect loop, this must by finish
+                       if (CR_CHAN(chanlist[i]) & 1)   // odd channel cann't by differencial
+                               if (CR_AREF(chanlist[i]) == AREF_DIFF) {
+                                       comedi_error(dev,
+                                               "Odd channel can't be differential input!\n");
+                                       return 0;
+                               }
+                       nowmustbechan =
+                               (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
+                       if (CR_AREF(chansegment[i - 1]) == AREF_DIFF)
+                               nowmustbechan = (nowmustbechan + 1) % s->n_chan;
+                       if (nowmustbechan != CR_CHAN(chanlist[i])) {    // channel list isn't continous :-(
+                               rt_printk
+                                       ("channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
+                                       i, CR_CHAN(chanlist[i]), nowmustbechan,
+                                       CR_CHAN(chanlist[0]));
+                               return 0;
+                       }
+                       chansegment[i] = chanlist[i];   // well, this is next correct channel in list
+               }
+
+               for (i = 0, segpos = 0; i < n_chan; i++) {      // check whole chanlist
+                       //rt_printk("%d %d=%d %d\n",CR_CHAN(chansegment[i%seglen]),CR_RANGE(chansegment[i%seglen]),CR_CHAN(chanlist[i]),CR_RANGE(chanlist[i]));
+                       if (chanlist[i] != chansegment[i % seglen]) {
+                               rt_printk
+                                       ("bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
+                                       i, CR_CHAN(chansegment[i]),
+                                       CR_RANGE(chansegment[i]),
+                                       CR_AREF(chansegment[i]),
+                                       CR_CHAN(chanlist[i % seglen]),
+                                       CR_RANGE(chanlist[i % seglen]),
+                                       CR_AREF(chansegment[i % seglen]));
+                               return 0;       // chan/gain list is strange
+                       }
+               }
+       } else {
+               seglen = 1;
+       }
+       return seglen;
+}
+
+static void setup_channel_list(comedi_device * dev, comedi_subdevice * s,
+       unsigned int *chanlist, unsigned int n_chan, unsigned int seglen)
+{
+       unsigned int i, range, chanprog;
+
+       DPRINTK("adv_pci1710 EDBG:  setup_channel_list(...,%d,%d)\n", n_chan,
+               seglen);
+       devpriv->act_chanlist_len = seglen;
+       devpriv->act_chanlist_pos = 0;
+
+       DPRINTK("SegLen: %d\n", seglen);
+       for (i = 0; i < seglen; i++) {  // store range list to card
+               chanprog = muxonechan[CR_CHAN(chanlist[i])];
+               outw(chanprog, dev->iobase + PCI171x_MUX);      /* select channel */
+               range = this_board->rangecode_ai[CR_RANGE(chanlist[i])];
+               if (CR_AREF(chanlist[i]) == AREF_DIFF)
+                       range |= 0x0020;
+               outw(range, dev->iobase + PCI171x_RANGE);       /* select gain */
+#ifdef PCI171x_PARANOIDCHECK
+               devpriv->act_chanlist[i] =
+                       (CR_CHAN(chanlist[i]) << 12) & 0xf000;
+#endif
+               DPRINTK("GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range,
+                       devpriv->act_chanlist[i]);
+       }
+
+       devpriv->ai_et_MuxVal =
+               CR_CHAN(chanlist[0]) | (CR_CHAN(chanlist[seglen - 1]) << 8);
+       outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX); /* select channel interval to scan */
+       DPRINTK("MUX: %4x L%4x.H%4x\n",
+               CR_CHAN(chanlist[0]) | (CR_CHAN(chanlist[seglen - 1]) << 8),
+               CR_CHAN(chanlist[0]), CR_CHAN(chanlist[seglen - 1]));
+}
+
+/*
+==============================================================================
+*/
+static void start_pacer(comedi_device * dev, int mode, unsigned int divisor1,
+       unsigned int divisor2)
+{
+       DPRINTK("adv_pci1710 EDBG: BGN: start_pacer(%d,%u,%u)\n", mode,
+               divisor1, divisor2);
+       outw(0xb4, dev->iobase + PCI171x_CNTCTRL);
+       outw(0x74, dev->iobase + PCI171x_CNTCTRL);
+
+       if (mode == 1) {
+               outw(divisor2 & 0xff, dev->iobase + PCI171x_CNT2);
+               outw((divisor2 >> 8) & 0xff, dev->iobase + PCI171x_CNT2);
+               outw(divisor1 & 0xff, dev->iobase + PCI171x_CNT1);
+               outw((divisor1 >> 8) & 0xff, dev->iobase + PCI171x_CNT1);
+       }
+       DPRINTK("adv_pci1710 EDBG: END: start_pacer(...)\n");
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_ai_cancel(comedi_device * dev, comedi_subdevice * s)
+{
+       DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cancel(...)\n");
+
+       switch (this_board->cardtype) {
+       default:
+               devpriv->CntrlReg &= Control_CNT0;
+               devpriv->CntrlReg |= Control_SW;
+
+               outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL); // reset any operations
+               start_pacer(dev, -1, 0, 0);
+               outb(0, dev->iobase + PCI171x_CLRFIFO);
+               outb(0, dev->iobase + PCI171x_CLRINT);
+               break;
+       }
+
+       devpriv->ai_do = 0;
+       devpriv->ai_act_scan = 0;
+       s->async->cur_chan = 0;
+       devpriv->ai_buf_ptr = 0;
+       devpriv->neverending_ai = 0;
+
+       DPRINTK("adv_pci1710 EDBG: END: pci171x_ai_cancel(...)\n");
+       return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_reset(comedi_device * dev)
+{
+       DPRINTK("adv_pci1710 EDBG: BGN: pci171x_reset(...)\n");
+       outw(0x30, dev->iobase + PCI171x_CNTCTRL);
+       devpriv->CntrlReg = Control_SW | Control_CNT0;  // Software trigger, CNT0=external
+       outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL); // reset any operations
+       outb(0, dev->iobase + PCI171x_CLRFIFO); // clear FIFO
+       outb(0, dev->iobase + PCI171x_CLRINT);  // clear INT request
+       start_pacer(dev, -1, 0, 0);     // stop 8254
+       devpriv->da_ranges = 0;
+       if (this_board->n_aochan) {
+               outb(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);  // set DACs to 0..5V
+               outw(0, dev->iobase + PCI171x_DA1);     // set DA outputs to 0V
+               devpriv->ao_data[0] = 0x0000;
+               if (this_board->n_aochan > 1) {
+                       outw(0, dev->iobase + PCI171x_DA2);
+                       devpriv->ao_data[1] = 0x0000;
+               }
+       }
+       outw(0, dev->iobase + PCI171x_DO);      // digital outputs to 0
+       outb(0, dev->iobase + PCI171x_CLRFIFO); // clear FIFO
+       outb(0, dev->iobase + PCI171x_CLRINT);  // clear INT request
+
+       DPRINTK("adv_pci1710 EDBG: END: pci171x_reset(...)\n");
+       return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci1720_reset(comedi_device * dev)
+{
+       DPRINTK("adv_pci1710 EDBG: BGN: pci1720_reset(...)\n");
+       outb(Syncont_SC0, dev->iobase + PCI1720_SYNCONT);       // set synchronous output mode
+       devpriv->da_ranges = 0xAA;
+       outb(devpriv->da_ranges, dev->iobase + PCI1720_RANGE);  // set all ranges to +/-5V
+       outw(0x0800, dev->iobase + PCI1720_DA0);        // set outputs to 0V
+       outw(0x0800, dev->iobase + PCI1720_DA1);
+       outw(0x0800, dev->iobase + PCI1720_DA2);
+       outw(0x0800, dev->iobase + PCI1720_DA3);
+       outb(0, dev->iobase + PCI1720_SYNCOUT); // update outputs
+       devpriv->ao_data[0] = 0x0800;
+       devpriv->ao_data[1] = 0x0800;
+       devpriv->ao_data[2] = 0x0800;
+       devpriv->ao_data[3] = 0x0800;
+       DPRINTK("adv_pci1710 EDBG: END: pci1720_reset(...)\n");
+       return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci1710_reset(comedi_device * dev)
+{
+       DPRINTK("adv_pci1710 EDBG: BGN: pci1710_reset(...)\n");
+       switch (this_board->cardtype) {
+       case TYPE_PCI1720:
+               return pci1720_reset(dev);
+       default:
+               return pci171x_reset(dev);
+       }
+       DPRINTK("adv_pci1710 EDBG: END: pci1710_reset(...)\n");
+}
+
+/*
+==============================================================================
+*/
+static int pci1710_attach(comedi_device * dev, comedi_devconfig * it)
+{
+       comedi_subdevice *s;
+       int ret, subdev, n_subdevices;
+       unsigned int irq;
+       unsigned long iobase;
+       struct pci_dev *pcidev;
+       int opt_bus, opt_slot;
+       const char *errstr;
+       unsigned char pci_bus, pci_slot, pci_func;
+       int i;
+       int board_index;
+
+       rt_printk("comedi%d: adv_pci1710: ", dev->minor);
+
+       opt_bus = it->options[0];
+       opt_slot = it->options[1];
+
+       if ((ret = alloc_private(dev, sizeof(pci1710_private))) < 0) {
+               rt_printk(" - Allocation failed!\n");
+               return -ENOMEM;
+       }
+
+       /* Look for matching PCI device */
+       errstr = "not found!";
+       pcidev = NULL;
+       board_index = this_board - boardtypes;
+       while (NULL != (pcidev = pci_get_device(PCI_VENDOR_ID_ADVANTECH,
+               PCI_ANY_ID, pcidev))) {
+               if(strcmp(this_board->name, DRV_NAME) == 0)
+               {
+                       for(i = 0; i < n_boardtypes; ++i)
+                       {
+                               if(pcidev->device == boardtypes[i].device_id)
+                               {
+                                       board_index = i;
+                                       break;
+                               }
+                       }
+                       if(i == n_boardtypes) continue;
+               }else
+               {
+                       if(pcidev->device != boardtypes[board_index].device_id) continue;
+               }
+
+               /* Found matching vendor/device. */
+               if (opt_bus || opt_slot) {
+                       /* Check bus/slot. */
+                       if (opt_bus != pcidev->bus->number
+                               || opt_slot != PCI_SLOT(pcidev->devfn))
+                               continue;       /* no match */
+               }
+               /*
+               * Look for device that isn't in use.
+               * Enable PCI device and request regions.
+               */
+               if (comedi_pci_enable(pcidev, DRV_NAME)) {
+                       errstr = "failed to enable PCI device and request regions!";
+                       continue;
+               }
+               // fixup board_ptr in case we were using the dummy entry with the driver name
+               dev->board_ptr = &boardtypes[board_index];
+               break;
+       }
+
+       if (!pcidev) {
+               if (opt_bus || opt_slot) {
+                       rt_printk(" - Card at b:s %d:%d %s\n",
+                               opt_bus, opt_slot, errstr);
+               } else {
+                       rt_printk(" - Card %s\n", errstr);
+               }
+               return -EIO;
+       }
+
+       pci_bus = pcidev->bus->number;
+       pci_slot = PCI_SLOT(pcidev->devfn);
+       pci_func = PCI_FUNC(pcidev->devfn);
+       irq = pcidev->irq;
+       iobase = pci_resource_start(pcidev, 2);
+
+       rt_printk(", b:s:f=%d:%d:%d, io=0x%4lx", pci_bus, pci_slot, pci_func,
+               iobase);
+
+       dev->iobase = iobase;
+
+       dev->board_name = this_board->name;
+       devpriv->pcidev = pcidev;
+
+       n_subdevices = 0;
+       if (this_board->n_aichan)
+               n_subdevices++;
+       if (this_board->n_aochan)
+               n_subdevices++;
+       if (this_board->n_dichan)
+               n_subdevices++;
+       if (this_board->n_dochan)
+               n_subdevices++;
+       if (this_board->n_counter)
+               n_subdevices++;
+
+       if ((ret = alloc_subdevices(dev, n_subdevices)) < 0) {
+               rt_printk(" - Allocation failed!\n");
+               return ret;
+       }
+
+       pci1710_reset(dev);
+
+       if (this_board->have_irq) {
+               if (irq) {
+                       if (comedi_request_irq(irq, interrupt_service_pci1710,
+                                       IRQF_SHARED, "Advantech PCI-1710",
+                                       dev)) {
+                               rt_printk
+                                       (", unable to allocate IRQ %d, DISABLING IT",
+                                       irq);
+                               irq = 0;        /* Can't use IRQ */
+                       } else {
+                               rt_printk(", irq=%u", irq);
+                       }
+               } else {
+                       rt_printk(", IRQ disabled");
+               }
+       } else {
+               irq = 0;
+       }
+
+       dev->irq = irq;
+
+       printk(".\n");
+
+       subdev = 0;
+
+       if (this_board->n_aichan) {
+               s = dev->subdevices + subdev;
+               dev->read_subdev = s;
+               s->type = COMEDI_SUBD_AI;
+               s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
+               if (this_board->n_aichand)
+                       s->subdev_flags |= SDF_DIFF;
+               s->n_chan = this_board->n_aichan;
+               s->maxdata = this_board->ai_maxdata;
+               s->len_chanlist = this_board->n_aichan;
+               s->range_table = this_board->rangelist_ai;
+               s->cancel = pci171x_ai_cancel;
+               s->insn_read = pci171x_insn_read_ai;
+               if (irq) {
+                       s->subdev_flags |= SDF_CMD_READ;
+                       s->do_cmdtest = pci171x_ai_cmdtest;
+                       s->do_cmd = pci171x_ai_cmd;
+               }
+               devpriv->i8254_osc_base = 100;  // 100ns=10MHz
+               subdev++;
+       }
+
+       if (this_board->n_aochan) {
+               s = dev->subdevices + subdev;
+               s->type = COMEDI_SUBD_AO;
+               s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+               s->n_chan = this_board->n_aochan;
+               s->maxdata = this_board->ao_maxdata;
+               s->len_chanlist = this_board->n_aochan;
+               s->range_table = this_board->rangelist_ao;
+               switch (this_board->cardtype) {
+               case TYPE_PCI1720:
+                       s->insn_write = pci1720_insn_write_ao;
+                       break;
+               default:
+                       s->insn_write = pci171x_insn_write_ao;
+                       break;
+               }
+               s->insn_read = pci171x_insn_read_ao;
+               subdev++;
+       }
+
+       if (this_board->n_dichan) {
+               s = dev->subdevices + subdev;
+               s->type = COMEDI_SUBD_DI;
+               s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
+               s->n_chan = this_board->n_dichan;
+               s->maxdata = 1;
+               s->len_chanlist = this_board->n_dichan;
+               s->range_table = &range_digital;
+               s->io_bits = 0; /* all bits input */
+               s->insn_bits = pci171x_insn_bits_di;
+               subdev++;
+       }
+
+       if (this_board->n_dochan) {
+               s = dev->subdevices + subdev;
+               s->type = COMEDI_SUBD_DO;
+               s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+               s->n_chan = this_board->n_dochan;
+               s->maxdata = 1;
+               s->len_chanlist = this_board->n_dochan;
+               s->range_table = &range_digital;
+               s->io_bits = (1 << this_board->n_dochan) - 1;   /* all bits output */
+               s->state = 0;
+               s->insn_bits = pci171x_insn_bits_do;
+               subdev++;
+       }
+
+       if (this_board->n_counter) {
+               s = dev->subdevices + subdev;
+               s->type = COMEDI_SUBD_COUNTER;
+               s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+               s->n_chan = this_board->n_counter;
+               s->len_chanlist = this_board->n_counter;
+               s->maxdata = 0xffff;
+               s->range_table = &range_unknown;
+               s->insn_read = pci171x_insn_counter_read;
+               s->insn_write = pci171x_insn_counter_write;
+               s->insn_config = pci171x_insn_counter_config;
+               subdev++;
+       }
+
+       devpriv->valid = 1;
+
+       return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci1710_detach(comedi_device * dev)
+{
+
+       if (dev->private) {
+               if (devpriv->valid)
+                       pci1710_reset(dev);
+               if (dev->irq)
+                       comedi_free_irq(dev->irq, dev);
+               if (devpriv->pcidev) {
+                       if (dev->iobase) {
+                               comedi_pci_disable(devpriv->pcidev);
+                       }
+                       pci_dev_put(devpriv->pcidev);
+               }
+       }
+
+       return 0;
+}
+
+/*
+==============================================================================
+*/
+COMEDI_PCI_INITCLEANUP(driver_pci1710, pci1710_pci_table);
+/*
+==============================================================================
+*/