2 * comedi/drivers/adv_pci1710.c
4 * Author: Michal Dobes <dobes@tesnet.cz>
6 * Thanks to ZhenGang Shang <ZhenGang.Shang@Advantech.com.cn>
7 * for testing and informations.
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
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
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,
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.
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
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
44 #include <linux/pci.h>
45 #include <linux/interrupt.h>
47 #include "../comedidev.h"
49 #include "comedi_fc.h"
51 #include "amcc_s5933.h"
53 #define PCI171x_PARANOIDCHECK /* if defined, then is used code which control
54 * correct channel number on every 12 bit
57 /* hardware types of the cards */
58 #define TYPE_PCI171X 0
59 #define TYPE_PCI1713 2
60 #define TYPE_PCI1720 3
62 #define PCI171x_AD_DATA 0 /* R: A/D data */
63 #define PCI171x_SOFTTRG 0 /* W: soft trigger for A/D */
64 #define PCI171x_RANGE 2 /* W: A/D gain/range register */
65 #define PCI171x_MUX 4 /* W: A/D multiplexor control */
66 #define PCI171x_STATUS 6 /* R: status register */
67 #define PCI171x_CONTROL 6 /* W: control register */
68 #define PCI171x_CLRINT 8 /* W: clear interrupts request */
69 #define PCI171x_CLRFIFO 9 /* W: clear FIFO */
70 #define PCI171x_DA1 10 /* W: D/A register */
71 #define PCI171x_DA2 12 /* W: D/A register */
72 #define PCI171x_DAREF 14 /* W: D/A reference control */
73 #define PCI171x_DI 16 /* R: digi inputs */
74 #define PCI171x_DO 16 /* R: digi inputs */
75 #define PCI171x_CNT0 24 /* R/W: 8254 counter 0 */
76 #define PCI171x_CNT1 26 /* R/W: 8254 counter 1 */
77 #define PCI171x_CNT2 28 /* R/W: 8254 counter 2 */
78 #define PCI171x_CNTCTRL 30 /* W: 8254 counter control */
80 /* upper bits from status register (PCI171x_STATUS) (lower is same with control
82 #define Status_FE 0x0100 /* 1=FIFO is empty */
83 #define Status_FH 0x0200 /* 1=FIFO is half full */
84 #define Status_FF 0x0400 /* 1=FIFO is full, fatal error */
85 #define Status_IRQ 0x0800 /* 1=IRQ occurred */
86 /* bits from control register (PCI171x_CONTROL) */
87 #define Control_CNT0 0x0040 /* 1=CNT0 have external source,
88 * 0=have internal 100kHz source */
89 #define Control_ONEFH 0x0020 /* 1=IRQ on FIFO is half full, 0=every sample */
90 #define Control_IRQEN 0x0010 /* 1=enable IRQ */
91 #define Control_GATE 0x0008 /* 1=enable external trigger GATE (8254?) */
92 #define Control_EXT 0x0004 /* 1=external trigger source */
93 #define Control_PACER 0x0002 /* 1=enable internal 8254 trigger source */
94 #define Control_SW 0x0001 /* 1=enable software trigger source */
95 /* bits from counter control register (PCI171x_CNTCTRL) */
96 #define Counter_BCD 0x0001 /* 0 = binary counter, 1 = BCD counter */
97 #define Counter_M0 0x0002 /* M0-M2 select modes 0-5 */
98 #define Counter_M1 0x0004 /* 000 = mode 0, 010 = mode 2 ... */
99 #define Counter_M2 0x0008
100 #define Counter_RW0 0x0010 /* RW0/RW1 select read/write mode */
101 #define Counter_RW1 0x0020
102 #define Counter_SC0 0x0040 /* Select Counter. Only 00 or 11 may */
103 #define Counter_SC1 0x0080 /* be used, 00 for CNT0,
104 * 11 for read-back command */
106 #define PCI1720_DA0 0 /* W: D/A register 0 */
107 #define PCI1720_DA1 2 /* W: D/A register 1 */
108 #define PCI1720_DA2 4 /* W: D/A register 2 */
109 #define PCI1720_DA3 6 /* W: D/A register 3 */
110 #define PCI1720_RANGE 8 /* R/W: D/A range register */
111 #define PCI1720_SYNCOUT 9 /* W: D/A synchronized output register */
112 #define PCI1720_SYNCONT 15 /* R/W: D/A synchronized control */
114 /* D/A synchronized control (PCI1720_SYNCONT) */
115 #define Syncont_SC0 1 /* set synchronous output mode */
117 static const struct comedi_lrange range_pci1710_3 = { 9, {
130 static const char range_codes_pci1710_3[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
131 0x10, 0x11, 0x12, 0x13 };
133 static const struct comedi_lrange range_pci1710hg = { 12, {
149 static const char range_codes_pci1710hg[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
150 0x05, 0x06, 0x07, 0x10, 0x11,
153 static const struct comedi_lrange range_pci17x1 = { 5, {
162 static const char range_codes_pci17x1[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
164 static const struct comedi_lrange range_pci1720 = { 4, {
172 static const struct comedi_lrange range_pci171x_da = { 2, {
178 enum pci1710_boardid {
188 const char *name; /* board name */
189 char have_irq; /* 1=card support IRQ */
190 char cardtype; /* 0=1710& co. 2=1713, ... */
191 int n_aichan; /* num of A/D chans */
192 int n_aichand; /* num of A/D chans in diff mode */
193 int n_aochan; /* num of D/A chans */
194 int n_dichan; /* num of DI chans */
195 int n_dochan; /* num of DO chans */
196 int n_counter; /* num of counters */
197 int ai_maxdata; /* resolution of A/D */
198 int ao_maxdata; /* resolution of D/A */
199 const struct comedi_lrange *rangelist_ai; /* rangelist for A/D */
200 const char *rangecode_ai; /* range codes for programming */
201 const struct comedi_lrange *rangelist_ao; /* rangelist for D/A */
202 unsigned int ai_ns_min; /* max sample speed of card v ns */
203 unsigned int fifo_half_size; /* size of FIFO/2 */
206 static const struct boardtype boardtypes[] = {
210 .cardtype = TYPE_PCI171X,
217 .ai_maxdata = 0x0fff,
218 .ao_maxdata = 0x0fff,
219 .rangelist_ai = &range_pci1710_3,
220 .rangecode_ai = range_codes_pci1710_3,
221 .rangelist_ao = &range_pci171x_da,
223 .fifo_half_size = 2048,
225 [BOARD_PCI1710HG] = {
228 .cardtype = TYPE_PCI171X,
235 .ai_maxdata = 0x0fff,
236 .ao_maxdata = 0x0fff,
237 .rangelist_ai = &range_pci1710hg,
238 .rangecode_ai = range_codes_pci1710hg,
239 .rangelist_ao = &range_pci171x_da,
241 .fifo_half_size = 2048,
246 .cardtype = TYPE_PCI171X,
252 .ai_maxdata = 0x0fff,
253 .ao_maxdata = 0x0fff,
254 .rangelist_ai = &range_pci17x1,
255 .rangecode_ai = range_codes_pci17x1,
256 .rangelist_ao = &range_pci171x_da,
258 .fifo_half_size = 512,
263 .cardtype = TYPE_PCI1713,
266 .ai_maxdata = 0x0fff,
267 .rangelist_ai = &range_pci1710_3,
268 .rangecode_ai = range_codes_pci1710_3,
270 .fifo_half_size = 2048,
274 .cardtype = TYPE_PCI1720,
276 .ao_maxdata = 0x0fff,
277 .rangelist_ao = &range_pci1720,
282 .cardtype = TYPE_PCI171X,
286 .ai_maxdata = 0x0fff,
287 .rangelist_ai = &range_pci17x1,
288 .rangecode_ai = range_codes_pci17x1,
290 .fifo_half_size = 512,
294 struct pci1710_private {
295 char neverending_ai; /* we do unlimited AI */
296 unsigned int CntrlReg; /* Control register */
297 unsigned int i8254_osc_base; /* frequence of onboard oscilator */
298 unsigned int ai_do; /* what do AI? 0=nothing, 1 to 4 mode */
299 unsigned int ai_act_scan; /* how many scans we finished */
300 unsigned int ai_act_chan; /* actual position in actual scan */
301 unsigned int ai_buf_ptr; /* data buffer ptr in samples */
302 unsigned char ai_eos; /* 1=EOS wake up */
304 unsigned int ai_et_CntrlReg;
305 unsigned int ai_et_MuxVal;
306 unsigned int ai_et_div1, ai_et_div2;
307 unsigned int act_chanlist[32]; /* list of scanned channel */
308 unsigned char act_chanlist_len; /* len of scanlist */
309 unsigned char act_chanlist_pos; /* actual position in MUX list */
310 unsigned char da_ranges; /* copy of D/A outpit range register */
311 unsigned int ai_scans; /* len of scanlist */
312 unsigned int ai_n_chan; /* how many channels is measured */
313 unsigned int *ai_chanlist; /* actaul chanlist */
314 unsigned int ai_flags; /* flaglist */
315 unsigned int ai_data_len; /* len of data buffer */
316 short *ai_data; /* data buffer */
317 unsigned int ai_timer1; /* timers */
318 unsigned int ai_timer2;
319 short ao_data[4]; /* data output buffer */
320 unsigned int cnt0_write_wait; /* after a write, wait for update of the
324 /* used for gain list programming */
325 static const unsigned int muxonechan[] = {
326 0x0000, 0x0101, 0x0202, 0x0303, 0x0404, 0x0505, 0x0606, 0x0707,
327 0x0808, 0x0909, 0x0a0a, 0x0b0b, 0x0c0c, 0x0d0d, 0x0e0e, 0x0f0f,
328 0x1010, 0x1111, 0x1212, 0x1313, 0x1414, 0x1515, 0x1616, 0x1717,
329 0x1818, 0x1919, 0x1a1a, 0x1b1b, 0x1c1c, 0x1d1d, 0x1e1e, 0x1f1f
333 ==============================================================================
334 Check if channel list from user is built correctly
335 If it's ok, then program scan/gain logic.
336 This works for all cards.
338 static int check_channel_list(struct comedi_device *dev,
339 struct comedi_subdevice *s,
340 unsigned int *chanlist, unsigned int n_chan)
342 unsigned int chansegment[32];
343 unsigned int i, nowmustbechan, seglen, segpos;
345 /* correct channel and range number check itself comedi/range.c */
347 comedi_error(dev, "range/channel list is empty!");
352 return 1; /* seglen=1 */
354 chansegment[0] = chanlist[0]; /* first channel is every time ok */
355 for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {
356 if (chanlist[0] == chanlist[i])
357 break; /* we detected a loop, stop */
358 if ((CR_CHAN(chanlist[i]) & 1) &&
359 (CR_AREF(chanlist[i]) == AREF_DIFF)) {
360 comedi_error(dev, "Odd channel cannot be differential input!\n");
363 nowmustbechan = (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
364 if (CR_AREF(chansegment[i - 1]) == AREF_DIFF)
365 nowmustbechan = (nowmustbechan + 1) % s->n_chan;
366 if (nowmustbechan != CR_CHAN(chanlist[i])) {
367 printk("channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
368 i, CR_CHAN(chanlist[i]), nowmustbechan,
369 CR_CHAN(chanlist[0]));
372 chansegment[i] = chanlist[i]; /* next correct channel in list */
375 for (i = 0, segpos = 0; i < n_chan; i++) {
376 if (chanlist[i] != chansegment[i % seglen]) {
377 printk("bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
378 i, CR_CHAN(chansegment[i]),
379 CR_RANGE(chansegment[i]),
380 CR_AREF(chansegment[i]),
381 CR_CHAN(chanlist[i % seglen]),
382 CR_RANGE(chanlist[i % seglen]),
383 CR_AREF(chansegment[i % seglen]));
390 static void setup_channel_list(struct comedi_device *dev,
391 struct comedi_subdevice *s,
392 unsigned int *chanlist, unsigned int n_chan,
395 const struct boardtype *this_board = comedi_board(dev);
396 struct pci1710_private *devpriv = dev->private;
397 unsigned int i, range, chanprog;
399 devpriv->act_chanlist_len = seglen;
400 devpriv->act_chanlist_pos = 0;
402 for (i = 0; i < seglen; i++) { /* store range list to card */
403 chanprog = muxonechan[CR_CHAN(chanlist[i])];
404 outw(chanprog, dev->iobase + PCI171x_MUX); /* select channel */
405 range = this_board->rangecode_ai[CR_RANGE(chanlist[i])];
406 if (CR_AREF(chanlist[i]) == AREF_DIFF)
408 outw(range, dev->iobase + PCI171x_RANGE); /* select gain */
409 #ifdef PCI171x_PARANOIDCHECK
410 devpriv->act_chanlist[i] =
411 (CR_CHAN(chanlist[i]) << 12) & 0xf000;
414 #ifdef PCI171x_PARANOIDCHECK
415 for ( ; i < n_chan; i++) { /* store remainder of channel list */
416 devpriv->act_chanlist[i] =
417 (CR_CHAN(chanlist[i]) << 12) & 0xf000;
421 devpriv->ai_et_MuxVal =
422 CR_CHAN(chanlist[0]) | (CR_CHAN(chanlist[seglen - 1]) << 8);
423 /* select channel interval to scan */
424 outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
428 ==============================================================================
430 static int pci171x_insn_read_ai(struct comedi_device *dev,
431 struct comedi_subdevice *s,
432 struct comedi_insn *insn, unsigned int *data)
434 struct pci1710_private *devpriv = dev->private;
436 #ifdef PCI171x_PARANOIDCHECK
437 const struct boardtype *this_board = comedi_board(dev);
441 devpriv->CntrlReg &= Control_CNT0;
442 devpriv->CntrlReg |= Control_SW; /* set software trigger */
443 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
444 outb(0, dev->iobase + PCI171x_CLRFIFO);
445 outb(0, dev->iobase + PCI171x_CLRINT);
447 setup_channel_list(dev, s, &insn->chanspec, 1, 1);
449 for (n = 0; n < insn->n; n++) {
450 outw(0, dev->iobase + PCI171x_SOFTTRG); /* start conversion */
454 if (!(inw(dev->iobase + PCI171x_STATUS) & Status_FE))
457 comedi_error(dev, "A/D insn timeout");
458 outb(0, dev->iobase + PCI171x_CLRFIFO);
459 outb(0, dev->iobase + PCI171x_CLRINT);
464 #ifdef PCI171x_PARANOIDCHECK
465 idata = inw(dev->iobase + PCI171x_AD_DATA);
466 if (this_board->cardtype != TYPE_PCI1713)
467 if ((idata & 0xf000) != devpriv->act_chanlist[0]) {
468 comedi_error(dev, "A/D insn data droput!");
471 data[n] = idata & 0x0fff;
473 data[n] = inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff;
478 outb(0, dev->iobase + PCI171x_CLRFIFO);
479 outb(0, dev->iobase + PCI171x_CLRINT);
485 ==============================================================================
487 static int pci171x_insn_write_ao(struct comedi_device *dev,
488 struct comedi_subdevice *s,
489 struct comedi_insn *insn, unsigned int *data)
491 struct pci1710_private *devpriv = dev->private;
493 int n, chan, range, ofs;
495 chan = CR_CHAN(insn->chanspec);
496 range = CR_RANGE(insn->chanspec);
498 devpriv->da_ranges &= 0xfb;
499 devpriv->da_ranges |= (range << 2);
500 outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
503 devpriv->da_ranges &= 0xfe;
504 devpriv->da_ranges |= range;
505 outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
508 val = devpriv->ao_data[chan];
510 for (n = 0; n < insn->n; n++) {
512 outw(val, dev->iobase + ofs);
515 devpriv->ao_data[chan] = val;
522 ==============================================================================
524 static int pci171x_insn_read_ao(struct comedi_device *dev,
525 struct comedi_subdevice *s,
526 struct comedi_insn *insn, unsigned int *data)
528 struct pci1710_private *devpriv = dev->private;
531 chan = CR_CHAN(insn->chanspec);
532 for (n = 0; n < insn->n; n++)
533 data[n] = devpriv->ao_data[chan];
539 ==============================================================================
541 static int pci171x_insn_bits_di(struct comedi_device *dev,
542 struct comedi_subdevice *s,
543 struct comedi_insn *insn, unsigned int *data)
545 data[1] = inw(dev->iobase + PCI171x_DI);
551 ==============================================================================
553 static int pci171x_insn_bits_do(struct comedi_device *dev,
554 struct comedi_subdevice *s,
555 struct comedi_insn *insn, unsigned int *data)
558 s->state &= ~data[0];
559 s->state |= (data[0] & data[1]);
560 outw(s->state, dev->iobase + PCI171x_DO);
568 ==============================================================================
570 static void start_pacer(struct comedi_device *dev, int mode,
571 unsigned int divisor1, unsigned int divisor2)
573 outw(0xb4, dev->iobase + PCI171x_CNTCTRL);
574 outw(0x74, dev->iobase + PCI171x_CNTCTRL);
577 outw(divisor2 & 0xff, dev->iobase + PCI171x_CNT2);
578 outw((divisor2 >> 8) & 0xff, dev->iobase + PCI171x_CNT2);
579 outw(divisor1 & 0xff, dev->iobase + PCI171x_CNT1);
580 outw((divisor1 >> 8) & 0xff, dev->iobase + PCI171x_CNT1);
585 ==============================================================================
587 static int pci171x_insn_counter_read(struct comedi_device *dev,
588 struct comedi_subdevice *s,
589 struct comedi_insn *insn,
592 unsigned int msb, lsb, ccntrl;
595 ccntrl = 0xD2; /* count only */
596 for (i = 0; i < insn->n; i++) {
597 outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
599 lsb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
600 msb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
602 data[0] = lsb | (msb << 8);
609 ==============================================================================
611 static int pci171x_insn_counter_write(struct comedi_device *dev,
612 struct comedi_subdevice *s,
613 struct comedi_insn *insn,
616 struct pci1710_private *devpriv = dev->private;
617 uint msb, lsb, ccntrl, status;
619 lsb = data[0] & 0x00FF;
620 msb = (data[0] & 0xFF00) >> 8;
622 /* write lsb, then msb */
623 outw(lsb, dev->iobase + PCI171x_CNT0);
624 outw(msb, dev->iobase + PCI171x_CNT0);
626 if (devpriv->cnt0_write_wait) {
627 /* wait for the new count to be loaded */
630 outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
631 status = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
632 } while (status & 0x40);
639 ==============================================================================
641 static int pci171x_insn_counter_config(struct comedi_device *dev,
642 struct comedi_subdevice *s,
643 struct comedi_insn *insn,
647 /* This doesn't work like a normal Comedi counter config */
648 struct pci1710_private *devpriv = dev->private;
651 devpriv->cnt0_write_wait = data[0] & 0x20;
653 /* internal or external clock? */
654 if (!(data[0] & 0x10)) { /* internal */
655 devpriv->CntrlReg &= ~Control_CNT0;
657 devpriv->CntrlReg |= Control_CNT0;
659 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
662 ccntrl |= Counter_M0;
664 ccntrl |= Counter_M1;
666 ccntrl |= Counter_M2;
668 ccntrl |= Counter_BCD;
669 ccntrl |= Counter_RW0; /* set read/write mode */
670 ccntrl |= Counter_RW1;
671 outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
678 ==============================================================================
680 static int pci1720_insn_write_ao(struct comedi_device *dev,
681 struct comedi_subdevice *s,
682 struct comedi_insn *insn, unsigned int *data)
684 struct pci1710_private *devpriv = dev->private;
686 int n, rangereg, chan;
688 chan = CR_CHAN(insn->chanspec);
689 rangereg = devpriv->da_ranges & (~(0x03 << (chan << 1)));
690 rangereg |= (CR_RANGE(insn->chanspec) << (chan << 1));
691 if (rangereg != devpriv->da_ranges) {
692 outb(rangereg, dev->iobase + PCI1720_RANGE);
693 devpriv->da_ranges = rangereg;
695 val = devpriv->ao_data[chan];
697 for (n = 0; n < insn->n; n++) {
699 outw(val, dev->iobase + PCI1720_DA0 + (chan << 1));
700 outb(0, dev->iobase + PCI1720_SYNCOUT); /* update outputs */
703 devpriv->ao_data[chan] = val;
709 ==============================================================================
711 static int pci171x_ai_cancel(struct comedi_device *dev,
712 struct comedi_subdevice *s)
714 const struct boardtype *this_board = comedi_board(dev);
715 struct pci1710_private *devpriv = dev->private;
717 switch (this_board->cardtype) {
719 devpriv->CntrlReg &= Control_CNT0;
720 devpriv->CntrlReg |= Control_SW;
722 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL); /* reset any operations */
723 start_pacer(dev, -1, 0, 0);
724 outb(0, dev->iobase + PCI171x_CLRFIFO);
725 outb(0, dev->iobase + PCI171x_CLRINT);
730 devpriv->ai_act_scan = 0;
731 s->async->cur_chan = 0;
732 devpriv->ai_buf_ptr = 0;
733 devpriv->neverending_ai = 0;
739 ==============================================================================
741 static void interrupt_pci1710_every_sample(void *d)
743 struct comedi_device *dev = d;
744 struct pci1710_private *devpriv = dev->private;
745 struct comedi_subdevice *s = &dev->subdevices[0];
747 #ifdef PCI171x_PARANOIDCHECK
748 const struct boardtype *this_board = comedi_board(dev);
752 m = inw(dev->iobase + PCI171x_STATUS);
754 printk("comedi%d: A/D FIFO empty (%4x)\n", dev->minor, m);
755 pci171x_ai_cancel(dev, s);
756 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
757 comedi_event(dev, s);
762 ("comedi%d: A/D FIFO Full status (Fatal Error!) (%4x)\n",
764 pci171x_ai_cancel(dev, s);
765 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
766 comedi_event(dev, s);
770 outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
772 for (; !(inw(dev->iobase + PCI171x_STATUS) & Status_FE);) {
773 #ifdef PCI171x_PARANOIDCHECK
774 sampl = inw(dev->iobase + PCI171x_AD_DATA);
775 if (this_board->cardtype != TYPE_PCI1713)
776 if ((sampl & 0xf000) !=
777 devpriv->act_chanlist[s->async->cur_chan]) {
779 ("comedi: A/D data dropout: received data from channel %d, expected %d!\n",
780 (sampl & 0xf000) >> 12,
783 async->cur_chan] & 0xf000) >>
785 pci171x_ai_cancel(dev, s);
787 COMEDI_CB_EOA | COMEDI_CB_ERROR;
788 comedi_event(dev, s);
791 comedi_buf_put(s->async, sampl & 0x0fff);
793 comedi_buf_put(s->async,
794 inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff);
796 ++s->async->cur_chan;
798 if (s->async->cur_chan >= devpriv->ai_n_chan)
799 s->async->cur_chan = 0;
802 if (s->async->cur_chan == 0) { /* one scan done */
803 devpriv->ai_act_scan++;
804 if ((!devpriv->neverending_ai) &&
805 (devpriv->ai_act_scan >= devpriv->ai_scans)) {
806 /* all data sampled */
807 pci171x_ai_cancel(dev, s);
808 s->async->events |= COMEDI_CB_EOA;
809 comedi_event(dev, s);
815 outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
817 comedi_event(dev, s);
821 ==============================================================================
823 static int move_block_from_fifo(struct comedi_device *dev,
824 struct comedi_subdevice *s, int n, int turn)
826 struct pci1710_private *devpriv = dev->private;
828 #ifdef PCI171x_PARANOIDCHECK
829 const struct boardtype *this_board = comedi_board(dev);
833 j = s->async->cur_chan;
834 for (i = 0; i < n; i++) {
835 #ifdef PCI171x_PARANOIDCHECK
836 sampl = inw(dev->iobase + PCI171x_AD_DATA);
837 if (this_board->cardtype != TYPE_PCI1713)
838 if ((sampl & 0xf000) != devpriv->act_chanlist[j]) {
840 ("comedi%d: A/D FIFO data dropout: received data from channel %d, expected %d! (%d/%d/%d/%d/%d/%4x)\n",
841 dev->minor, (sampl & 0xf000) >> 12,
842 (devpriv->act_chanlist[j] & 0xf000) >> 12,
843 i, j, devpriv->ai_act_scan, n, turn,
845 pci171x_ai_cancel(dev, s);
847 COMEDI_CB_EOA | COMEDI_CB_ERROR;
848 comedi_event(dev, s);
851 comedi_buf_put(s->async, sampl & 0x0fff);
853 comedi_buf_put(s->async,
854 inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff);
857 if (j >= devpriv->ai_n_chan) {
859 devpriv->ai_act_scan++;
862 s->async->cur_chan = j;
867 ==============================================================================
869 static void interrupt_pci1710_half_fifo(void *d)
871 struct comedi_device *dev = d;
872 const struct boardtype *this_board = comedi_board(dev);
873 struct pci1710_private *devpriv = dev->private;
874 struct comedi_subdevice *s = &dev->subdevices[0];
877 m = inw(dev->iobase + PCI171x_STATUS);
878 if (!(m & Status_FH)) {
879 printk("comedi%d: A/D FIFO not half full! (%4x)\n",
881 pci171x_ai_cancel(dev, s);
882 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
883 comedi_event(dev, s);
888 ("comedi%d: A/D FIFO Full status (Fatal Error!) (%4x)\n",
890 pci171x_ai_cancel(dev, s);
891 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
892 comedi_event(dev, s);
896 samplesinbuf = this_board->fifo_half_size;
897 if (samplesinbuf * sizeof(short) >= devpriv->ai_data_len) {
898 m = devpriv->ai_data_len / sizeof(short);
899 if (move_block_from_fifo(dev, s, m, 0))
905 if (move_block_from_fifo(dev, s, samplesinbuf, 1))
909 if (!devpriv->neverending_ai)
910 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data
912 pci171x_ai_cancel(dev, s);
913 s->async->events |= COMEDI_CB_EOA;
914 comedi_event(dev, s);
917 outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
919 comedi_event(dev, s);
923 ==============================================================================
925 static irqreturn_t interrupt_service_pci1710(int irq, void *d)
927 struct comedi_device *dev = d;
928 struct pci1710_private *devpriv = dev->private;
930 if (!dev->attached) /* is device attached? */
931 return IRQ_NONE; /* no, exit */
932 /* is this interrupt from our board? */
933 if (!(inw(dev->iobase + PCI171x_STATUS) & Status_IRQ))
934 return IRQ_NONE; /* no, exit */
936 if (devpriv->ai_et) { /* Switch from initial TRIG_EXT to TRIG_xxx. */
938 devpriv->CntrlReg &= Control_CNT0;
939 devpriv->CntrlReg |= Control_SW; /* set software trigger */
940 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
941 devpriv->CntrlReg = devpriv->ai_et_CntrlReg;
942 outb(0, dev->iobase + PCI171x_CLRFIFO);
943 outb(0, dev->iobase + PCI171x_CLRINT);
944 outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
945 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
947 start_pacer(dev, 1, devpriv->ai_et_div1, devpriv->ai_et_div2);
950 if (devpriv->ai_eos) { /* We use FIFO half full INT or not? */
951 interrupt_pci1710_every_sample(d);
953 interrupt_pci1710_half_fifo(d);
959 ==============================================================================
961 static int pci171x_ai_docmd_and_mode(int mode, struct comedi_device *dev,
962 struct comedi_subdevice *s)
964 const struct boardtype *this_board = comedi_board(dev);
965 struct pci1710_private *devpriv = dev->private;
966 unsigned int divisor1 = 0, divisor2 = 0;
969 start_pacer(dev, -1, 0, 0); /* stop pacer */
971 seglen = check_channel_list(dev, s, devpriv->ai_chanlist,
975 setup_channel_list(dev, s, devpriv->ai_chanlist,
976 devpriv->ai_n_chan, seglen);
978 outb(0, dev->iobase + PCI171x_CLRFIFO);
979 outb(0, dev->iobase + PCI171x_CLRINT);
981 devpriv->ai_do = mode;
983 devpriv->ai_act_scan = 0;
984 s->async->cur_chan = 0;
985 devpriv->ai_buf_ptr = 0;
986 devpriv->neverending_ai = 0;
988 devpriv->CntrlReg &= Control_CNT0;
989 /* don't we want wake up every scan? devpriv->ai_eos=1; */
990 if ((devpriv->ai_flags & TRIG_WAKE_EOS)) {
993 devpriv->CntrlReg |= Control_ONEFH;
997 if ((devpriv->ai_scans == 0) || (devpriv->ai_scans == -1))
998 devpriv->neverending_ai = 1;
999 /* well, user want neverending */
1001 devpriv->neverending_ai = 0;
1006 if (devpriv->ai_timer1 < this_board->ai_ns_min)
1007 devpriv->ai_timer1 = this_board->ai_ns_min;
1008 devpriv->CntrlReg |= Control_PACER | Control_IRQEN;
1010 devpriv->ai_et_CntrlReg = devpriv->CntrlReg;
1011 devpriv->CntrlReg &=
1012 ~(Control_PACER | Control_ONEFH | Control_GATE);
1013 devpriv->CntrlReg |= Control_EXT;
1018 i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
1019 &divisor2, &devpriv->ai_timer1,
1020 devpriv->ai_flags & TRIG_ROUND_MASK);
1021 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
1024 start_pacer(dev, mode, divisor1, divisor2);
1026 devpriv->ai_et_div1 = divisor1;
1027 devpriv->ai_et_div2 = divisor2;
1031 devpriv->CntrlReg |= Control_EXT | Control_IRQEN;
1032 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
1040 ==============================================================================
1042 static int pci171x_ai_cmdtest(struct comedi_device *dev,
1043 struct comedi_subdevice *s,
1044 struct comedi_cmd *cmd)
1046 const struct boardtype *this_board = comedi_board(dev);
1047 struct pci1710_private *devpriv = dev->private;
1050 unsigned int divisor1 = 0, divisor2 = 0;
1052 /* Step 1 : check if triggers are trivially valid */
1054 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
1055 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
1056 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
1057 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
1058 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
1063 /* step 2a: make sure trigger sources are unique */
1065 err |= cfc_check_trigger_is_unique(cmd->start_src);
1066 err |= cfc_check_trigger_is_unique(cmd->convert_src);
1067 err |= cfc_check_trigger_is_unique(cmd->stop_src);
1069 /* step 2b: and mutually compatible */
1074 /* Step 3: check if arguments are trivially valid */
1076 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
1077 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
1079 if (cmd->convert_src == TRIG_TIMER)
1080 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
1081 this_board->ai_ns_min);
1082 else /* TRIG_FOLLOW */
1083 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
1085 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
1087 if (cmd->stop_src == TRIG_COUNT)
1088 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
1089 else /* TRIG_NONE */
1090 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
1095 /* step 4: fix up any arguments */
1097 if (cmd->convert_src == TRIG_TIMER) {
1098 tmp = cmd->convert_arg;
1099 i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
1100 &divisor2, &cmd->convert_arg,
1101 cmd->flags & TRIG_ROUND_MASK);
1102 if (cmd->convert_arg < this_board->ai_ns_min)
1103 cmd->convert_arg = this_board->ai_ns_min;
1104 if (tmp != cmd->convert_arg)
1111 /* step 5: complain about special chanlist considerations */
1113 if (cmd->chanlist) {
1114 if (!check_channel_list(dev, s, cmd->chanlist,
1116 return 5; /* incorrect channels list */
1123 ==============================================================================
1125 static int pci171x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
1127 struct pci1710_private *devpriv = dev->private;
1128 struct comedi_cmd *cmd = &s->async->cmd;
1130 devpriv->ai_n_chan = cmd->chanlist_len;
1131 devpriv->ai_chanlist = cmd->chanlist;
1132 devpriv->ai_flags = cmd->flags;
1133 devpriv->ai_data_len = s->async->prealloc_bufsz;
1134 devpriv->ai_data = s->async->prealloc_buf;
1135 devpriv->ai_timer1 = 0;
1136 devpriv->ai_timer2 = 0;
1138 if (cmd->stop_src == TRIG_COUNT)
1139 devpriv->ai_scans = cmd->stop_arg;
1141 devpriv->ai_scans = 0;
1144 if (cmd->scan_begin_src == TRIG_FOLLOW) { /* mode 1, 2, 3 */
1145 if (cmd->convert_src == TRIG_TIMER) { /* mode 1 and 2 */
1146 devpriv->ai_timer1 = cmd->convert_arg;
1147 return pci171x_ai_docmd_and_mode(cmd->start_src ==
1148 TRIG_EXT ? 2 : 1, dev,
1151 if (cmd->convert_src == TRIG_EXT) { /* mode 3 */
1152 return pci171x_ai_docmd_and_mode(3, dev, s);
1160 ==============================================================================
1162 static int pci171x_reset(struct comedi_device *dev)
1164 const struct boardtype *this_board = comedi_board(dev);
1165 struct pci1710_private *devpriv = dev->private;
1167 outw(0x30, dev->iobase + PCI171x_CNTCTRL);
1168 devpriv->CntrlReg = Control_SW | Control_CNT0; /* Software trigger, CNT0=external */
1169 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL); /* reset any operations */
1170 outb(0, dev->iobase + PCI171x_CLRFIFO); /* clear FIFO */
1171 outb(0, dev->iobase + PCI171x_CLRINT); /* clear INT request */
1172 start_pacer(dev, -1, 0, 0); /* stop 8254 */
1173 devpriv->da_ranges = 0;
1174 if (this_board->n_aochan) {
1175 outb(devpriv->da_ranges, dev->iobase + PCI171x_DAREF); /* set DACs to 0..5V */
1176 outw(0, dev->iobase + PCI171x_DA1); /* set DA outputs to 0V */
1177 devpriv->ao_data[0] = 0x0000;
1178 if (this_board->n_aochan > 1) {
1179 outw(0, dev->iobase + PCI171x_DA2);
1180 devpriv->ao_data[1] = 0x0000;
1183 outw(0, dev->iobase + PCI171x_DO); /* digital outputs to 0 */
1184 outb(0, dev->iobase + PCI171x_CLRFIFO); /* clear FIFO */
1185 outb(0, dev->iobase + PCI171x_CLRINT); /* clear INT request */
1191 ==============================================================================
1193 static int pci1720_reset(struct comedi_device *dev)
1195 struct pci1710_private *devpriv = dev->private;
1197 outb(Syncont_SC0, dev->iobase + PCI1720_SYNCONT); /* set synchronous output mode */
1198 devpriv->da_ranges = 0xAA;
1199 outb(devpriv->da_ranges, dev->iobase + PCI1720_RANGE); /* set all ranges to +/-5V */
1200 outw(0x0800, dev->iobase + PCI1720_DA0); /* set outputs to 0V */
1201 outw(0x0800, dev->iobase + PCI1720_DA1);
1202 outw(0x0800, dev->iobase + PCI1720_DA2);
1203 outw(0x0800, dev->iobase + PCI1720_DA3);
1204 outb(0, dev->iobase + PCI1720_SYNCOUT); /* update outputs */
1205 devpriv->ao_data[0] = 0x0800;
1206 devpriv->ao_data[1] = 0x0800;
1207 devpriv->ao_data[2] = 0x0800;
1208 devpriv->ao_data[3] = 0x0800;
1213 ==============================================================================
1215 static int pci1710_reset(struct comedi_device *dev)
1217 const struct boardtype *this_board = comedi_board(dev);
1219 switch (this_board->cardtype) {
1221 return pci1720_reset(dev);
1223 return pci171x_reset(dev);
1227 static int pci1710_auto_attach(struct comedi_device *dev,
1228 unsigned long context)
1230 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
1231 const struct boardtype *this_board = NULL;
1232 struct pci1710_private *devpriv;
1233 struct comedi_subdevice *s;
1234 int ret, subdev, n_subdevices;
1236 if (context < ARRAY_SIZE(boardtypes))
1237 this_board = &boardtypes[context];
1240 dev->board_ptr = this_board;
1241 dev->board_name = this_board->name;
1243 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
1246 dev->private = devpriv;
1248 ret = comedi_pci_enable(dev);
1251 dev->iobase = pci_resource_start(pcidev, 2);
1254 if (this_board->n_aichan)
1256 if (this_board->n_aochan)
1258 if (this_board->n_dichan)
1260 if (this_board->n_dochan)
1262 if (this_board->n_counter)
1265 ret = comedi_alloc_subdevices(dev, n_subdevices);
1271 if (this_board->have_irq && pcidev->irq) {
1272 ret = request_irq(pcidev->irq, interrupt_service_pci1710,
1273 IRQF_SHARED, dev->board_name, dev);
1275 dev->irq = pcidev->irq;
1280 if (this_board->n_aichan) {
1281 s = &dev->subdevices[subdev];
1282 dev->read_subdev = s;
1283 s->type = COMEDI_SUBD_AI;
1284 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
1285 if (this_board->n_aichand)
1286 s->subdev_flags |= SDF_DIFF;
1287 s->n_chan = this_board->n_aichan;
1288 s->maxdata = this_board->ai_maxdata;
1289 s->len_chanlist = this_board->n_aichan;
1290 s->range_table = this_board->rangelist_ai;
1291 s->cancel = pci171x_ai_cancel;
1292 s->insn_read = pci171x_insn_read_ai;
1294 s->subdev_flags |= SDF_CMD_READ;
1295 s->do_cmdtest = pci171x_ai_cmdtest;
1296 s->do_cmd = pci171x_ai_cmd;
1298 devpriv->i8254_osc_base = 100; /* 100ns=10MHz */
1302 if (this_board->n_aochan) {
1303 s = &dev->subdevices[subdev];
1304 s->type = COMEDI_SUBD_AO;
1305 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1306 s->n_chan = this_board->n_aochan;
1307 s->maxdata = this_board->ao_maxdata;
1308 s->len_chanlist = this_board->n_aochan;
1309 s->range_table = this_board->rangelist_ao;
1310 switch (this_board->cardtype) {
1312 s->insn_write = pci1720_insn_write_ao;
1315 s->insn_write = pci171x_insn_write_ao;
1318 s->insn_read = pci171x_insn_read_ao;
1322 if (this_board->n_dichan) {
1323 s = &dev->subdevices[subdev];
1324 s->type = COMEDI_SUBD_DI;
1325 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
1326 s->n_chan = this_board->n_dichan;
1328 s->len_chanlist = this_board->n_dichan;
1329 s->range_table = &range_digital;
1330 s->io_bits = 0; /* all bits input */
1331 s->insn_bits = pci171x_insn_bits_di;
1335 if (this_board->n_dochan) {
1336 s = &dev->subdevices[subdev];
1337 s->type = COMEDI_SUBD_DO;
1338 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1339 s->n_chan = this_board->n_dochan;
1341 s->len_chanlist = this_board->n_dochan;
1342 s->range_table = &range_digital;
1343 /* all bits output */
1344 s->io_bits = (1 << this_board->n_dochan) - 1;
1346 s->insn_bits = pci171x_insn_bits_do;
1350 if (this_board->n_counter) {
1351 s = &dev->subdevices[subdev];
1352 s->type = COMEDI_SUBD_COUNTER;
1353 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
1354 s->n_chan = this_board->n_counter;
1355 s->len_chanlist = this_board->n_counter;
1356 s->maxdata = 0xffff;
1357 s->range_table = &range_unknown;
1358 s->insn_read = pci171x_insn_counter_read;
1359 s->insn_write = pci171x_insn_counter_write;
1360 s->insn_config = pci171x_insn_counter_config;
1364 dev_info(dev->class_dev, "%s attached, irq %sabled\n",
1365 dev->board_name, dev->irq ? "en" : "dis");
1370 static void pci1710_detach(struct comedi_device *dev)
1375 free_irq(dev->irq, dev);
1376 comedi_pci_disable(dev);
1379 static struct comedi_driver adv_pci1710_driver = {
1380 .driver_name = "adv_pci1710",
1381 .module = THIS_MODULE,
1382 .auto_attach = pci1710_auto_attach,
1383 .detach = pci1710_detach,
1386 static int adv_pci1710_pci_probe(struct pci_dev *dev,
1387 const struct pci_device_id *id)
1389 return comedi_pci_auto_config(dev, &adv_pci1710_driver,
1393 static DEFINE_PCI_DEVICE_TABLE(adv_pci1710_pci_table) = {
1395 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1396 PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050),
1397 .driver_data = BOARD_PCI1710,
1399 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1400 PCI_VENDOR_ID_ADVANTECH, 0x0000),
1401 .driver_data = BOARD_PCI1710,
1403 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1404 PCI_VENDOR_ID_ADVANTECH, 0xb100),
1405 .driver_data = BOARD_PCI1710,
1407 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1408 PCI_VENDOR_ID_ADVANTECH, 0xb200),
1409 .driver_data = BOARD_PCI1710,
1411 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1412 PCI_VENDOR_ID_ADVANTECH, 0xc100),
1413 .driver_data = BOARD_PCI1710,
1415 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1416 PCI_VENDOR_ID_ADVANTECH, 0xc200),
1417 .driver_data = BOARD_PCI1710,
1419 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd100),
1420 .driver_data = BOARD_PCI1710,
1422 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1423 PCI_VENDOR_ID_ADVANTECH, 0x0002),
1424 .driver_data = BOARD_PCI1710HG,
1426 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1427 PCI_VENDOR_ID_ADVANTECH, 0xb102),
1428 .driver_data = BOARD_PCI1710HG,
1430 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1431 PCI_VENDOR_ID_ADVANTECH, 0xb202),
1432 .driver_data = BOARD_PCI1710HG,
1434 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1435 PCI_VENDOR_ID_ADVANTECH, 0xc102),
1436 .driver_data = BOARD_PCI1710HG,
1438 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1439 PCI_VENDOR_ID_ADVANTECH, 0xc202),
1440 .driver_data = BOARD_PCI1710HG,
1442 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd102),
1443 .driver_data = BOARD_PCI1710HG,
1445 { PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 },
1446 { PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 },
1447 { PCI_VDEVICE(ADVANTECH, 0x1720), BOARD_PCI1720 },
1448 { PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 },
1451 MODULE_DEVICE_TABLE(pci, adv_pci1710_pci_table);
1453 static struct pci_driver adv_pci1710_pci_driver = {
1454 .name = "adv_pci1710",
1455 .id_table = adv_pci1710_pci_table,
1456 .probe = adv_pci1710_pci_probe,
1457 .remove = comedi_pci_auto_unconfig,
1459 module_comedi_pci_driver(adv_pci1710_driver, adv_pci1710_pci_driver);
1461 MODULE_AUTHOR("Comedi http://www.comedi.org");
1462 MODULE_DESCRIPTION("Comedi low-level driver");
1463 MODULE_LICENSE("GPL");