New driver from Michal Dobes
authorDavid Schleef <ds@schleef.org>
Tue, 3 Apr 2001 09:32:32 +0000 (09:32 +0000)
committerDavid Schleef <ds@schleef.org>
Tue, 3 Apr 2001 09:32:32 +0000 (09:32 +0000)
comedi/drivers/adv_pci1710.c [new file with mode: 0644]

diff --git a/comedi/drivers/adv_pci1710.c b/comedi/drivers/adv_pci1710.c
new file mode 100644 (file)
index 0000000..57bbdd5
--- /dev/null
@@ -0,0 +1,1643 @@
+/*
+ * comedi/drivers/adv_pci1710.c
+ *
+ * Author: Michal Dobes <majkl@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 
+ * 
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/comedidev.h>
+#include <8253.h>
+#include <amcc_s5933.h>
+
+
+#define ADVANTECH_VENDOR       0x13fe  /* Advantech PCI vendor ID */
+
+#define PCI171x_PARANOIDCHECK          /* if defined, then is used code which control correct channel number on every 12 bit sample */
+
+#undef PCI171X_EXTDEBUG
+
+// 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 */
+
+#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 */
+
+
+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 char range_codes_pci1710_3[]={0x00, 0x01, 0x02, 0x03, 0x04, 0x10, 0x11, 0x12, 0x13 };
+
+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 char range_codes_pci1710hg[]={0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13 };
+
+comedi_lrange range_pci17x1={ 5, {
+       BIP_RANGE(10),
+       BIP_RANGE(5),
+       BIP_RANGE(2.5),
+       BIP_RANGE(1.25),
+       BIP_RANGE(0.625)
+       }
+};
+
+static char range_codes_pci17x1[]={0x00, 0x01, 0x02, 0x03, 0x04 };
+
+comedi_lrange range_pci1720={ 4, {
+       UNI_RANGE(5),
+       UNI_RANGE(10),
+       BIP_RANGE(5),
+       BIP_RANGE(10)
+       }
+};
+
+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);
+
+static unsigned short  pci_list_builded=0;     /*=1 list of card is know */
+
+typedef struct {
+       char            *name;          // driver name
+       int             vendor_id;      // PCI vendor a device ID of card
+       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             ai_maxdata;     // resolution of A/D
+       int             ao_maxdata;     // resolution of D/A
+       comedi_lrange   *rangelist_ai;  // rangelist for A/D
+       char            *rangecode_ai;  // range codes for programming
+       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 boardtype boardtypes[] =
+{
+       {"pci1710", ADVANTECH_VENDOR, 0x1710,
+        IORANGE_171x, 1, TYPE_PCI171X,
+        16, 8, 2, 16, 16,  0x0fff, 0x0fff,
+        &range_pci1710_3, range_codes_pci1710_3, &range_pci171x_da,
+        10000, 2048 },
+       {"pci1710hg", ADVANTECH_VENDOR, 0x1710,
+        IORANGE_171x, 1, TYPE_PCI171X,
+        16, 8, 2, 16, 16,  0x0fff, 0x0fff,
+        &range_pci1710hg, range_codes_pci1710hg, &range_pci171x_da,
+        10000, 2048 },
+       {"pci1711", ADVANTECH_VENDOR, 0x1711,
+        IORANGE_171x, 1, TYPE_PCI171X,
+        16, 0, 2, 16, 16,  0x0fff, 0x0fff,
+        &range_pci17x1, range_codes_pci17x1, &range_pci171x_da,
+        10000, 512 },
+       {"pci1713", ADVANTECH_VENDOR, 0x1713,
+        IORANGE_171x, 1, TYPE_PCI1713,
+        32,16, 0,  0,  0,  0x0fff, 0x0000,
+        &range_pci17x1, range_codes_pci1710_3, NULL,
+        10000, 2048 },
+       {"pci1720", ADVANTECH_VENDOR, 0x1720,
+        IORANGE_1720, 0, TYPE_PCI1720,
+        0, 0, 4, 0, 0,  0x0000, 0x0fff,
+        NULL, NULL, &range_pci1720,
+        0, 0 },
+       {"pci1731", ADVANTECH_VENDOR, 0x1731,
+        IORANGE_171x, 1, TYPE_PCI171X,
+        16, 0, 0, 16, 16,  0x0fff, 0x0000,
+        &range_pci17x1, range_codes_pci17x1, NULL,
+        10000, 512 },
+};
+
+#define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
+
+comedi_driver driver_pci1710={
+       driver_name:    "adv_pci1710",
+       module:         THIS_MODULE,
+       attach:         pci1710_attach,
+       detach:         pci1710_detach,
+       num_names:      n_boardtypes,
+       board_name:     boardtypes,
+       offset:         sizeof(boardtype),
+};
+
+typedef struct{
+       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 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
+} pci1710_private;
+
+#define devpriv ((pci1710_private *)dev->private)
+#define this_board ((boardtype *)dev->board_ptr)
+
+/* 
+==============================================================================
+*/
+
+int check_and_setup_channel_list(comedi_device * dev, comedi_subdevice * s, unsigned int *chanlist, unsigned int n_chan, char check);
+void start_pacer(comedi_device * dev, int mode, unsigned int divisor1, unsigned int divisor2);
+static int pci1710_reset(comedi_device *dev);
+int pci171x_ai_cancel(comedi_device * dev, comedi_subdevice * s);
+
+static 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};
+
+#ifdef CONFIG_COMEDI_MODE0
+
+/* 
+==============================================================================
+*/
+static int pci171x_ai_mode0(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) 
+{
+        int timeout,i;
+#ifdef PCI171x_PARANOIDCHECK
+       unsigned int data;
+#endif
+
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: BGN: pci171x_ai_mode0(...)\n");
+#endif
+       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);
+       
+       if (!check_and_setup_channel_list(dev,s,it->chanlist,it->n_chan, 0))  return -EINVAL;
+       
+       if (it->n==0) it->n=1;
+       
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 A ST=%4x IO=%x\n",inw(dev->iobase+PCI171x_STATUS), dev->iobase+PCI171x_STATUS);
+#endif
+       for (i=0; i<(it->n_chan*it->n); i++) {
+               outw(0, dev->iobase+PCI171x_SOFTTRG); /* start conversion */
+#ifdef PCI171X_EXTDEBUG
+               rt_printk("adv_pci1710 B i=%d ST=%4x\n",i,inw(dev->iobase+PCI171x_STATUS));
+#endif
+               udelay(1);
+#ifdef PCI171X_EXTDEBUG
+               rt_printk("adv_pci1710 C i=%d ST=%4x\n",i,inw(dev->iobase+PCI171x_STATUS));
+#endif
+               timeout=100;
+               while (timeout--) {
+                       if (!(inw(dev->iobase+PCI171x_STATUS) & Status_FE)) goto conv_finish;
+#ifdef PCI171X_EXTDEBUG
+                       if (!(timeout%10))
+                               rt_printk("adv_pci1710 D i=%d tm=%d ST=%4x\n",i,timeout,inw(dev->iobase+PCI171x_STATUS));
+#endif
+                       udelay(1);
+               }
+               comedi_error(dev,"A/D mode0 timeout");
+               it->data[i]=0;
+               outb(0,dev->iobase + PCI171x_CLRFIFO);
+               outb(0,dev->iobase + PCI171x_CLRINT);
+#ifdef PCI171X_EXTDEBUG
+               rt_printk("adv_pci1710 i=%d ST=%4x DT=%4x to=%d\n",i,inw(dev->iobase+PCI171x_STATUS),inw(dev->iobase+PCI171x_AD_DATA),timeout);
+               rt_printk("adv_pci1710 EDBG: END pci171x_ai_mode0(...)\n");
+#endif
+               return -ETIME;
+conv_finish:
+#ifdef PCI171x_PARANOIDCHECK
+               data=inw(dev->iobase+PCI171x_AD_DATA);
+               if (this_board->cardtype!=TYPE_PCI1713)
+                       if ((data & 0xf000)!=devpriv->act_chanlist[i % devpriv->act_chanlist_len]) {
+                               comedi_error(dev,"A/D mode0 data droput!");
+                               return -ETIME;
+                       }
+               it->data[i] = data & 0x0fff; 
+#else
+               it->data[i] = inw(dev->iobase+PCI171x_AD_DATA) & 0x0fff; 
+#endif
+       }
+
+       outb(0,dev->iobase + PCI171x_CLRFIFO);
+       outb(0,dev->iobase + PCI171x_CLRINT);
+
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: END pci171x_ai_mode0(...) i=%d\n",i);
+#endif
+        return i;
+}
+
+/* 
+==============================================================================
+*/
+static int pci171x_ao_mode0(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) 
+{
+        int chan,i,range;
+        sampl_t data;
+
+       for (i=0;i<it->n_chan;i++){
+               data=it->data[i];
+               chan=CR_CHAN(it->chanlist[i]);
+               range=CR_RANGE(it->chanlist[i]);
+               if (chan) {
+                       devpriv->da_ranges&=0xfb;
+                       devpriv->da_ranges|=(range<<2);
+                       outw(devpriv->da_ranges, dev->iobase+PCI171x_DAREF);
+                       outw(data, dev->iobase + PCI171x_DA2);
+                       devpriv->ao_data[1]=data;
+               } else {
+                       devpriv->da_ranges&=0xfe;
+                       devpriv->da_ranges|=range;
+                       outw(devpriv->da_ranges, dev->iobase+PCI171x_DAREF);
+                       outw(data, dev->iobase + PCI171x_DA1);
+                       devpriv->ao_data[0]=data;
+               } 
+        }    
+
+        return it->n_chan;
+}
+
+/* 
+==============================================================================
+*/
+static int pci171x_di_mode0(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) 
+{ 
+        unsigned int data;
+        int chan;
+        int i;
+
+       data = inw(dev->iobase + PCI171x_DI);
+
+        for(i=0;i<it->n_chan;i++) {
+               chan=CR_CHAN(it->chanlist[i]);
+               it->data[i]=(data>>chan)&1;
+        }
+
+       return it->n_chan;
+}
+
+/* 
+==============================================================================
+*/
+static int pci171x_do_mode0(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) 
+{
+        unsigned int mask, data;
+        int chan;
+        int i;
+
+       data=s->state;
+        for(i=0;i<it->n_chan;i++) {
+//     rt_printk("chl=%x dt=%x\n",it->chanlist[i],it->data[i]);
+               chan=CR_CHAN(it->chanlist[i]);
+               mask=(1<<chan);
+               data &= ~mask;
+               if(it->data[i])
+                       data |= mask;
+        }
+       outw(data, dev->iobase + PCI171x_DO);
+        s->state = data;
+//     rt_printk("do=%x\n",data);
+
+       return it->n_chan;
+}
+
+#endif
+
+/* 
+==============================================================================
+*/
+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
+
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: BGN: pci171x_insn_read_ai(...)\n");
+#endif
+       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);
+       
+       if (!check_and_setup_channel_list(dev,s,&insn->chanspec, 1, 0))  return -EINVAL;
+
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 A ST=%4x IO=%x\n",inw(dev->iobase+PCI171x_STATUS), dev->iobase+PCI171x_STATUS);
+#endif
+       for (n=0; n<insn->n; n++) {
+               outw(0, dev->iobase+PCI171x_SOFTTRG); /* start conversion */
+#ifdef PCI171X_EXTDEBUG
+               rt_printk("adv_pci1710 B n=%d ST=%4x\n",n,inw(dev->iobase+PCI171x_STATUS));
+#endif
+               udelay(1);
+#ifdef PCI171X_EXTDEBUG
+               rt_printk("adv_pci1710 C n=%d ST=%4x\n",n,inw(dev->iobase+PCI171x_STATUS));
+#endif
+               timeout=100;
+               while (timeout--) {
+                       if (!(inw(dev->iobase+PCI171x_STATUS) & Status_FE)) goto conv_finish;
+#ifdef PCI171X_EXTDEBUG
+                       if (!(timeout%10))
+                               rt_printk("adv_pci1710 D n=%d tm=%d ST=%4x\n",n,timeout,inw(dev->iobase+PCI171x_STATUS));
+#endif
+                       udelay(1);
+               }
+               comedi_error(dev,"A/D insn timeout");
+               outb(0,dev->iobase + PCI171x_CLRFIFO);
+               outb(0,dev->iobase + PCI171x_CLRINT);
+               data[n]=0;
+#ifdef PCI171X_EXTDEBUG
+               rt_printk("adv_pci1710 EDBG: END: pci171x_insn_read_ai(...) n=%d\n",n);
+#endif
+               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);
+
+#ifdef PCI171X_EXTDEBUG
+               rt_printk("adv_pci1710 EDBG: END: pci171x_insn_read_ai(...) n=%d\n",n);
+#endif
+       return n;
+}
+
+/* 
+==============================================================================
+*/
+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;
+
+}
+
+/* 
+==============================================================================
+*/
+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;
+}
+
+/*
+==============================================================================
+*/
+int pci171x_insn_read_di(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data)
+{
+       int n;
+       
+       for (n=0; n<insn->n; n++) {
+               data[n] = inw(dev->iobase + PCI171x_DI);
+       }
+
+       return n;
+}
+
+/*
+==============================================================================
+*/
+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;
+}
+
+/*
+==============================================================================
+*/
+int pci171x_insn_write_do(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data)
+{
+       int n;
+       
+       for (n=0; n<insn->n; n++) {
+               s->state=data[n]& 0xff;
+               outw(s->state, dev->iobase + PCI171x_DO);
+       }
+
+       return n;
+}
+
+/*
+==============================================================================
+*/
+int pci171x_insn_read_do(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data)
+{
+       int n;
+       
+       for (n=0; n<insn->n; n++) 
+               data[n]=s->state & 0xff;
+
+       return n;
+}
+
+/*
+==============================================================================
+*/
+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] = inw(dev->iobase + PCI171x_DI);
+
+       return 2;
+}
+
+#ifdef CONFIG_COMEDI_MODE0
+/* 
+==============================================================================
+*/
+static int pci1720_ao_mode0(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) 
+{
+        int i,rangereg,chan;
+
+       for (i=0;i<it->n_chan;i++) {
+               chan=CR_CHAN(it->chanlist[i]);
+               rangereg=devpriv->da_ranges & (~(0x03<<(chan<<1)));
+               rangereg|=(CR_RANGE(it->chanlist[i])<<(chan<<1));
+               if (rangereg!=devpriv->da_ranges) {
+                       outb(rangereg, dev->iobase+PCI1720_RANGE);
+                       devpriv->da_ranges=rangereg;
+               }
+               outw(it->data[i], dev->iobase + PCI1720_DA0+(chan<<1));
+               devpriv->ao_data[chan]=it->data[i];
+        }    
+
+        outb(0, dev->iobase + PCI1720_SYNCOUT); // update outputs
+
+        return it->n_chan;
+}
+#endif
+
+/* 
+==============================================================================
+*/
+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
+
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: BGN: interrupt_pci1710_every_sample(...)\n");
+#endif
+       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);
+               comedi_error_done(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);
+               comedi_error_done(dev,s);
+               return;
+       }
+
+       outb(0, dev->iobase + PCI171x_CLRINT);                  // clear our INT request
+
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("FOR ");
+#endif
+       for (;!(inw(dev->iobase+PCI171x_STATUS)&Status_FE);) {
+#ifdef PCI171x_PARANOIDCHECK
+               sampl=inw(dev->iobase+PCI171x_AD_DATA);
+#ifdef PCI171X_EXTDEBUG
+               rt_printk("%04x:",sampl);
+#endif
+               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);
+                               comedi_error_done(dev,s);
+                               return;
+                       }
+               s->cur_trig.data[s->async->buf_int_ptr>>1]=sampl & 0x0fff;
+#ifdef PCI171X_EXTDEBUG
+               rt_printk("%8d %2d %8d~",s->async->buf_int_ptr,s->async->cur_chan,s->async->buf_int_count);
+#endif
+#else
+               s->cur_trig.data[s->async->buf_int_ptr>>1]=inw(dev->iobase+PCI171x_AD_DATA) & 0x0fff;
+#endif
+               s->async->buf_int_count+=sizeof(sampl_t);
+               s->async->buf_int_ptr+=sizeof(sampl_t);
+               s->async->cur_chan++;
+
+               if (s->async->buf_int_ptr>=devpriv->ai_data_len) {      // buffer rollover
+                       s->async->buf_int_ptr = 0;
+#ifdef PCI171X_EXTDEBUG
+                       rt_printk("adv_pci1710 EDBG: EOBUF1 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);
+#endif
+                       comedi_eobuf(dev, s);
+#ifdef PCI171X_EXTDEBUG
+                       rt_printk("adv_pci1710 EDBG: EOBUF2\n");
+#endif
+               }
+
+               if(s->async->cur_chan>=devpriv->ai_n_chan){     // one scan done
+                       s->async->cur_chan=0;
+                       devpriv->ai_act_scan++;
+#ifdef PCI171X_EXTDEBUG
+                       rt_printk("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);
+#endif
+                       comedi_eos(dev, s);
+#ifdef PCI171X_EXTDEBUG
+                       rt_printk("adv_pci1710 EDBG: EOS2\n");
+#endif
+                       if ((!devpriv->neverending_ai)&&(devpriv->ai_act_scan>=devpriv->ai_scans)) { // all data sampled 
+                               pci171x_ai_cancel(dev,s);
+                               comedi_done(dev,s); 
+                               return;
+                       }
+               }
+       }
+
+       outb(0, dev->iobase + PCI171x_CLRINT);                  // clear our INT request
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: END: interrupt_pci1710_every_sample(...)\n");
+#endif
+}
+
+/* 
+==============================================================================
+*/
+static int move_block_from_fifo(comedi_device *dev,comedi_subdevice *s,sampl_t *data,int n,int turn)
+{
+       int i,j;
+#ifdef PCI171x_PARANOIDCHECK
+       int sampl;
+#endif
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: BGN: move_block_from_fifo(...,%d,%d)\n",n,turn);
+#endif
+       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);
+                               s->async->buf_int_count+=i*sizeof(sampl_t);
+                               s->async->buf_int_ptr+=i*sizeof(sampl_t);
+                               s->async->cur_chan=j;
+                               pci171x_ai_cancel(dev,s);
+                               comedi_error_done(dev,s);
+                               return 1;
+                       }
+               *data=sampl & 0x0fff;
+#else
+               *data=inw(dev->iobase+PCI171x_AD_DATA) & 0x0fff;
+#endif
+               data++;
+               j++;
+               if(j>=devpriv->ai_n_chan){
+                       j=0;
+                       devpriv->ai_act_scan++;
+               }
+       }
+       s->async->cur_chan=j;
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: END: move_block_from_fifo(...)\n");
+#endif
+       return 0;
+}
+
+/* 
+==============================================================================
+*/
+static void interrupt_pci1710_half_fifo(void *d) 
+{
+       comedi_device   *dev = d;
+       comedi_subdevice *s = dev->subdevices + 0;
+       int             m,samplesinbuf;
+       
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: BGN: interrupt_pci1710_half_fifo(...)\n");
+#endif
+       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);
+               comedi_error_done(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);
+               comedi_error_done(dev,s);
+               return;
+       }
+
+       samplesinbuf=this_board->fifo_half_size;
+       if(s->async->buf_int_ptr+samplesinbuf*sizeof(sampl_t)>=devpriv->ai_data_len){
+               m=(devpriv->ai_data_len-s->async->buf_int_ptr)/sizeof(sampl_t);
+               if (move_block_from_fifo(dev,s,((void *)(s->cur_trig.data))+s->async->buf_int_ptr,m,0))
+                       return;
+               s->async->buf_int_count+=m*sizeof(sampl_t);
+               samplesinbuf-=m;
+               s->async->buf_int_ptr=0;
+#ifdef PCI171X_EXTDEBUG
+               rt_printk("adv_pci1710 EDBG: EOBUF1 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);
+#endif
+               comedi_eobuf(dev,s);
+#ifdef PCI171X_EXTDEBUG
+               rt_printk("adv_pci1710 EDBG: EOBUF2\n");
+#endif
+       }
+
+       if (samplesinbuf) {
+               if (move_block_from_fifo(dev,s,((void *)(s->cur_trig.data))+s->async->buf_int_ptr,samplesinbuf,1))
+                       return;
+
+               s->async->buf_int_count+=samplesinbuf*sizeof(sampl_t);
+               s->async->buf_int_ptr+=samplesinbuf*sizeof(sampl_t);
+
+#ifdef PCI171X_EXTDEBUG
+               rt_printk("adv_pci1710 EDBG: BUFCHECK1 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);
+#endif
+               comedi_bufcheck(dev,s);
+#ifdef PCI171X_EXTDEBUG
+               rt_printk("adv_pci1710 EDBG: BUFCHECK2\n");
+#endif
+       }
+
+       if (!devpriv->neverending_ai)
+               if ( devpriv->ai_act_scan>=devpriv->ai_scans ) { /* all data sampled */
+                       pci171x_ai_cancel(dev,s);
+                       comedi_done(dev,s); 
+                       return;
+               }
+       outb(0, dev->iobase + PCI171x_CLRINT);                  // clear our INT request
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: END: interrupt_pci1710_half_fifo(...)\n");
+#endif
+}
+
+/* 
+==============================================================================
+*/
+static void interrupt_service_pci1710(int irq, void *d, struct pt_regs *regs) 
+{
+        comedi_device *dev = d;
+       
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: BGN: interrupt_service_pci1710(%d,...)\n",irq);
+#endif
+       if (!(inw(dev->iobase + PCI171x_STATUS) & Status_IRQ))  // is this interrupt from our board?
+           return;                                             // no, exit
+
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: interrupt_service_pci1710() ST: %4x\n",inw(dev->iobase + PCI171x_STATUS));
+#endif
+
+       if (devpriv->ai_eos) {  // We use FIFO half full INT or not?
+               interrupt_pci1710_every_sample(d);
+       } else {
+               interrupt_pci1710_half_fifo(d);
+       }
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: END: interrupt_service_pci1710(...)\n");
+#endif
+}
+
+/* 
+==============================================================================
+*/
+static int pci171x_ai_docmd_and_mode(int mode, comedi_device * dev, comedi_subdevice * s) 
+{
+        unsigned int divisor1, divisor2;
+
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: BGN: pci171x_ai_docmd_and_mode(%d,...)\n",mode);
+#endif
+       start_pacer(dev, -1, 0, 0); // stop pacer
+
+        if (!check_and_setup_channel_list(dev, s, devpriv->ai_chanlist, devpriv->ai_n_chan, 0)) return -EINVAL;
+       udelay(1);
+
+       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:
+               if (devpriv->ai_timer1<this_board->ai_ns_min) devpriv->ai_timer1=this_board->ai_ns_min;
+               devpriv->CntrlReg|=Control_PACER|Control_IRQEN;
+               i8253_cascade_ns_to_timer(devpriv->i8254_osc_base,&divisor1,&divisor2,&devpriv->ai_timer1,devpriv->ai_flags&TRIG_ROUND_MASK);
+#ifdef PCI171X_EXTDEBUG
+                rt_printk("adv_pci1710 EDBG: OSC base=%u div1=%u div2=%u timer=%u\n",devpriv->i8254_osc_base,divisor1,divisor2,devpriv->ai_timer1);
+#endif
+               outw(devpriv->CntrlReg, dev->iobase+PCI171x_CONTROL);
+               start_pacer(dev, mode, divisor1, divisor2);     // start pacer
+               break;
+       case 3:
+               devpriv->CntrlReg|=Control_EXT|Control_IRQEN;
+               outw(devpriv->CntrlReg, dev->iobase+PCI171x_CONTROL);
+               break;
+       }
+       
+    
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: END: pci171x_ai_docmd_and_mode(...)\n");
+#endif
+       return 0;
+}
+
+#ifdef CONFIG_COMEDI_MODES
+
+/* 
+==============================================================================
+*/
+static int pci171x_ai_mode13(int mode, comedi_device * dev, comedi_subdevice * s, comedi_trig * it) 
+{
+       int ret;
+       
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: BGN:  pci171x_ai_mode13(%d,...)\n",mode);
+#endif
+       devpriv->ai_n_chan=it->n_chan;
+       devpriv->ai_chanlist=it->chanlist;
+       devpriv->ai_scans=it->n;
+       devpriv->ai_flags=it->flags;
+       devpriv->ai_data_len=it->data_len;
+       devpriv->ai_data=it->data;
+       devpriv->ai_timer1=it->trigvar;
+       devpriv->ai_timer2=it->trigvar1;
+       
+       ret=pci171x_ai_docmd_and_mode(mode, dev, s);
+
+       it->trigvar=devpriv->ai_timer1;
+       it->trigvar1=devpriv->ai_timer2;
+
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: END:  pci171x_ai_mode13(...)\n");
+#endif
+       return ret;     
+}
+
+/* 
+==============================================================================
+*/
+static int pci171x_ai_mode1(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) 
+{
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: pci171x_ai_mode1(...)\n");
+#endif
+        return pci171x_ai_mode13(1, dev, s, it);
+}
+
+/* 
+==============================================================================
+*/
+static int pci171x_ai_mode3(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) 
+{
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: pci171x_ai_mode3(...)\n");
+#endif
+        return pci171x_ai_mode13(3, dev, s, it);
+}
+
+#endif
+
+#ifdef PCI171X_EXTDEBUG
+/* 
+==============================================================================
+*/
+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;
+
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...)\n");
+       pci171x_cmdtest_out(-1, cmd);
+#endif
+       /* step 1: make sure trigger sources are trivially valid */
+
+       tmp=cmd->start_src;
+       cmd->start_src &= TRIG_NOW;
+       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);
+               rt_printk("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=1\n",err);
+#endif
+               return 1;
+       }
+
+       /* step 2: make sure trigger sources are unique and mutually compatible */
+
+       if(cmd->start_src!=TRIG_NOW) {
+               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);
+               rt_printk("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=2\n",err);
+#endif
+               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);
+               rt_printk("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=3\n",err);
+#endif
+               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 (cmd->chanlist)
+               if (!check_and_setup_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len, 1)) return 5; // incorrect channels list
+
+       if(err) {
+#ifdef PCI171X_EXTDEBUG
+               rt_printk("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=4\n",err);
+#endif
+               return 4;
+       }
+
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) ret=0\n");
+#endif
+       return 0;
+}
+
+/* 
+==============================================================================
+*/
+static int pci171x_ai_cmd(comedi_device *dev,comedi_subdevice *s)
+{
+       comedi_cmd *cmd=&s->async->cmd;
+
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: BGN: pci171x_ai_cmd(...)\n");
+#endif
+       devpriv->ai_n_chan=cmd->chanlist_len;
+       devpriv->ai_chanlist=cmd->chanlist;
+       devpriv->ai_flags=cmd->flags;
+       devpriv->ai_data_len=cmd->data_len;
+       devpriv->ai_data=cmd->data;
+       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 or 3
+               if (cmd->convert_src==TRIG_TIMER) { // mode 1
+                       devpriv->ai_timer1=cmd->convert_arg;
+                       return pci171x_ai_docmd_and_mode(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.
+*/
+int check_and_setup_channel_list(comedi_device * dev, comedi_subdevice * s, unsigned int *chanlist, unsigned int n_chan, char check) 
+{
+        unsigned int chansegment[32];  
+        unsigned int i, nowmustbechan, seglen, segpos,range,chanprog;
+    
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG:  check_and_setup_channel_list(...,%d)\n",n_chan);
+#endif
+    /* correct channel and range number check itself comedi/range.c */
+       if (n_chan<1) {
+               if (!check) 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) {
+                                       if (!check) rt_printk("comedi%d: adv_pci171x: Odd channel cann't be differencial input!\n", dev->minor);
+                                       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 :-(
+                               if (!check) rt_printk("comedi%d: adv_pci171x: channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
+                                           dev->minor,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]) {
+                               if (!check) rt_printk("comedi%d: adv_pci171x: bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
+                                           dev->minor,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;
+       }
+       
+       if (check) return 1;
+       
+       devpriv->act_chanlist_len=seglen;
+       devpriv->act_chanlist_pos=0;
+
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("SegLen: %d\n", seglen);
+#endif
+       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
+#ifdef PCI171X_EXTDEBUG
+               rt_printk("GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range, devpriv->act_chanlist[i]);
+#endif
+       }
+
+       udelay(1);
+
+       outw(CR_CHAN(chanlist[0]) | (CR_CHAN(chanlist[seglen-1]) << 8) , dev->iobase+PCI171x_MUX); /* select channel interval to scan */
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("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]));
+#endif
+       return 1; // we can serve this with MUX logic
+}
+
+/*
+==============================================================================
+*/
+void start_pacer(comedi_device * dev, int mode, unsigned int divisor1, unsigned int divisor2) 
+{
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: BGN: start_pacer(%d,%u,%u)\n",mode,divisor1,divisor2);
+#endif
+        outw(0xb4, dev->iobase + PCI171x_CNTCTRL);
+        outw(0x74, dev->iobase + PCI171x_CNTCTRL);
+        udelay(1);
+  
+        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);
+        }
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: END: start_pacer(...)\n");
+#endif
+}
+
+/* 
+==============================================================================
+*/
+int pci171x_ai_cancel(comedi_device * dev, comedi_subdevice * s)
+{
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: BGN: pci171x_ai_cancel(...)\n");
+#endif
+
+       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;
+
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: END: pci171x_ai_cancel(...)\n");
+#endif
+       return 0;
+}
+
+/* 
+==============================================================================
+*/
+static int pci171x_reset(comedi_device *dev)
+{
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: BGN: pci171x_reset(...)\n");
+#endif
+       outw(0x30, dev->iobase + PCI171x_CNTCTRL);
+       devpriv->CntrlReg=Control_SW;   // Software trigger, CNT0=100kHz
+       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
+       
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: END: pci171x_reset(...)\n");
+#endif
+       return 0;
+}
+
+/* 
+==============================================================================
+*/
+static int pci1720_reset(comedi_device *dev)
+{
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: BGN: pci1720_reset(...)\n");
+#endif
+       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;
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: END: pci1720_reset(...)\n");
+#endif
+       return 0;
+}
+
+/* 
+==============================================================================
+*/
+static int pci1710_reset(comedi_device *dev)
+{
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: BGN: pci1710_reset(...)\n");
+#endif
+        switch (this_board->cardtype) {
+       case TYPE_PCI1720:
+               return pci1720_reset(dev);
+       default:
+               return pci171x_reset(dev);
+       }
+#ifdef PCI171X_EXTDEBUG
+       rt_printk("adv_pci1710 EDBG: END: pci1710_reset(...)\n");
+#endif
+}
+
+/* 
+==============================================================================
+*/
+static int pci1710_attach(comedi_device *dev,comedi_devconfig *it)
+{
+       comedi_subdevice *s;
+       int ret,subdev;
+       unsigned short io_addr[5],master,irq;
+       struct pcilst_struct *card=NULL;
+        unsigned int iobase;
+       unsigned char pci_bus,pci_slot,pci_func;
+       
+       if (!pci_list_builded) {
+               pci_card_list_init(ADVANTECH_VENDOR,
+#ifdef PCI171X_EXTDEBUG
+                                                   1);
+#else
+                                                   0);
+#endif
+               pci_list_builded=1;
+       }
+
+       rt_printk("comedi%d: adv_pci1710: board=%s",dev->minor,this_board->name);
+
+       if ((card=select_and_alloc_pci_card(ADVANTECH_VENDOR, this_board->device_id, it->options[0], it->options[1]))==NULL) 
+               return -EIO;
+       
+       if ((pci_card_data(card,&pci_bus,&pci_slot,&pci_func,
+                           &io_addr[0],&irq,&master))<0) {
+               pci_card_free(card);
+               rt_printk(" - Can't get configuration data!\n");
+               return -EIO;
+       }
+       
+       iobase=io_addr[2];
+               
+       rt_printk(", b:s:f=%d:%d:%d, io=0x%4x",pci_bus,pci_slot,pci_func,iobase);
+       
+        if (check_region(iobase, this_board->iorange) < 0) {
+               pci_card_free(card);
+               rt_printk("I/O port conflict\n");
+               return -EIO;
+        }
+
+        dev->iobase=iobase;
+        request_region(dev->iobase, this_board->iorange, "Advantech PCI-1710");
+    
+       dev->board_name = this_board->name;
+
+       if((ret=alloc_private(dev,sizeof(pci1710_private)))<0) {
+               release_region(dev->iobase, this_board->iorange);
+               pci_card_free(card);
+               return -ENOMEM;
+       }
+               
+        dev->n_subdevices = 0;
+       if (this_board->n_aichan) dev->n_subdevices++;
+       if (this_board->n_aochan) dev->n_subdevices++;
+       if (this_board->n_dichan) dev->n_subdevices++;
+       if (this_board->n_dochan) dev->n_subdevices++;
+       
+        if((ret=alloc_subdevices(dev))<0) {
+               release_region(dev->iobase, this_board->iorange);
+               pci_card_free(card);
+               return ret;
+       }
+
+       if (this_board->have_irq) {
+               if (irq)  {
+                       if (comedi_request_irq(irq, interrupt_service_pci1710, SA_SHIRQ, "Advantech PCI-1710", dev)) {
+                               rt_printk(", unable to allocate IRQ %d, DISABLING IT", irq);
+                               irq=0; /* Can't use IRQ */
+                       } else {
+                               rt_printk(", irq=%d", 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;
+#ifdef CONFIG_COMEDI_MODE0
+               s->trig[0] = pci171x_ai_mode0;
+#endif
+               s->insn_read=pci171x_insn_read_ai;
+               if (irq) {
+#ifdef CONFIG_COMEDI_MODES
+                       s->trig[1] = pci171x_ai_mode1;
+                       s->trig[3] = pci171x_ai_mode3;
+#endif
+                       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_WRITEABLE|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:
+#ifdef CONFIG_COMEDI_MODE0
+                       s->trig[0] = pci1720_ao_mode0;
+#endif
+                       s->insn_write=pci1720_insn_write_ao;
+                       break;
+               default:
+#ifdef CONFIG_COMEDI_MODE0
+                       s->trig[0] = pci171x_ao_mode0;
+#endif
+                       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;
+#ifdef CONFIG_COMEDI_MODE0
+               s->trig[0] = pci171x_di_mode0;
+#endif
+               s->io_bits=0;           /* all bits input */
+               s->insn_read=pci171x_insn_read_di;
+               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_WRITEABLE|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;
+#ifdef CONFIG_COMEDI_MODE0
+               s->trig[0] = pci171x_do_mode0;
+#endif
+               s->io_bits=(1 << this_board->n_dochan)-1;       /* all bits output */
+               s->state=0;
+               s->insn_read=pci171x_insn_read_do;
+               s->insn_write=pci171x_insn_write_do;
+               s->insn_bits=pci171x_insn_bits_do;
+               subdev++;
+       }
+       
+// XXX There is unused counter 0 from onboard 82C54. Well, one user counter/timer?
+       
+       devpriv->valid=1;
+
+       pci1710_reset(dev);
+
+       return 0;
+}
+
+/* 
+==============================================================================
+*/
+static int pci1710_detach(comedi_device *dev)
+{
+       
+       if (dev->private) 
+               if (devpriv->valid) pci1710_reset(dev);
+       
+       if (dev->irq) free_irq(dev->irq,dev);
+       if (dev->iobase) release_region(dev->iobase,this_board->iorange);
+
+       if (pci_list_builded) {
+               pci_card_list_cleanup(ADVANTECH_VENDOR);
+               pci_list_builded=0;
+       }
+
+
+       return 0;
+}
+
+/* 
+==============================================================================
+*/
+COMEDI_INITCLEANUP(driver_pci1710);
+/* 
+==============================================================================
+*/
+