staging: comedi: adv_pci1710: do comedi_handle_events() in common code patch
[firefly-linux-kernel-4.4.55.git] / drivers / staging / comedi / drivers / adv_pci1710.c
1 /*
2  * comedi/drivers/adv_pci1710.c
3  *
4  * Author: Michal Dobes <dobes@tesnet.cz>
5  *
6  * Thanks to ZhenGang Shang <ZhenGang.Shang@Advantech.com.cn>
7  * for testing and informations.
8  *
9  *  hardware driver for Advantech cards:
10  *   card:   PCI-1710, PCI-1710HG, PCI-1711, PCI-1713, PCI-1720, PCI-1731
11  *   driver: pci1710,  pci1710hg,  pci1711,  pci1713,  pci1720,  pci1731
12  *
13  * Options:
14  *  [0] - PCI bus number - if bus number and slot number are 0,
15  *                         then driver search for first unused card
16  *  [1] - PCI slot number
17  *
18 */
19 /*
20 Driver: adv_pci1710
21 Description: Advantech PCI-1710, PCI-1710HG, PCI-1711, PCI-1713,
22              Advantech PCI-1720, PCI-1731
23 Author: Michal Dobes <dobes@tesnet.cz>
24 Devices: [Advantech] PCI-1710 (adv_pci1710), PCI-1710HG (pci1710hg),
25   PCI-1711 (adv_pci1710), PCI-1713, PCI-1720,
26   PCI-1731
27 Status: works
28
29 This driver supports AI, AO, DI and DO subdevices.
30 AI subdevice supports cmd and insn interface,
31 other subdevices support only insn interface.
32
33 The PCI-1710 and PCI-1710HG have the same PCI device ID, so the
34 driver cannot distinguish between them, as would be normal for a
35 PCI driver.
36
37 Configuration options:
38   [0] - PCI bus of device (optional)
39   [1] - PCI slot of device (optional)
40         If bus/slot is not specified, the first available PCI
41         device will be used.
42 */
43
44 #include <linux/module.h>
45 #include <linux/pci.h>
46 #include <linux/interrupt.h>
47
48 #include "../comedidev.h"
49
50 #include "comedi_fc.h"
51 #include "8253.h"
52 #include "amcc_s5933.h"
53
54 #define PCI171x_AD_DATA  0      /* R:   A/D data */
55 #define PCI171x_SOFTTRG  0      /* W:   soft trigger for A/D */
56 #define PCI171x_RANGE    2      /* W:   A/D gain/range register */
57 #define PCI171x_MUX      4      /* W:   A/D multiplexor control */
58 #define PCI171x_STATUS   6      /* R:   status register */
59 #define PCI171x_CONTROL  6      /* W:   control register */
60 #define PCI171x_CLRINT   8      /* W:   clear interrupts request */
61 #define PCI171x_CLRFIFO  9      /* W:   clear FIFO */
62 #define PCI171x_DA1     10      /* W:   D/A register */
63 #define PCI171x_DA2     12      /* W:   D/A register */
64 #define PCI171x_DAREF   14      /* W:   D/A reference control */
65 #define PCI171x_DI      16      /* R:   digi inputs */
66 #define PCI171x_DO      16      /* R:   digi inputs */
67
68 #define PCI171X_TIMER_BASE      0x18
69
70 #define PCI171x_CNT0    24      /* R/W: 8254 counter 0 */
71 #define PCI171x_CNT1    26      /* R/W: 8254 counter 1 */
72 #define PCI171x_CNT2    28      /* R/W: 8254 counter 2 */
73 #define PCI171x_CNTCTRL 30      /* W:   8254 counter control */
74
75 /* upper bits from status register (PCI171x_STATUS) (lower is same with control
76  * reg) */
77 #define Status_FE       0x0100  /* 1=FIFO is empty */
78 #define Status_FH       0x0200  /* 1=FIFO is half full */
79 #define Status_FF       0x0400  /* 1=FIFO is full, fatal error */
80 #define Status_IRQ      0x0800  /* 1=IRQ occurred */
81 /* bits from control register (PCI171x_CONTROL) */
82 #define Control_CNT0    0x0040  /* 1=CNT0 have external source,
83                                  * 0=have internal 100kHz source */
84 #define Control_ONEFH   0x0020  /* 1=IRQ on FIFO is half full, 0=every sample */
85 #define Control_IRQEN   0x0010  /* 1=enable IRQ */
86 #define Control_GATE    0x0008  /* 1=enable external trigger GATE (8254?) */
87 #define Control_EXT     0x0004  /* 1=external trigger source */
88 #define Control_PACER   0x0002  /* 1=enable internal 8254 trigger source */
89 #define Control_SW      0x0001  /* 1=enable software trigger source */
90 /* bits from counter control register (PCI171x_CNTCTRL) */
91 #define Counter_BCD     0x0001  /* 0 = binary counter, 1 = BCD counter */
92 #define Counter_M0      0x0002  /* M0-M2 select modes 0-5 */
93 #define Counter_M1      0x0004  /* 000 = mode 0, 010 = mode 2 ... */
94 #define Counter_M2      0x0008
95 #define Counter_RW0     0x0010  /* RW0/RW1 select read/write mode */
96 #define Counter_RW1     0x0020
97 #define Counter_SC0     0x0040  /* Select Counter. Only 00 or 11 may */
98 #define Counter_SC1     0x0080  /* be used, 00 for CNT0,
99                                  * 11 for read-back command */
100
101 #define PCI1720_DA0      0      /* W:   D/A register 0 */
102 #define PCI1720_DA1      2      /* W:   D/A register 1 */
103 #define PCI1720_DA2      4      /* W:   D/A register 2 */
104 #define PCI1720_DA3      6      /* W:   D/A register 3 */
105 #define PCI1720_RANGE    8      /* R/W: D/A range register */
106 #define PCI1720_SYNCOUT  9      /* W:   D/A synchronized output register */
107 #define PCI1720_SYNCONT 15      /* R/W: D/A synchronized control */
108
109 /* D/A synchronized control (PCI1720_SYNCONT) */
110 #define Syncont_SC0      1      /* set synchronous output mode */
111
112 static const struct comedi_lrange range_pci1710_3 = {
113         9, {
114                 BIP_RANGE(5),
115                 BIP_RANGE(2.5),
116                 BIP_RANGE(1.25),
117                 BIP_RANGE(0.625),
118                 BIP_RANGE(10),
119                 UNI_RANGE(10),
120                 UNI_RANGE(5),
121                 UNI_RANGE(2.5),
122                 UNI_RANGE(1.25)
123         }
124 };
125
126 static const char range_codes_pci1710_3[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
127                                               0x10, 0x11, 0x12, 0x13 };
128
129 static const struct comedi_lrange range_pci1710hg = {
130         12, {
131                 BIP_RANGE(5),
132                 BIP_RANGE(0.5),
133                 BIP_RANGE(0.05),
134                 BIP_RANGE(0.005),
135                 BIP_RANGE(10),
136                 BIP_RANGE(1),
137                 BIP_RANGE(0.1),
138                 BIP_RANGE(0.01),
139                 UNI_RANGE(10),
140                 UNI_RANGE(1),
141                 UNI_RANGE(0.1),
142                 UNI_RANGE(0.01)
143         }
144 };
145
146 static const char range_codes_pci1710hg[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
147                                               0x05, 0x06, 0x07, 0x10, 0x11,
148                                               0x12, 0x13 };
149
150 static const struct comedi_lrange range_pci17x1 = {
151         5, {
152                 BIP_RANGE(10),
153                 BIP_RANGE(5),
154                 BIP_RANGE(2.5),
155                 BIP_RANGE(1.25),
156                 BIP_RANGE(0.625)
157         }
158 };
159
160 static const char range_codes_pci17x1[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
161
162 static const struct comedi_lrange pci1720_ao_range = {
163         4, {
164                 UNI_RANGE(5),
165                 UNI_RANGE(10),
166                 BIP_RANGE(5),
167                 BIP_RANGE(10)
168         }
169 };
170
171 static const struct comedi_lrange pci171x_ao_range = {
172         2, {
173                 UNI_RANGE(5),
174                 UNI_RANGE(10)
175         }
176 };
177
178 enum pci1710_boardid {
179         BOARD_PCI1710,
180         BOARD_PCI1710HG,
181         BOARD_PCI1711,
182         BOARD_PCI1713,
183         BOARD_PCI1720,
184         BOARD_PCI1731,
185 };
186
187 struct boardtype {
188         const char *name;       /*  board name */
189         int n_aichan;           /*  num of A/D chans */
190         const struct comedi_lrange *rangelist_ai;       /*  rangelist for A/D */
191         const char *rangecode_ai;       /*  range codes for programming */
192         unsigned int is_pci1713:1;
193         unsigned int is_pci1720:1;
194         unsigned int has_irq:1;
195         unsigned int has_large_fifo:1;  /* 4K or 1K FIFO */
196         unsigned int has_diff_ai:1;
197         unsigned int has_ao:1;
198         unsigned int has_di_do:1;
199         unsigned int has_counter:1;
200 };
201
202 static const struct boardtype boardtypes[] = {
203         [BOARD_PCI1710] = {
204                 .name           = "pci1710",
205                 .n_aichan       = 16,
206                 .rangelist_ai   = &range_pci1710_3,
207                 .rangecode_ai   = range_codes_pci1710_3,
208                 .has_irq        = 1,
209                 .has_large_fifo = 1,
210                 .has_diff_ai    = 1,
211                 .has_ao         = 1,
212                 .has_di_do      = 1,
213                 .has_counter    = 1,
214         },
215         [BOARD_PCI1710HG] = {
216                 .name           = "pci1710hg",
217                 .n_aichan       = 16,
218                 .rangelist_ai   = &range_pci1710hg,
219                 .rangecode_ai   = range_codes_pci1710hg,
220                 .has_irq        = 1,
221                 .has_large_fifo = 1,
222                 .has_diff_ai    = 1,
223                 .has_ao         = 1,
224                 .has_di_do      = 1,
225                 .has_counter    = 1,
226         },
227         [BOARD_PCI1711] = {
228                 .name           = "pci1711",
229                 .n_aichan       = 16,
230                 .rangelist_ai   = &range_pci17x1,
231                 .rangecode_ai   = range_codes_pci17x1,
232                 .has_irq        = 1,
233                 .has_ao         = 1,
234                 .has_di_do      = 1,
235                 .has_counter    = 1,
236         },
237         [BOARD_PCI1713] = {
238                 .name           = "pci1713",
239                 .n_aichan       = 32,
240                 .rangelist_ai   = &range_pci1710_3,
241                 .rangecode_ai   = range_codes_pci1710_3,
242                 .is_pci1713     = 1,
243                 .has_irq        = 1,
244                 .has_large_fifo = 1,
245                 .has_diff_ai    = 1,
246         },
247         [BOARD_PCI1720] = {
248                 .name           = "pci1720",
249                 .is_pci1720     = 1,
250                 .has_ao         = 1,
251         },
252         [BOARD_PCI1731] = {
253                 .name           = "pci1731",
254                 .n_aichan       = 16,
255                 .rangelist_ai   = &range_pci17x1,
256                 .rangecode_ai   = range_codes_pci17x1,
257                 .has_irq        = 1,
258                 .has_di_do      = 1,
259         },
260 };
261
262 struct pci1710_private {
263         unsigned int max_samples;
264         unsigned int CntrlReg;  /*  Control register */
265         unsigned char ai_et;
266         unsigned int ai_et_CntrlReg;
267         unsigned int ai_et_MuxVal;
268         unsigned int next_divisor1;
269         unsigned int next_divisor2;
270         unsigned int divisor1;
271         unsigned int divisor2;
272         unsigned int act_chanlist[32];  /*  list of scanned channel */
273         unsigned char saved_seglen;     /* len of the non-repeating chanlist */
274         unsigned char da_ranges;        /*  copy of D/A outpit range register */
275         unsigned int cnt0_write_wait;   /* after a write, wait for update of the
276                                          * internal state */
277 };
278
279 static int pci171x_ai_check_chanlist(struct comedi_device *dev,
280                                      struct comedi_subdevice *s,
281                                      struct comedi_cmd *cmd)
282 {
283         struct pci1710_private *devpriv = dev->private;
284         unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
285         unsigned int last_aref = CR_AREF(cmd->chanlist[0]);
286         unsigned int next_chan = (chan0 + 1) % s->n_chan;
287         unsigned int chansegment[32];
288         unsigned int seglen;
289         int i;
290
291         if (cmd->chanlist_len == 1) {
292                 devpriv->saved_seglen = cmd->chanlist_len;
293                 return 0;
294         }
295
296         /* first channel is always ok */
297         chansegment[0] = cmd->chanlist[0];
298
299         for (i = 1; i < cmd->chanlist_len; i++) {
300                 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
301                 unsigned int aref = CR_AREF(cmd->chanlist[i]);
302
303                 if (cmd->chanlist[0] == cmd->chanlist[i])
304                         break;  /*  we detected a loop, stop */
305
306                 if (aref == AREF_DIFF && (chan & 1)) {
307                         dev_err(dev->class_dev,
308                                 "Odd channel cannot be differential input!\n");
309                         return -EINVAL;
310                 }
311
312                 if (last_aref == AREF_DIFF)
313                         next_chan = (next_chan + 1) % s->n_chan;
314                 if (chan != next_chan) {
315                         dev_err(dev->class_dev,
316                                 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
317                                 i, chan, next_chan, chan0);
318                         return -EINVAL;
319                 }
320
321                 /* next correct channel in list */
322                 chansegment[i] = cmd->chanlist[i];
323                 last_aref = aref;
324         }
325         seglen = i;
326
327         for (i = 0; i < cmd->chanlist_len; i++) {
328                 if (cmd->chanlist[i] != chansegment[i % seglen]) {
329                         dev_err(dev->class_dev,
330                                 "bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
331                                 i, CR_CHAN(chansegment[i]),
332                                 CR_RANGE(chansegment[i]),
333                                 CR_AREF(chansegment[i]),
334                                 CR_CHAN(cmd->chanlist[i % seglen]),
335                                 CR_RANGE(cmd->chanlist[i % seglen]),
336                                 CR_AREF(chansegment[i % seglen]));
337                         return -EINVAL;
338                 }
339         }
340         devpriv->saved_seglen = seglen;
341
342         return 0;
343 }
344
345 static void pci171x_ai_setup_chanlist(struct comedi_device *dev,
346                                       struct comedi_subdevice *s,
347                                       unsigned int *chanlist,
348                                       unsigned int n_chan,
349                                       unsigned int seglen)
350 {
351         const struct boardtype *board = dev->board_ptr;
352         struct pci1710_private *devpriv = dev->private;
353         unsigned int first_chan = CR_CHAN(chanlist[0]);
354         unsigned int last_chan = CR_CHAN(chanlist[seglen - 1]);
355         unsigned int i;
356
357         for (i = 0; i < seglen; i++) {  /*  store range list to card */
358                 unsigned int chan = CR_CHAN(chanlist[i]);
359                 unsigned int range = CR_RANGE(chanlist[i]);
360                 unsigned int aref = CR_AREF(chanlist[i]);
361                 unsigned int rangeval;
362
363                 rangeval = board->rangecode_ai[range];
364                 if (aref == AREF_DIFF)
365                         rangeval |= 0x0020;
366
367                 /* select channel and set range */
368                 outw(chan | (chan << 8), dev->iobase + PCI171x_MUX);
369                 outw(rangeval, dev->iobase + PCI171x_RANGE);
370
371                 devpriv->act_chanlist[i] = chan;
372         }
373         for ( ; i < n_chan; i++)        /* store remainder of channel list */
374                 devpriv->act_chanlist[i] = CR_CHAN(chanlist[i]);
375
376         /* select channel interval to scan */
377         devpriv->ai_et_MuxVal = first_chan | (last_chan << 8);
378         outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
379 }
380
381 static int pci171x_ai_eoc(struct comedi_device *dev,
382                           struct comedi_subdevice *s,
383                           struct comedi_insn *insn,
384                           unsigned long context)
385 {
386         unsigned int status;
387
388         status = inw(dev->iobase + PCI171x_STATUS);
389         if ((status & Status_FE) == 0)
390                 return 0;
391         return -EBUSY;
392 }
393
394 static int pci171x_ai_read_sample(struct comedi_device *dev,
395                                   struct comedi_subdevice *s,
396                                   unsigned int cur_chan,
397                                   unsigned int *val)
398 {
399         const struct boardtype *board = dev->board_ptr;
400         struct pci1710_private *devpriv = dev->private;
401         unsigned int sample;
402         unsigned int chan;
403
404         sample = inw(dev->iobase + PCI171x_AD_DATA);
405         if (!board->is_pci1713) {
406                 /*
407                  * The upper 4 bits of the 16-bit sample are the channel number
408                  * that the sample was acquired from. Verify that this channel
409                  * number matches the expected channel number.
410                  */
411                 chan = sample >> 12;
412                 if (chan != devpriv->act_chanlist[cur_chan]) {
413                         dev_err(dev->class_dev,
414                                 "A/D data droput: received from channel %d, expected %d\n",
415                                 chan, devpriv->act_chanlist[cur_chan]);
416                         return -ENODATA;
417                 }
418         }
419         *val = sample & s->maxdata;
420         return 0;
421 }
422
423 static int pci171x_ai_insn_read(struct comedi_device *dev,
424                                 struct comedi_subdevice *s,
425                                 struct comedi_insn *insn,
426                                 unsigned int *data)
427 {
428         struct pci1710_private *devpriv = dev->private;
429         unsigned int chan = CR_CHAN(insn->chanspec);
430         int ret = 0;
431         int i;
432
433         devpriv->CntrlReg &= Control_CNT0;
434         devpriv->CntrlReg |= Control_SW;        /*  set software trigger */
435         outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
436         outb(0, dev->iobase + PCI171x_CLRFIFO);
437         outb(0, dev->iobase + PCI171x_CLRINT);
438
439         pci171x_ai_setup_chanlist(dev, s, &insn->chanspec, 1, 1);
440
441         for (i = 0; i < insn->n; i++) {
442                 unsigned int val;
443
444                 outw(0, dev->iobase + PCI171x_SOFTTRG); /* start conversion */
445
446                 ret = comedi_timeout(dev, s, insn, pci171x_ai_eoc, 0);
447                 if (ret)
448                         break;
449
450                 ret = pci171x_ai_read_sample(dev, s, chan, &val);
451                 if (ret)
452                         break;
453
454                 data[i] = val;
455         }
456
457         outb(0, dev->iobase + PCI171x_CLRFIFO);
458         outb(0, dev->iobase + PCI171x_CLRINT);
459
460         return ret ? ret : insn->n;
461 }
462
463 static int pci171x_ao_insn_write(struct comedi_device *dev,
464                                  struct comedi_subdevice *s,
465                                  struct comedi_insn *insn,
466                                  unsigned int *data)
467 {
468         struct pci1710_private *devpriv = dev->private;
469         unsigned int chan = CR_CHAN(insn->chanspec);
470         unsigned int range = CR_RANGE(insn->chanspec);
471         unsigned int reg = chan ? PCI171x_DA2 : PCI171x_DA1;
472         unsigned int val = s->readback[chan];
473         int i;
474
475         devpriv->da_ranges &= ~(1 << (chan << 1));
476         devpriv->da_ranges |= (range << (chan << 1));
477         outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
478
479         for (i = 0; i < insn->n; i++) {
480                 val = data[i];
481                 outw(val, dev->iobase + reg);
482         }
483
484         s->readback[chan] = val;
485
486         return insn->n;
487 }
488
489 static int pci171x_di_insn_bits(struct comedi_device *dev,
490                                 struct comedi_subdevice *s,
491                                 struct comedi_insn *insn,
492                                 unsigned int *data)
493 {
494         data[1] = inw(dev->iobase + PCI171x_DI);
495
496         return insn->n;
497 }
498
499 static int pci171x_do_insn_bits(struct comedi_device *dev,
500                                 struct comedi_subdevice *s,
501                                 struct comedi_insn *insn,
502                                 unsigned int *data)
503 {
504         if (comedi_dio_update_state(s, data))
505                 outw(s->state, dev->iobase + PCI171x_DO);
506
507         data[1] = s->state;
508
509         return insn->n;
510 }
511
512 static void pci171x_start_pacer(struct comedi_device *dev,
513                                 bool load_counters)
514 {
515         struct pci1710_private *devpriv = dev->private;
516         unsigned long timer_base = dev->iobase + PCI171X_TIMER_BASE;
517
518         i8254_set_mode(timer_base, 1, 2, I8254_MODE2 | I8254_BINARY);
519         i8254_set_mode(timer_base, 1, 1, I8254_MODE2 | I8254_BINARY);
520
521         if (load_counters) {
522                 i8254_write(timer_base, 1, 2, devpriv->divisor2);
523                 i8254_write(timer_base, 1, 1, devpriv->divisor1);
524         }
525 }
526
527 static int pci171x_counter_insn_read(struct comedi_device *dev,
528                                      struct comedi_subdevice *s,
529                                      struct comedi_insn *insn,
530                                      unsigned int *data)
531 {
532         unsigned int msb, lsb, ccntrl;
533         int i;
534
535         ccntrl = 0xD2;          /* count only */
536         for (i = 0; i < insn->n; i++) {
537                 outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
538
539                 lsb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
540                 msb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
541
542                 data[0] = lsb | (msb << 8);
543         }
544
545         return insn->n;
546 }
547
548 static int pci171x_counter_insn_write(struct comedi_device *dev,
549                                       struct comedi_subdevice *s,
550                                       struct comedi_insn *insn,
551                                       unsigned int *data)
552 {
553         struct pci1710_private *devpriv = dev->private;
554         uint msb, lsb, ccntrl, status;
555
556         lsb = data[0] & 0x00FF;
557         msb = (data[0] & 0xFF00) >> 8;
558
559         /* write lsb, then msb */
560         outw(lsb, dev->iobase + PCI171x_CNT0);
561         outw(msb, dev->iobase + PCI171x_CNT0);
562
563         if (devpriv->cnt0_write_wait) {
564                 /* wait for the new count to be loaded */
565                 ccntrl = 0xE2;
566                 do {
567                         outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
568                         status = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
569                 } while (status & 0x40);
570         }
571
572         return insn->n;
573 }
574
575 static int pci171x_counter_insn_config(struct comedi_device *dev,
576                                        struct comedi_subdevice *s,
577                                        struct comedi_insn *insn,
578                                        unsigned int *data)
579 {
580 #ifdef unused
581         /* This doesn't work like a normal Comedi counter config */
582         struct pci1710_private *devpriv = dev->private;
583         uint ccntrl = 0;
584
585         devpriv->cnt0_write_wait = data[0] & 0x20;
586
587         /* internal or external clock? */
588         if (!(data[0] & 0x10)) {        /* internal */
589                 devpriv->CntrlReg &= ~Control_CNT0;
590         } else {
591                 devpriv->CntrlReg |= Control_CNT0;
592         }
593         outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
594
595         if (data[0] & 0x01)
596                 ccntrl |= Counter_M0;
597         if (data[0] & 0x02)
598                 ccntrl |= Counter_M1;
599         if (data[0] & 0x04)
600                 ccntrl |= Counter_M2;
601         if (data[0] & 0x08)
602                 ccntrl |= Counter_BCD;
603         ccntrl |= Counter_RW0;  /* set read/write mode */
604         ccntrl |= Counter_RW1;
605         outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
606 #endif
607
608         return 1;
609 }
610
611 static int pci1720_ao_insn_write(struct comedi_device *dev,
612                                  struct comedi_subdevice *s,
613                                  struct comedi_insn *insn,
614                                  unsigned int *data)
615 {
616         struct pci1710_private *devpriv = dev->private;
617         unsigned int chan = CR_CHAN(insn->chanspec);
618         unsigned int range = CR_RANGE(insn->chanspec);
619         unsigned int val;
620         int i;
621
622         val = devpriv->da_ranges & (~(0x03 << (chan << 1)));
623         val |= (range << (chan << 1));
624         if (val != devpriv->da_ranges) {
625                 outb(val, dev->iobase + PCI1720_RANGE);
626                 devpriv->da_ranges = val;
627         }
628
629         val = s->readback[chan];
630         for (i = 0; i < insn->n; i++) {
631                 val = data[i];
632                 outw(val, dev->iobase + PCI1720_DA0 + (chan << 1));
633                 outb(0, dev->iobase + PCI1720_SYNCOUT); /* update outputs */
634         }
635
636         s->readback[chan] = val;
637
638         return insn->n;
639 }
640
641 static int pci171x_ai_cancel(struct comedi_device *dev,
642                              struct comedi_subdevice *s)
643 {
644         struct pci1710_private *devpriv = dev->private;
645
646         devpriv->CntrlReg &= Control_CNT0;
647         devpriv->CntrlReg |= Control_SW;
648         /* reset any operations */
649         outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
650         pci171x_start_pacer(dev, false);
651         outb(0, dev->iobase + PCI171x_CLRFIFO);
652         outb(0, dev->iobase + PCI171x_CLRINT);
653
654         return 0;
655 }
656
657 static void pci1710_handle_every_sample(struct comedi_device *dev,
658                                         struct comedi_subdevice *s)
659 {
660         struct comedi_cmd *cmd = &s->async->cmd;
661         unsigned int status;
662         unsigned int val;
663         int ret;
664
665         status = inw(dev->iobase + PCI171x_STATUS);
666         if (status & Status_FE) {
667                 dev_dbg(dev->class_dev, "A/D FIFO empty (%4x)\n", status);
668                 s->async->events |= COMEDI_CB_ERROR;
669                 return;
670         }
671         if (status & Status_FF) {
672                 dev_dbg(dev->class_dev,
673                         "A/D FIFO Full status (Fatal Error!) (%4x)\n", status);
674                 s->async->events |= COMEDI_CB_ERROR;
675                 return;
676         }
677
678         outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear our INT request */
679
680         for (; !(inw(dev->iobase + PCI171x_STATUS) & Status_FE);) {
681                 ret = pci171x_ai_read_sample(dev, s, s->async->cur_chan, &val);
682                 if (ret) {
683                         s->async->events |= COMEDI_CB_ERROR;
684                         break;
685                 }
686
687                 comedi_buf_write_samples(s, &val, 1);
688
689                 if (cmd->stop_src == TRIG_COUNT &&
690                     s->async->scans_done >= cmd->stop_arg) {
691                         s->async->events |= COMEDI_CB_EOA;
692                         break;
693                 }
694         }
695
696         outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear our INT request */
697 }
698
699 static int move_block_from_fifo(struct comedi_device *dev,
700                                 struct comedi_subdevice *s, int n, int turn)
701 {
702         unsigned int val;
703         int ret;
704         int i;
705
706         for (i = 0; i < n; i++) {
707                 ret = pci171x_ai_read_sample(dev, s, s->async->cur_chan, &val);
708                 if (ret) {
709                         s->async->events |= COMEDI_CB_ERROR;
710                         return ret;
711                 }
712
713                 comedi_buf_write_samples(s, &val, 1);
714         }
715         return 0;
716 }
717
718 static void pci1710_handle_fifo(struct comedi_device *dev,
719                                 struct comedi_subdevice *s)
720 {
721         struct pci1710_private *devpriv = dev->private;
722         struct comedi_cmd *cmd = &s->async->cmd;
723         unsigned int nsamples;
724         unsigned int m;
725
726         m = inw(dev->iobase + PCI171x_STATUS);
727         if (!(m & Status_FH)) {
728                 dev_dbg(dev->class_dev, "A/D FIFO not half full! (%4x)\n", m);
729                 s->async->events |= COMEDI_CB_ERROR;
730                 return;
731         }
732         if (m & Status_FF) {
733                 dev_dbg(dev->class_dev,
734                         "A/D FIFO Full status (Fatal Error!) (%4x)\n", m);
735                 s->async->events |= COMEDI_CB_ERROR;
736                 return;
737         }
738
739         nsamples = devpriv->max_samples;
740         if (comedi_samples_to_bytes(s, nsamples) >= s->async->prealloc_bufsz) {
741                 m = comedi_bytes_to_samples(s, s->async->prealloc_bufsz);
742                 if (move_block_from_fifo(dev, s, m, 0))
743                         return;
744                 nsamples -= m;
745         }
746
747         if (nsamples) {
748                 if (move_block_from_fifo(dev, s, nsamples, 1))
749                         return;
750         }
751
752         if (cmd->stop_src == TRIG_COUNT &&
753             s->async->scans_done >= cmd->stop_arg) {
754                 s->async->events |= COMEDI_CB_EOA;
755                 return;
756         }
757         outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear our INT request */
758
759 }
760
761 static irqreturn_t interrupt_service_pci1710(int irq, void *d)
762 {
763         struct comedi_device *dev = d;
764         struct pci1710_private *devpriv = dev->private;
765         struct comedi_subdevice *s;
766         struct comedi_cmd *cmd;
767
768         if (!dev->attached)     /*  is device attached? */
769                 return IRQ_NONE;        /*  no, exit */
770
771         s = dev->read_subdev;
772         cmd = &s->async->cmd;
773
774         /*  is this interrupt from our board? */
775         if (!(inw(dev->iobase + PCI171x_STATUS) & Status_IRQ))
776                 return IRQ_NONE;        /*  no, exit */
777
778         if (devpriv->ai_et) {   /*  Switch from initial TRIG_EXT to TRIG_xxx. */
779                 devpriv->ai_et = 0;
780                 devpriv->CntrlReg &= Control_CNT0;
781                 devpriv->CntrlReg |= Control_SW; /* set software trigger */
782                 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
783                 devpriv->CntrlReg = devpriv->ai_et_CntrlReg;
784                 outb(0, dev->iobase + PCI171x_CLRFIFO);
785                 outb(0, dev->iobase + PCI171x_CLRINT);
786                 outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
787                 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
788                 pci171x_start_pacer(dev, true);
789                 return IRQ_HANDLED;
790         }
791
792         if (cmd->flags & CMDF_WAKE_EOS)
793                 pci1710_handle_every_sample(dev, s);
794         else
795                 pci1710_handle_fifo(dev, s);
796
797         comedi_handle_events(dev, s);
798
799         return IRQ_HANDLED;
800 }
801
802 static int pci171x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
803 {
804         struct pci1710_private *devpriv = dev->private;
805         struct comedi_cmd *cmd = &s->async->cmd;
806
807         pci171x_start_pacer(dev, false);
808
809         pci171x_ai_setup_chanlist(dev, s, cmd->chanlist, cmd->chanlist_len,
810                                   devpriv->saved_seglen);
811
812         outb(0, dev->iobase + PCI171x_CLRFIFO);
813         outb(0, dev->iobase + PCI171x_CLRINT);
814
815         devpriv->CntrlReg &= Control_CNT0;
816         if ((cmd->flags & CMDF_WAKE_EOS) == 0)
817                 devpriv->CntrlReg |= Control_ONEFH;
818
819         devpriv->divisor1 = devpriv->next_divisor1;
820         devpriv->divisor2 = devpriv->next_divisor2;
821
822         if (cmd->convert_src == TRIG_TIMER) {
823                 devpriv->CntrlReg |= Control_PACER | Control_IRQEN;
824                 if (cmd->start_src == TRIG_EXT) {
825                         devpriv->ai_et_CntrlReg = devpriv->CntrlReg;
826                         devpriv->CntrlReg &=
827                             ~(Control_PACER | Control_ONEFH | Control_GATE);
828                         devpriv->CntrlReg |= Control_EXT;
829                         devpriv->ai_et = 1;
830                 } else {        /* TRIG_NOW */
831                         devpriv->ai_et = 0;
832                 }
833                 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
834
835                 if (cmd->start_src == TRIG_NOW)
836                         pci171x_start_pacer(dev, true);
837         } else {        /* TRIG_EXT */
838                 devpriv->CntrlReg |= Control_EXT | Control_IRQEN;
839                 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
840         }
841
842         return 0;
843 }
844
845 static int pci171x_ai_cmdtest(struct comedi_device *dev,
846                               struct comedi_subdevice *s,
847                               struct comedi_cmd *cmd)
848 {
849         struct pci1710_private *devpriv = dev->private;
850         int err = 0;
851         unsigned int arg;
852
853         /* Step 1 : check if triggers are trivially valid */
854
855         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
856         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
857         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
858         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
859         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
860
861         if (err)
862                 return 1;
863
864         /* step 2a: make sure trigger sources are unique */
865
866         err |= cfc_check_trigger_is_unique(cmd->start_src);
867         err |= cfc_check_trigger_is_unique(cmd->convert_src);
868         err |= cfc_check_trigger_is_unique(cmd->stop_src);
869
870         /* step 2b: and mutually compatible */
871
872         if (err)
873                 return 2;
874
875         /* Step 3: check if arguments are trivially valid */
876
877         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
878         err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
879
880         if (cmd->convert_src == TRIG_TIMER)
881                 err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 10000);
882         else    /* TRIG_FOLLOW */
883                 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
884
885         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
886
887         if (cmd->stop_src == TRIG_COUNT)
888                 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
889         else    /* TRIG_NONE */
890                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
891
892         if (err)
893                 return 3;
894
895         /* step 4: fix up any arguments */
896
897         if (cmd->convert_src == TRIG_TIMER) {
898                 arg = cmd->convert_arg;
899                 i8253_cascade_ns_to_timer(I8254_OSC_BASE_10MHZ,
900                                           &devpriv->next_divisor1,
901                                           &devpriv->next_divisor2,
902                                           &arg, cmd->flags);
903                 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
904         }
905
906         if (err)
907                 return 4;
908
909         /* Step 5: check channel list */
910
911         err |= pci171x_ai_check_chanlist(dev, s, cmd);
912
913         if (err)
914                 return 5;
915
916         return 0;
917 }
918
919 static int pci171x_reset(struct comedi_device *dev)
920 {
921         const struct boardtype *board = dev->board_ptr;
922         struct pci1710_private *devpriv = dev->private;
923
924         outw(0x30, dev->iobase + PCI171x_CNTCTRL);
925         /* Software trigger, CNT0=external */
926         devpriv->CntrlReg = Control_SW | Control_CNT0;
927         /* reset any operations */
928         outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
929         outb(0, dev->iobase + PCI171x_CLRFIFO); /*  clear FIFO */
930         outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear INT request */
931         pci171x_start_pacer(dev, false);
932         devpriv->da_ranges = 0;
933         if (board->has_ao) {
934                 /* set DACs to 0..5V */
935                 outb(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
936                 outw(0, dev->iobase + PCI171x_DA1); /* set DA outputs to 0V */
937                 outw(0, dev->iobase + PCI171x_DA2);
938         }
939         outw(0, dev->iobase + PCI171x_DO);      /*  digital outputs to 0 */
940         outb(0, dev->iobase + PCI171x_CLRFIFO); /*  clear FIFO */
941         outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear INT request */
942
943         return 0;
944 }
945
946 static int pci1720_reset(struct comedi_device *dev)
947 {
948         struct pci1710_private *devpriv = dev->private;
949         /* set synchronous output mode */
950         outb(Syncont_SC0, dev->iobase + PCI1720_SYNCONT);
951         devpriv->da_ranges = 0xAA;
952         /* set all ranges to +/-5V */
953         outb(devpriv->da_ranges, dev->iobase + PCI1720_RANGE);
954         outw(0x0800, dev->iobase + PCI1720_DA0);        /*  set outputs to 0V */
955         outw(0x0800, dev->iobase + PCI1720_DA1);
956         outw(0x0800, dev->iobase + PCI1720_DA2);
957         outw(0x0800, dev->iobase + PCI1720_DA3);
958         outb(0, dev->iobase + PCI1720_SYNCOUT); /*  update outputs */
959
960         return 0;
961 }
962
963 static int pci1710_reset(struct comedi_device *dev)
964 {
965         const struct boardtype *board = dev->board_ptr;
966
967         if (board->is_pci1720)
968                 return pci1720_reset(dev);
969
970         return pci171x_reset(dev);
971 }
972
973 static int pci1710_auto_attach(struct comedi_device *dev,
974                                unsigned long context)
975 {
976         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
977         const struct boardtype *board = NULL;
978         struct pci1710_private *devpriv;
979         struct comedi_subdevice *s;
980         int ret, subdev, n_subdevices;
981
982         if (context < ARRAY_SIZE(boardtypes))
983                 board = &boardtypes[context];
984         if (!board)
985                 return -ENODEV;
986         dev->board_ptr = board;
987         dev->board_name = board->name;
988
989         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
990         if (!devpriv)
991                 return -ENOMEM;
992
993         ret = comedi_pci_enable(dev);
994         if (ret)
995                 return ret;
996         dev->iobase = pci_resource_start(pcidev, 2);
997
998         n_subdevices = 0;
999         if (board->n_aichan)
1000                 n_subdevices++;
1001         if (board->has_ao)
1002                 n_subdevices++;
1003         if (board->has_di_do)
1004                 n_subdevices += 2;
1005         if (board->has_counter)
1006                 n_subdevices++;
1007
1008         ret = comedi_alloc_subdevices(dev, n_subdevices);
1009         if (ret)
1010                 return ret;
1011
1012         pci1710_reset(dev);
1013
1014         if (board->has_irq && pcidev->irq) {
1015                 ret = request_irq(pcidev->irq, interrupt_service_pci1710,
1016                                   IRQF_SHARED, dev->board_name, dev);
1017                 if (ret == 0)
1018                         dev->irq = pcidev->irq;
1019         }
1020
1021         subdev = 0;
1022
1023         if (board->n_aichan) {
1024                 s = &dev->subdevices[subdev];
1025                 s->type         = COMEDI_SUBD_AI;
1026                 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
1027                 if (board->has_diff_ai)
1028                         s->subdev_flags |= SDF_DIFF;
1029                 s->n_chan       = board->n_aichan;
1030                 s->maxdata      = 0x0fff;
1031                 s->range_table  = board->rangelist_ai;
1032                 s->insn_read    = pci171x_ai_insn_read;
1033                 if (dev->irq) {
1034                         dev->read_subdev = s;
1035                         s->subdev_flags |= SDF_CMD_READ;
1036                         s->len_chanlist = s->n_chan;
1037                         s->do_cmdtest   = pci171x_ai_cmdtest;
1038                         s->do_cmd       = pci171x_ai_cmd;
1039                         s->cancel       = pci171x_ai_cancel;
1040                 }
1041                 subdev++;
1042         }
1043
1044         if (board->has_ao) {
1045                 s = &dev->subdevices[subdev];
1046                 s->type         = COMEDI_SUBD_AO;
1047                 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1048                 s->maxdata      = 0x0fff;
1049                 if (board->is_pci1720) {
1050                         s->n_chan       = 4;
1051                         s->range_table  = &pci1720_ao_range;
1052                         s->insn_write   = pci1720_ao_insn_write;
1053                 } else {
1054                         s->n_chan       = 2;
1055                         s->range_table  = &pci171x_ao_range;
1056                         s->insn_write   = pci171x_ao_insn_write;
1057                 }
1058
1059                 ret = comedi_alloc_subdev_readback(s);
1060                 if (ret)
1061                         return ret;
1062
1063                 /* initialize the readback values to match the board reset */
1064                 if (board->is_pci1720) {
1065                         int i;
1066
1067                         for (i = 0; i < s->n_chan; i++)
1068                                 s->readback[i] = 0x0800;
1069                 }
1070
1071                 subdev++;
1072         }
1073
1074         if (board->has_di_do) {
1075                 s = &dev->subdevices[subdev];
1076                 s->type         = COMEDI_SUBD_DI;
1077                 s->subdev_flags = SDF_READABLE;
1078                 s->n_chan       = 16;
1079                 s->maxdata      = 1;
1080                 s->range_table  = &range_digital;
1081                 s->insn_bits    = pci171x_di_insn_bits;
1082                 subdev++;
1083
1084                 s = &dev->subdevices[subdev];
1085                 s->type         = COMEDI_SUBD_DO;
1086                 s->subdev_flags = SDF_WRITABLE;
1087                 s->n_chan       = 16;
1088                 s->maxdata      = 1;
1089                 s->range_table  = &range_digital;
1090                 s->insn_bits    = pci171x_do_insn_bits;
1091                 subdev++;
1092         }
1093
1094         if (board->has_counter) {
1095                 s = &dev->subdevices[subdev];
1096                 s->type         = COMEDI_SUBD_COUNTER;
1097                 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
1098                 s->n_chan       = 1;
1099                 s->maxdata      = 0xffff;
1100                 s->range_table  = &range_unknown;
1101                 s->insn_read    = pci171x_counter_insn_read;
1102                 s->insn_write   = pci171x_counter_insn_write;
1103                 s->insn_config  = pci171x_counter_insn_config;
1104                 subdev++;
1105         }
1106
1107         /* max_samples is half the FIFO size (2 bytes/sample) */
1108         devpriv->max_samples = (board->has_large_fifo) ? 2048 : 512;
1109
1110         return 0;
1111 }
1112
1113 static void pci1710_detach(struct comedi_device *dev)
1114 {
1115         if (dev->iobase)
1116                 pci1710_reset(dev);
1117         comedi_pci_detach(dev);
1118 }
1119
1120 static struct comedi_driver adv_pci1710_driver = {
1121         .driver_name    = "adv_pci1710",
1122         .module         = THIS_MODULE,
1123         .auto_attach    = pci1710_auto_attach,
1124         .detach         = pci1710_detach,
1125 };
1126
1127 static int adv_pci1710_pci_probe(struct pci_dev *dev,
1128                                  const struct pci_device_id *id)
1129 {
1130         return comedi_pci_auto_config(dev, &adv_pci1710_driver,
1131                                       id->driver_data);
1132 }
1133
1134 static const struct pci_device_id adv_pci1710_pci_table[] = {
1135         {
1136                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1137                                PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050),
1138                 .driver_data = BOARD_PCI1710,
1139         }, {
1140                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1141                                PCI_VENDOR_ID_ADVANTECH, 0x0000),
1142                 .driver_data = BOARD_PCI1710,
1143         }, {
1144                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1145                                PCI_VENDOR_ID_ADVANTECH, 0xb100),
1146                 .driver_data = BOARD_PCI1710,
1147         }, {
1148                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1149                                PCI_VENDOR_ID_ADVANTECH, 0xb200),
1150                 .driver_data = BOARD_PCI1710,
1151         }, {
1152                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1153                                PCI_VENDOR_ID_ADVANTECH, 0xc100),
1154                 .driver_data = BOARD_PCI1710,
1155         }, {
1156                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1157                                PCI_VENDOR_ID_ADVANTECH, 0xc200),
1158                 .driver_data = BOARD_PCI1710,
1159         }, {
1160                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd100),
1161                 .driver_data = BOARD_PCI1710,
1162         }, {
1163                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1164                                PCI_VENDOR_ID_ADVANTECH, 0x0002),
1165                 .driver_data = BOARD_PCI1710HG,
1166         }, {
1167                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1168                                PCI_VENDOR_ID_ADVANTECH, 0xb102),
1169                 .driver_data = BOARD_PCI1710HG,
1170         }, {
1171                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1172                                PCI_VENDOR_ID_ADVANTECH, 0xb202),
1173                 .driver_data = BOARD_PCI1710HG,
1174         }, {
1175                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1176                                PCI_VENDOR_ID_ADVANTECH, 0xc102),
1177                 .driver_data = BOARD_PCI1710HG,
1178         }, {
1179                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1180                                PCI_VENDOR_ID_ADVANTECH, 0xc202),
1181                 .driver_data = BOARD_PCI1710HG,
1182         }, {
1183                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd102),
1184                 .driver_data = BOARD_PCI1710HG,
1185         },
1186         { PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 },
1187         { PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 },
1188         { PCI_VDEVICE(ADVANTECH, 0x1720), BOARD_PCI1720 },
1189         { PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 },
1190         { 0 }
1191 };
1192 MODULE_DEVICE_TABLE(pci, adv_pci1710_pci_table);
1193
1194 static struct pci_driver adv_pci1710_pci_driver = {
1195         .name           = "adv_pci1710",
1196         .id_table       = adv_pci1710_pci_table,
1197         .probe          = adv_pci1710_pci_probe,
1198         .remove         = comedi_pci_auto_unconfig,
1199 };
1200 module_comedi_pci_driver(adv_pci1710_driver, adv_pci1710_pci_driver);
1201
1202 MODULE_AUTHOR("Comedi http://www.comedi.org");
1203 MODULE_DESCRIPTION("Comedi: Advantech PCI-1710 Series Multifunction DAS Cards");
1204 MODULE_LICENSE("GPL");