Version:  2.0.40 2.2.26 2.4.37 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15 3.16 3.17 3.18 3.19 4.0

Linux/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         int ret = 0;
430         int i;
431 
432         devpriv->CntrlReg &= Control_CNT0;
433         devpriv->CntrlReg |= Control_SW;        /*  set software trigger */
434         outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
435         outb(0, dev->iobase + PCI171x_CLRFIFO);
436         outb(0, dev->iobase + PCI171x_CLRINT);
437 
438         pci171x_ai_setup_chanlist(dev, s, &insn->chanspec, 1, 1);
439 
440         for (i = 0; i < insn->n; i++) {
441                 unsigned int val;
442 
443                 outw(0, dev->iobase + PCI171x_SOFTTRG); /* start conversion */
444 
445                 ret = comedi_timeout(dev, s, insn, pci171x_ai_eoc, 0);
446                 if (ret)
447                         break;
448 
449                 ret = pci171x_ai_read_sample(dev, s, 0, &val);
450                 if (ret)
451                         break;
452 
453                 data[i] = val;
454         }
455 
456         outb(0, dev->iobase + PCI171x_CLRFIFO);
457         outb(0, dev->iobase + PCI171x_CLRINT);
458 
459         return ret ? ret : insn->n;
460 }
461 
462 static int pci171x_ao_insn_write(struct comedi_device *dev,
463                                  struct comedi_subdevice *s,
464                                  struct comedi_insn *insn,
465                                  unsigned int *data)
466 {
467         struct pci1710_private *devpriv = dev->private;
468         unsigned int chan = CR_CHAN(insn->chanspec);
469         unsigned int range = CR_RANGE(insn->chanspec);
470         unsigned int reg = chan ? PCI171x_DA2 : PCI171x_DA1;
471         unsigned int val = s->readback[chan];
472         int i;
473 
474         devpriv->da_ranges &= ~(1 << (chan << 1));
475         devpriv->da_ranges |= (range << (chan << 1));
476         outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
477 
478         for (i = 0; i < insn->n; i++) {
479                 val = data[i];
480                 outw(val, dev->iobase + reg);
481         }
482 
483         s->readback[chan] = val;
484 
485         return insn->n;
486 }
487 
488 static int pci171x_di_insn_bits(struct comedi_device *dev,
489                                 struct comedi_subdevice *s,
490                                 struct comedi_insn *insn,
491                                 unsigned int *data)
492 {
493         data[1] = inw(dev->iobase + PCI171x_DI);
494 
495         return insn->n;
496 }
497 
498 static int pci171x_do_insn_bits(struct comedi_device *dev,
499                                 struct comedi_subdevice *s,
500                                 struct comedi_insn *insn,
501                                 unsigned int *data)
502 {
503         if (comedi_dio_update_state(s, data))
504                 outw(s->state, dev->iobase + PCI171x_DO);
505 
506         data[1] = s->state;
507 
508         return insn->n;
509 }
510 
511 static void pci171x_start_pacer(struct comedi_device *dev,
512                                 bool load_counters)
513 {
514         struct pci1710_private *devpriv = dev->private;
515         unsigned long timer_base = dev->iobase + PCI171X_TIMER_BASE;
516 
517         i8254_set_mode(timer_base, 1, 2, I8254_MODE2 | I8254_BINARY);
518         i8254_set_mode(timer_base, 1, 1, I8254_MODE2 | I8254_BINARY);
519 
520         if (load_counters) {
521                 i8254_write(timer_base, 1, 2, devpriv->divisor2);
522                 i8254_write(timer_base, 1, 1, devpriv->divisor1);
523         }
524 }
525 
526 static int pci171x_counter_insn_read(struct comedi_device *dev,
527                                      struct comedi_subdevice *s,
528                                      struct comedi_insn *insn,
529                                      unsigned int *data)
530 {
531         unsigned int msb, lsb, ccntrl;
532         int i;
533 
534         ccntrl = 0xD2;          /* count only */
535         for (i = 0; i < insn->n; i++) {
536                 outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
537 
538                 lsb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
539                 msb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
540 
541                 data[0] = lsb | (msb << 8);
542         }
543 
544         return insn->n;
545 }
546 
547 static int pci171x_counter_insn_write(struct comedi_device *dev,
548                                       struct comedi_subdevice *s,
549                                       struct comedi_insn *insn,
550                                       unsigned int *data)
551 {
552         struct pci1710_private *devpriv = dev->private;
553         uint msb, lsb, ccntrl, status;
554 
555         lsb = data[0] & 0x00FF;
556         msb = (data[0] & 0xFF00) >> 8;
557 
558         /* write lsb, then msb */
559         outw(lsb, dev->iobase + PCI171x_CNT0);
560         outw(msb, dev->iobase + PCI171x_CNT0);
561 
562         if (devpriv->cnt0_write_wait) {
563                 /* wait for the new count to be loaded */
564                 ccntrl = 0xE2;
565                 do {
566                         outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
567                         status = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
568                 } while (status & 0x40);
569         }
570 
571         return insn->n;
572 }
573 
574 static int pci171x_counter_insn_config(struct comedi_device *dev,
575                                        struct comedi_subdevice *s,
576                                        struct comedi_insn *insn,
577                                        unsigned int *data)
578 {
579 #ifdef unused
580         /* This doesn't work like a normal Comedi counter config */
581         struct pci1710_private *devpriv = dev->private;
582         uint ccntrl = 0;
583 
584         devpriv->cnt0_write_wait = data[0] & 0x20;
585 
586         /* internal or external clock? */
587         if (!(data[0] & 0x10)) {        /* internal */
588                 devpriv->CntrlReg &= ~Control_CNT0;
589         } else {
590                 devpriv->CntrlReg |= Control_CNT0;
591         }
592         outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
593 
594         if (data[0] & 0x01)
595                 ccntrl |= Counter_M0;
596         if (data[0] & 0x02)
597                 ccntrl |= Counter_M1;
598         if (data[0] & 0x04)
599                 ccntrl |= Counter_M2;
600         if (data[0] & 0x08)
601                 ccntrl |= Counter_BCD;
602         ccntrl |= Counter_RW0;  /* set read/write mode */
603         ccntrl |= Counter_RW1;
604         outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
605 #endif
606 
607         return 1;
608 }
609 
610 static int pci1720_ao_insn_write(struct comedi_device *dev,
611                                  struct comedi_subdevice *s,
612                                  struct comedi_insn *insn,
613                                  unsigned int *data)
614 {
615         struct pci1710_private *devpriv = dev->private;
616         unsigned int chan = CR_CHAN(insn->chanspec);
617         unsigned int range = CR_RANGE(insn->chanspec);
618         unsigned int val;
619         int i;
620 
621         val = devpriv->da_ranges & (~(0x03 << (chan << 1)));
622         val |= (range << (chan << 1));
623         if (val != devpriv->da_ranges) {
624                 outb(val, dev->iobase + PCI1720_RANGE);
625                 devpriv->da_ranges = val;
626         }
627 
628         val = s->readback[chan];
629         for (i = 0; i < insn->n; i++) {
630                 val = data[i];
631                 outw(val, dev->iobase + PCI1720_DA0 + (chan << 1));
632                 outb(0, dev->iobase + PCI1720_SYNCOUT); /* update outputs */
633         }
634 
635         s->readback[chan] = val;
636 
637         return insn->n;
638 }
639 
640 static int pci171x_ai_cancel(struct comedi_device *dev,
641                              struct comedi_subdevice *s)
642 {
643         struct pci1710_private *devpriv = dev->private;
644 
645         devpriv->CntrlReg &= Control_CNT0;
646         devpriv->CntrlReg |= Control_SW;
647         /* reset any operations */
648         outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
649         pci171x_start_pacer(dev, false);
650         outb(0, dev->iobase + PCI171x_CLRFIFO);
651         outb(0, dev->iobase + PCI171x_CLRINT);
652 
653         return 0;
654 }
655 
656 static void pci1710_handle_every_sample(struct comedi_device *dev,
657                                         struct comedi_subdevice *s)
658 {
659         struct comedi_cmd *cmd = &s->async->cmd;
660         unsigned int status;
661         unsigned int val;
662         int ret;
663 
664         status = inw(dev->iobase + PCI171x_STATUS);
665         if (status & Status_FE) {
666                 dev_dbg(dev->class_dev, "A/D FIFO empty (%4x)\n", status);
667                 s->async->events |= COMEDI_CB_ERROR;
668                 return;
669         }
670         if (status & Status_FF) {
671                 dev_dbg(dev->class_dev,
672                         "A/D FIFO Full status (Fatal Error!) (%4x)\n", status);
673                 s->async->events |= COMEDI_CB_ERROR;
674                 return;
675         }
676 
677         outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear our INT request */
678 
679         for (; !(inw(dev->iobase + PCI171x_STATUS) & Status_FE);) {
680                 ret = pci171x_ai_read_sample(dev, s, s->async->cur_chan, &val);
681                 if (ret) {
682                         s->async->events |= COMEDI_CB_ERROR;
683                         break;
684                 }
685 
686                 comedi_buf_write_samples(s, &val, 1);
687 
688                 if (cmd->stop_src == TRIG_COUNT &&
689                     s->async->scans_done >= cmd->stop_arg) {
690                         s->async->events |= COMEDI_CB_EOA;
691                         break;
692                 }
693         }
694 
695         outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear our INT request */
696 }
697 
698 static void pci1710_handle_fifo(struct comedi_device *dev,
699                                 struct comedi_subdevice *s)
700 {
701         struct pci1710_private *devpriv = dev->private;
702         struct comedi_async *async = s->async;
703         struct comedi_cmd *cmd = &async->cmd;
704         unsigned int status;
705         int i;
706 
707         status = inw(dev->iobase + PCI171x_STATUS);
708         if (!(status & Status_FH)) {
709                 dev_dbg(dev->class_dev, "A/D FIFO not half full!\n");
710                 async->events |= COMEDI_CB_ERROR;
711                 return;
712         }
713         if (status & Status_FF) {
714                 dev_dbg(dev->class_dev,
715                         "A/D FIFO Full status (Fatal Error!)\n");
716                 async->events |= COMEDI_CB_ERROR;
717                 return;
718         }
719 
720         for (i = 0; i < devpriv->max_samples; i++) {
721                 unsigned int val;
722                 int ret;
723 
724                 ret = pci171x_ai_read_sample(dev, s, s->async->cur_chan, &val);
725                 if (ret) {
726                         s->async->events |= COMEDI_CB_ERROR;
727                         break;
728                 }
729 
730                 if (!comedi_buf_write_samples(s, &val, 1))
731                         break;
732 
733                 if (cmd->stop_src == TRIG_COUNT &&
734                     async->scans_done >= cmd->stop_arg) {
735                         async->events |= COMEDI_CB_EOA;
736                         break;
737                 }
738         }
739 
740         outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear our INT request */
741 }
742 
743 static irqreturn_t interrupt_service_pci1710(int irq, void *d)
744 {
745         struct comedi_device *dev = d;
746         struct pci1710_private *devpriv = dev->private;
747         struct comedi_subdevice *s;
748         struct comedi_cmd *cmd;
749 
750         if (!dev->attached)     /*  is device attached? */
751                 return IRQ_NONE;        /*  no, exit */
752 
753         s = dev->read_subdev;
754         cmd = &s->async->cmd;
755 
756         /*  is this interrupt from our board? */
757         if (!(inw(dev->iobase + PCI171x_STATUS) & Status_IRQ))
758                 return IRQ_NONE;        /*  no, exit */
759 
760         if (devpriv->ai_et) {   /*  Switch from initial TRIG_EXT to TRIG_xxx. */
761                 devpriv->ai_et = 0;
762                 devpriv->CntrlReg &= Control_CNT0;
763                 devpriv->CntrlReg |= Control_SW; /* set software trigger */
764                 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
765                 devpriv->CntrlReg = devpriv->ai_et_CntrlReg;
766                 outb(0, dev->iobase + PCI171x_CLRFIFO);
767                 outb(0, dev->iobase + PCI171x_CLRINT);
768                 outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
769                 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
770                 pci171x_start_pacer(dev, true);
771                 return IRQ_HANDLED;
772         }
773 
774         if (cmd->flags & CMDF_WAKE_EOS)
775                 pci1710_handle_every_sample(dev, s);
776         else
777                 pci1710_handle_fifo(dev, s);
778 
779         comedi_handle_events(dev, s);
780 
781         return IRQ_HANDLED;
782 }
783 
784 static int pci171x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
785 {
786         struct pci1710_private *devpriv = dev->private;
787         struct comedi_cmd *cmd = &s->async->cmd;
788 
789         pci171x_start_pacer(dev, false);
790 
791         pci171x_ai_setup_chanlist(dev, s, cmd->chanlist, cmd->chanlist_len,
792                                   devpriv->saved_seglen);
793 
794         outb(0, dev->iobase + PCI171x_CLRFIFO);
795         outb(0, dev->iobase + PCI171x_CLRINT);
796 
797         devpriv->CntrlReg &= Control_CNT0;
798         if ((cmd->flags & CMDF_WAKE_EOS) == 0)
799                 devpriv->CntrlReg |= Control_ONEFH;
800 
801         devpriv->divisor1 = devpriv->next_divisor1;
802         devpriv->divisor2 = devpriv->next_divisor2;
803 
804         if (cmd->convert_src == TRIG_TIMER) {
805                 devpriv->CntrlReg |= Control_PACER | Control_IRQEN;
806                 if (cmd->start_src == TRIG_EXT) {
807                         devpriv->ai_et_CntrlReg = devpriv->CntrlReg;
808                         devpriv->CntrlReg &=
809                             ~(Control_PACER | Control_ONEFH | Control_GATE);
810                         devpriv->CntrlReg |= Control_EXT;
811                         devpriv->ai_et = 1;
812                 } else {        /* TRIG_NOW */
813                         devpriv->ai_et = 0;
814                 }
815                 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
816 
817                 if (cmd->start_src == TRIG_NOW)
818                         pci171x_start_pacer(dev, true);
819         } else {        /* TRIG_EXT */
820                 devpriv->CntrlReg |= Control_EXT | Control_IRQEN;
821                 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
822         }
823 
824         return 0;
825 }
826 
827 static int pci171x_ai_cmdtest(struct comedi_device *dev,
828                               struct comedi_subdevice *s,
829                               struct comedi_cmd *cmd)
830 {
831         struct pci1710_private *devpriv = dev->private;
832         int err = 0;
833         unsigned int arg;
834 
835         /* Step 1 : check if triggers are trivially valid */
836 
837         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
838         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
839         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
840         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
841         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
842 
843         if (err)
844                 return 1;
845 
846         /* step 2a: make sure trigger sources are unique */
847 
848         err |= cfc_check_trigger_is_unique(cmd->start_src);
849         err |= cfc_check_trigger_is_unique(cmd->convert_src);
850         err |= cfc_check_trigger_is_unique(cmd->stop_src);
851 
852         /* step 2b: and mutually compatible */
853 
854         if (err)
855                 return 2;
856 
857         /* Step 3: check if arguments are trivially valid */
858 
859         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
860         err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
861 
862         if (cmd->convert_src == TRIG_TIMER)
863                 err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 10000);
864         else    /* TRIG_FOLLOW */
865                 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
866 
867         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
868 
869         if (cmd->stop_src == TRIG_COUNT)
870                 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
871         else    /* TRIG_NONE */
872                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
873 
874         if (err)
875                 return 3;
876 
877         /* step 4: fix up any arguments */
878 
879         if (cmd->convert_src == TRIG_TIMER) {
880                 arg = cmd->convert_arg;
881                 i8253_cascade_ns_to_timer(I8254_OSC_BASE_10MHZ,
882                                           &devpriv->next_divisor1,
883                                           &devpriv->next_divisor2,
884                                           &arg, cmd->flags);
885                 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
886         }
887 
888         if (err)
889                 return 4;
890 
891         /* Step 5: check channel list */
892 
893         err |= pci171x_ai_check_chanlist(dev, s, cmd);
894 
895         if (err)
896                 return 5;
897 
898         return 0;
899 }
900 
901 static int pci171x_reset(struct comedi_device *dev)
902 {
903         const struct boardtype *board = dev->board_ptr;
904         struct pci1710_private *devpriv = dev->private;
905 
906         outw(0x30, dev->iobase + PCI171x_CNTCTRL);
907         /* Software trigger, CNT0=external */
908         devpriv->CntrlReg = Control_SW | Control_CNT0;
909         /* reset any operations */
910         outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
911         outb(0, dev->iobase + PCI171x_CLRFIFO); /*  clear FIFO */
912         outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear INT request */
913         pci171x_start_pacer(dev, false);
914         devpriv->da_ranges = 0;
915         if (board->has_ao) {
916                 /* set DACs to 0..5V */
917                 outb(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
918                 outw(0, dev->iobase + PCI171x_DA1); /* set DA outputs to 0V */
919                 outw(0, dev->iobase + PCI171x_DA2);
920         }
921         outw(0, dev->iobase + PCI171x_DO);      /*  digital outputs to 0 */
922         outb(0, dev->iobase + PCI171x_CLRFIFO); /*  clear FIFO */
923         outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear INT request */
924 
925         return 0;
926 }
927 
928 static int pci1720_reset(struct comedi_device *dev)
929 {
930         struct pci1710_private *devpriv = dev->private;
931         /* set synchronous output mode */
932         outb(Syncont_SC0, dev->iobase + PCI1720_SYNCONT);
933         devpriv->da_ranges = 0xAA;
934         /* set all ranges to +/-5V */
935         outb(devpriv->da_ranges, dev->iobase + PCI1720_RANGE);
936         outw(0x0800, dev->iobase + PCI1720_DA0);        /*  set outputs to 0V */
937         outw(0x0800, dev->iobase + PCI1720_DA1);
938         outw(0x0800, dev->iobase + PCI1720_DA2);
939         outw(0x0800, dev->iobase + PCI1720_DA3);
940         outb(0, dev->iobase + PCI1720_SYNCOUT); /*  update outputs */
941 
942         return 0;
943 }
944 
945 static int pci1710_reset(struct comedi_device *dev)
946 {
947         const struct boardtype *board = dev->board_ptr;
948 
949         if (board->is_pci1720)
950                 return pci1720_reset(dev);
951 
952         return pci171x_reset(dev);
953 }
954 
955 static int pci1710_auto_attach(struct comedi_device *dev,
956                                unsigned long context)
957 {
958         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
959         const struct boardtype *board = NULL;
960         struct pci1710_private *devpriv;
961         struct comedi_subdevice *s;
962         int ret, subdev, n_subdevices;
963 
964         if (context < ARRAY_SIZE(boardtypes))
965                 board = &boardtypes[context];
966         if (!board)
967                 return -ENODEV;
968         dev->board_ptr = board;
969         dev->board_name = board->name;
970 
971         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
972         if (!devpriv)
973                 return -ENOMEM;
974 
975         ret = comedi_pci_enable(dev);
976         if (ret)
977                 return ret;
978         dev->iobase = pci_resource_start(pcidev, 2);
979 
980         n_subdevices = 0;
981         if (board->n_aichan)
982                 n_subdevices++;
983         if (board->has_ao)
984                 n_subdevices++;
985         if (board->has_di_do)
986                 n_subdevices += 2;
987         if (board->has_counter)
988                 n_subdevices++;
989 
990         ret = comedi_alloc_subdevices(dev, n_subdevices);
991         if (ret)
992                 return ret;
993 
994         pci1710_reset(dev);
995 
996         if (board->has_irq && pcidev->irq) {
997                 ret = request_irq(pcidev->irq, interrupt_service_pci1710,
998                                   IRQF_SHARED, dev->board_name, dev);
999                 if (ret == 0)
1000                         dev->irq = pcidev->irq;
1001         }
1002 
1003         subdev = 0;
1004 
1005         if (board->n_aichan) {
1006                 s = &dev->subdevices[subdev];
1007                 s->type         = COMEDI_SUBD_AI;
1008                 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
1009                 if (board->has_diff_ai)
1010                         s->subdev_flags |= SDF_DIFF;
1011                 s->n_chan       = board->n_aichan;
1012                 s->maxdata      = 0x0fff;
1013                 s->range_table  = board->rangelist_ai;
1014                 s->insn_read    = pci171x_ai_insn_read;
1015                 if (dev->irq) {
1016                         dev->read_subdev = s;
1017                         s->subdev_flags |= SDF_CMD_READ;
1018                         s->len_chanlist = s->n_chan;
1019                         s->do_cmdtest   = pci171x_ai_cmdtest;
1020                         s->do_cmd       = pci171x_ai_cmd;
1021                         s->cancel       = pci171x_ai_cancel;
1022                 }
1023                 subdev++;
1024         }
1025 
1026         if (board->has_ao) {
1027                 s = &dev->subdevices[subdev];
1028                 s->type         = COMEDI_SUBD_AO;
1029                 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1030                 s->maxdata      = 0x0fff;
1031                 if (board->is_pci1720) {
1032                         s->n_chan       = 4;
1033                         s->range_table  = &pci1720_ao_range;
1034                         s->insn_write   = pci1720_ao_insn_write;
1035                 } else {
1036                         s->n_chan       = 2;
1037                         s->range_table  = &pci171x_ao_range;
1038                         s->insn_write   = pci171x_ao_insn_write;
1039                 }
1040 
1041                 ret = comedi_alloc_subdev_readback(s);
1042                 if (ret)
1043                         return ret;
1044 
1045                 /* initialize the readback values to match the board reset */
1046                 if (board->is_pci1720) {
1047                         int i;
1048 
1049                         for (i = 0; i < s->n_chan; i++)
1050                                 s->readback[i] = 0x0800;
1051                 }
1052 
1053                 subdev++;
1054         }
1055 
1056         if (board->has_di_do) {
1057                 s = &dev->subdevices[subdev];
1058                 s->type         = COMEDI_SUBD_DI;
1059                 s->subdev_flags = SDF_READABLE;
1060                 s->n_chan       = 16;
1061                 s->maxdata      = 1;
1062                 s->range_table  = &range_digital;
1063                 s->insn_bits    = pci171x_di_insn_bits;
1064                 subdev++;
1065 
1066                 s = &dev->subdevices[subdev];
1067                 s->type         = COMEDI_SUBD_DO;
1068                 s->subdev_flags = SDF_WRITABLE;
1069                 s->n_chan       = 16;
1070                 s->maxdata      = 1;
1071                 s->range_table  = &range_digital;
1072                 s->insn_bits    = pci171x_do_insn_bits;
1073                 subdev++;
1074         }
1075 
1076         if (board->has_counter) {
1077                 s = &dev->subdevices[subdev];
1078                 s->type         = COMEDI_SUBD_COUNTER;
1079                 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
1080                 s->n_chan       = 1;
1081                 s->maxdata      = 0xffff;
1082                 s->range_table  = &range_unknown;
1083                 s->insn_read    = pci171x_counter_insn_read;
1084                 s->insn_write   = pci171x_counter_insn_write;
1085                 s->insn_config  = pci171x_counter_insn_config;
1086                 subdev++;
1087         }
1088 
1089         /* max_samples is half the FIFO size (2 bytes/sample) */
1090         devpriv->max_samples = (board->has_large_fifo) ? 2048 : 512;
1091 
1092         return 0;
1093 }
1094 
1095 static void pci1710_detach(struct comedi_device *dev)
1096 {
1097         if (dev->iobase)
1098                 pci1710_reset(dev);
1099         comedi_pci_detach(dev);
1100 }
1101 
1102 static struct comedi_driver adv_pci1710_driver = {
1103         .driver_name    = "adv_pci1710",
1104         .module         = THIS_MODULE,
1105         .auto_attach    = pci1710_auto_attach,
1106         .detach         = pci1710_detach,
1107 };
1108 
1109 static int adv_pci1710_pci_probe(struct pci_dev *dev,
1110                                  const struct pci_device_id *id)
1111 {
1112         return comedi_pci_auto_config(dev, &adv_pci1710_driver,
1113                                       id->driver_data);
1114 }
1115 
1116 static const struct pci_device_id adv_pci1710_pci_table[] = {
1117         {
1118                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1119                                PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050),
1120                 .driver_data = BOARD_PCI1710,
1121         }, {
1122                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1123                                PCI_VENDOR_ID_ADVANTECH, 0x0000),
1124                 .driver_data = BOARD_PCI1710,
1125         }, {
1126                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1127                                PCI_VENDOR_ID_ADVANTECH, 0xb100),
1128                 .driver_data = BOARD_PCI1710,
1129         }, {
1130                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1131                                PCI_VENDOR_ID_ADVANTECH, 0xb200),
1132                 .driver_data = BOARD_PCI1710,
1133         }, {
1134                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1135                                PCI_VENDOR_ID_ADVANTECH, 0xc100),
1136                 .driver_data = BOARD_PCI1710,
1137         }, {
1138                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1139                                PCI_VENDOR_ID_ADVANTECH, 0xc200),
1140                 .driver_data = BOARD_PCI1710,
1141         }, {
1142                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd100),
1143                 .driver_data = BOARD_PCI1710,
1144         }, {
1145                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1146                                PCI_VENDOR_ID_ADVANTECH, 0x0002),
1147                 .driver_data = BOARD_PCI1710HG,
1148         }, {
1149                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1150                                PCI_VENDOR_ID_ADVANTECH, 0xb102),
1151                 .driver_data = BOARD_PCI1710HG,
1152         }, {
1153                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1154                                PCI_VENDOR_ID_ADVANTECH, 0xb202),
1155                 .driver_data = BOARD_PCI1710HG,
1156         }, {
1157                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1158                                PCI_VENDOR_ID_ADVANTECH, 0xc102),
1159                 .driver_data = BOARD_PCI1710HG,
1160         }, {
1161                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1162                                PCI_VENDOR_ID_ADVANTECH, 0xc202),
1163                 .driver_data = BOARD_PCI1710HG,
1164         }, {
1165                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd102),
1166                 .driver_data = BOARD_PCI1710HG,
1167         },
1168         { PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 },
1169         { PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 },
1170         { PCI_VDEVICE(ADVANTECH, 0x1720), BOARD_PCI1720 },
1171         { PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 },
1172         { 0 }
1173 };
1174 MODULE_DEVICE_TABLE(pci, adv_pci1710_pci_table);
1175 
1176 static struct pci_driver adv_pci1710_pci_driver = {
1177         .name           = "adv_pci1710",
1178         .id_table       = adv_pci1710_pci_table,
1179         .probe          = adv_pci1710_pci_probe,
1180         .remove         = comedi_pci_auto_unconfig,
1181 };
1182 module_comedi_pci_driver(adv_pci1710_driver, adv_pci1710_pci_driver);
1183 
1184 MODULE_AUTHOR("Comedi http://www.comedi.org");
1185 MODULE_DESCRIPTION("Comedi: Advantech PCI-1710 Series Multifunction DAS Cards");
1186 MODULE_LICENSE("GPL");
1187 

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us