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/module.h>
45 #include <linux/pci.h>
46 #include <linux/interrupt.h>
48 #include "../comedidev.h"
50 #include "comedi_fc.h"
52 #include "amcc_s5933.h"
54 #define PCI171x_PARANOIDCHECK /* if defined, then is used code which control
55 * correct channel number on every 12 bit
58 /* hardware types of the cards */
59 #define TYPE_PCI171X 0
60 #define TYPE_PCI1713 2
61 #define TYPE_PCI1720 3
63 #define PCI171x_AD_DATA 0 /* R: A/D data */
64 #define PCI171x_SOFTTRG 0 /* W: soft trigger for A/D */
65 #define PCI171x_RANGE 2 /* W: A/D gain/range register */
66 #define PCI171x_MUX 4 /* W: A/D multiplexor control */
67 #define PCI171x_STATUS 6 /* R: status register */
68 #define PCI171x_CONTROL 6 /* W: control register */
69 #define PCI171x_CLRINT 8 /* W: clear interrupts request */
70 #define PCI171x_CLRFIFO 9 /* W: clear FIFO */
71 #define PCI171x_DA1 10 /* W: D/A register */
72 #define PCI171x_DA2 12 /* W: D/A register */
73 #define PCI171x_DAREF 14 /* W: D/A reference control */
74 #define PCI171x_DI 16 /* R: digi inputs */
75 #define PCI171x_DO 16 /* R: digi inputs */
76 #define PCI171x_CNT0 24 /* R/W: 8254 counter 0 */
77 #define PCI171x_CNT1 26 /* R/W: 8254 counter 1 */
78 #define PCI171x_CNT2 28 /* R/W: 8254 counter 2 */
79 #define PCI171x_CNTCTRL 30 /* W: 8254 counter control */
81 /* upper bits from status register (PCI171x_STATUS) (lower is same with control
83 #define Status_FE 0x0100 /* 1=FIFO is empty */
84 #define Status_FH 0x0200 /* 1=FIFO is half full */
85 #define Status_FF 0x0400 /* 1=FIFO is full, fatal error */
86 #define Status_IRQ 0x0800 /* 1=IRQ occurred */
87 /* bits from control register (PCI171x_CONTROL) */
88 #define Control_CNT0 0x0040 /* 1=CNT0 have external source,
89 * 0=have internal 100kHz source */
90 #define Control_ONEFH 0x0020 /* 1=IRQ on FIFO is half full, 0=every sample */
91 #define Control_IRQEN 0x0010 /* 1=enable IRQ */
92 #define Control_GATE 0x0008 /* 1=enable external trigger GATE (8254?) */
93 #define Control_EXT 0x0004 /* 1=external trigger source */
94 #define Control_PACER 0x0002 /* 1=enable internal 8254 trigger source */
95 #define Control_SW 0x0001 /* 1=enable software trigger source */
96 /* bits from counter control register (PCI171x_CNTCTRL) */
97 #define Counter_BCD 0x0001 /* 0 = binary counter, 1 = BCD counter */
98 #define Counter_M0 0x0002 /* M0-M2 select modes 0-5 */
99 #define Counter_M1 0x0004 /* 000 = mode 0, 010 = mode 2 ... */
100 #define Counter_M2 0x0008
101 #define Counter_RW0 0x0010 /* RW0/RW1 select read/write mode */
102 #define Counter_RW1 0x0020
103 #define Counter_SC0 0x0040 /* Select Counter. Only 00 or 11 may */
104 #define Counter_SC1 0x0080 /* be used, 00 for CNT0,
105 * 11 for read-back command */
107 #define PCI1720_DA0 0 /* W: D/A register 0 */
108 #define PCI1720_DA1 2 /* W: D/A register 1 */
109 #define PCI1720_DA2 4 /* W: D/A register 2 */
110 #define PCI1720_DA3 6 /* W: D/A register 3 */
111 #define PCI1720_RANGE 8 /* R/W: D/A range register */
112 #define PCI1720_SYNCOUT 9 /* W: D/A synchronized output register */
113 #define PCI1720_SYNCONT 15 /* R/W: D/A synchronized control */
115 /* D/A synchronized control (PCI1720_SYNCONT) */
116 #define Syncont_SC0 1 /* set synchronous output mode */
118 static const struct comedi_lrange range_pci1710_3 = { 9, {
131 static const char range_codes_pci1710_3[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
132 0x10, 0x11, 0x12, 0x13 };
134 static const struct comedi_lrange range_pci1710hg = { 12, {
150 static const char range_codes_pci1710hg[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
151 0x05, 0x06, 0x07, 0x10, 0x11,
154 static const struct comedi_lrange range_pci17x1 = { 5, {
163 static const char range_codes_pci17x1[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
165 static const struct comedi_lrange range_pci1720 = { 4, {
173 static const struct comedi_lrange range_pci171x_da = { 2, {
179 enum pci1710_boardid {
189 const char *name; /* board name */
190 char have_irq; /* 1=card support IRQ */
191 char cardtype; /* 0=1710& co. 2=1713, ... */
192 int n_aichan; /* num of A/D chans */
193 int n_aichand; /* num of A/D chans in diff mode */
194 int n_aochan; /* num of D/A chans */
195 int n_dichan; /* num of DI chans */
196 int n_dochan; /* num of DO chans */
197 int n_counter; /* num of counters */
198 int ai_maxdata; /* resolution of A/D */
199 int ao_maxdata; /* resolution of D/A */
200 const struct comedi_lrange *rangelist_ai; /* rangelist for A/D */
201 const char *rangecode_ai; /* range codes for programming */
202 const struct comedi_lrange *rangelist_ao; /* rangelist for D/A */
203 unsigned int ai_ns_min; /* max sample speed of card v ns */
204 unsigned int fifo_half_size; /* size of FIFO/2 */
207 static const struct boardtype boardtypes[] = {
211 .cardtype = TYPE_PCI171X,
218 .ai_maxdata = 0x0fff,
219 .ao_maxdata = 0x0fff,
220 .rangelist_ai = &range_pci1710_3,
221 .rangecode_ai = range_codes_pci1710_3,
222 .rangelist_ao = &range_pci171x_da,
224 .fifo_half_size = 2048,
226 [BOARD_PCI1710HG] = {
229 .cardtype = TYPE_PCI171X,
236 .ai_maxdata = 0x0fff,
237 .ao_maxdata = 0x0fff,
238 .rangelist_ai = &range_pci1710hg,
239 .rangecode_ai = range_codes_pci1710hg,
240 .rangelist_ao = &range_pci171x_da,
242 .fifo_half_size = 2048,
247 .cardtype = TYPE_PCI171X,
253 .ai_maxdata = 0x0fff,
254 .ao_maxdata = 0x0fff,
255 .rangelist_ai = &range_pci17x1,
256 .rangecode_ai = range_codes_pci17x1,
257 .rangelist_ao = &range_pci171x_da,
259 .fifo_half_size = 512,
264 .cardtype = TYPE_PCI1713,
267 .ai_maxdata = 0x0fff,
268 .rangelist_ai = &range_pci1710_3,
269 .rangecode_ai = range_codes_pci1710_3,
271 .fifo_half_size = 2048,
275 .cardtype = TYPE_PCI1720,
277 .ao_maxdata = 0x0fff,
278 .rangelist_ao = &range_pci1720,
283 .cardtype = TYPE_PCI171X,
287 .ai_maxdata = 0x0fff,
288 .rangelist_ai = &range_pci17x1,
289 .rangecode_ai = range_codes_pci17x1,
291 .fifo_half_size = 512,
295 struct pci1710_private {
296 char neverending_ai; /* we do unlimited AI */
297 unsigned int CntrlReg; /* Control register */
298 unsigned int i8254_osc_base; /* frequence of onboard oscilator */
299 unsigned int ai_do; /* what do AI? 0=nothing, 1 to 4 mode */
300 unsigned int ai_act_scan; /* how many scans we finished */
301 unsigned int ai_act_chan; /* actual position in actual scan */
302 unsigned int ai_buf_ptr; /* data buffer ptr in samples */
303 unsigned char ai_eos; /* 1=EOS wake up */
305 unsigned int ai_et_CntrlReg;
306 unsigned int ai_et_MuxVal;
307 unsigned int ai_et_div1, ai_et_div2;
308 unsigned int act_chanlist[32]; /* list of scanned channel */
309 unsigned char act_chanlist_len; /* len of scanlist */
310 unsigned char act_chanlist_pos; /* actual position in MUX list */
311 unsigned char da_ranges; /* copy of D/A outpit range register */
312 unsigned int ai_scans; /* len of scanlist */
313 unsigned int ai_n_chan; /* how many channels is measured */
314 unsigned int *ai_chanlist; /* actaul chanlist */
315 unsigned int ai_flags; /* flaglist */
316 unsigned int ai_data_len; /* len of data buffer */
317 short *ai_data; /* data buffer */
318 unsigned int ai_timer1; /* timers */
319 unsigned int ai_timer2;
320 short ao_data[4]; /* data output buffer */
321 unsigned int cnt0_write_wait; /* after a write, wait for update of the
325 /* used for gain list programming */
326 static const unsigned int muxonechan[] = {
327 0x0000, 0x0101, 0x0202, 0x0303, 0x0404, 0x0505, 0x0606, 0x0707,
328 0x0808, 0x0909, 0x0a0a, 0x0b0b, 0x0c0c, 0x0d0d, 0x0e0e, 0x0f0f,
329 0x1010, 0x1111, 0x1212, 0x1313, 0x1414, 0x1515, 0x1616, 0x1717,
330 0x1818, 0x1919, 0x1a1a, 0x1b1b, 0x1c1c, 0x1d1d, 0x1e1e, 0x1f1f
334 ==============================================================================
335 Check if channel list from user is built correctly
336 If it's ok, then program scan/gain logic.
337 This works for all cards.
339 static int check_channel_list(struct comedi_device *dev,
340 struct comedi_subdevice *s,
341 unsigned int *chanlist, unsigned int n_chan)
343 unsigned int chansegment[32];
344 unsigned int i, nowmustbechan, seglen, segpos;
346 /* correct channel and range number check itself comedi/range.c */
348 comedi_error(dev, "range/channel list is empty!");
353 return 1; /* seglen=1 */
355 chansegment[0] = chanlist[0]; /* first channel is every time ok */
356 for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {
357 if (chanlist[0] == chanlist[i])
358 break; /* we detected a loop, stop */
359 if ((CR_CHAN(chanlist[i]) & 1) &&
360 (CR_AREF(chanlist[i]) == AREF_DIFF)) {
361 comedi_error(dev, "Odd channel cannot be differential input!\n");
364 nowmustbechan = (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
365 if (CR_AREF(chansegment[i - 1]) == AREF_DIFF)
366 nowmustbechan = (nowmustbechan + 1) % s->n_chan;
367 if (nowmustbechan != CR_CHAN(chanlist[i])) {
368 printk("channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
369 i, CR_CHAN(chanlist[i]), nowmustbechan,
370 CR_CHAN(chanlist[0]));
373 chansegment[i] = chanlist[i]; /* next correct channel in list */
376 for (i = 0, segpos = 0; i < n_chan; i++) {
377 if (chanlist[i] != chansegment[i % seglen]) {
378 printk("bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
379 i, CR_CHAN(chansegment[i]),
380 CR_RANGE(chansegment[i]),
381 CR_AREF(chansegment[i]),
382 CR_CHAN(chanlist[i % seglen]),
383 CR_RANGE(chanlist[i % seglen]),
384 CR_AREF(chansegment[i % seglen]));
391 static void setup_channel_list(struct comedi_device *dev,
392 struct comedi_subdevice *s,
393 unsigned int *chanlist, unsigned int n_chan,
396 const struct boardtype *this_board = comedi_board(dev);
397 struct pci1710_private *devpriv = dev->private;
398 unsigned int i, range, chanprog;
400 devpriv->act_chanlist_len = seglen;
401 devpriv->act_chanlist_pos = 0;
403 for (i = 0; i < seglen; i++) { /* store range list to card */
404 chanprog = muxonechan[CR_CHAN(chanlist[i])];
405 outw(chanprog, dev->iobase + PCI171x_MUX); /* select channel */
406 range = this_board->rangecode_ai[CR_RANGE(chanlist[i])];
407 if (CR_AREF(chanlist[i]) == AREF_DIFF)
409 outw(range, dev->iobase + PCI171x_RANGE); /* select gain */
410 #ifdef PCI171x_PARANOIDCHECK
411 devpriv->act_chanlist[i] =
412 (CR_CHAN(chanlist[i]) << 12) & 0xf000;
415 #ifdef PCI171x_PARANOIDCHECK
416 for ( ; i < n_chan; i++) { /* store remainder of channel list */
417 devpriv->act_chanlist[i] =
418 (CR_CHAN(chanlist[i]) << 12) & 0xf000;
422 devpriv->ai_et_MuxVal =
423 CR_CHAN(chanlist[0]) | (CR_CHAN(chanlist[seglen - 1]) << 8);
424 /* select channel interval to scan */
425 outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
429 ==============================================================================
431 static int pci171x_insn_read_ai(struct comedi_device *dev,
432 struct comedi_subdevice *s,
433 struct comedi_insn *insn, unsigned int *data)
435 struct pci1710_private *devpriv = dev->private;
437 #ifdef PCI171x_PARANOIDCHECK
438 const struct boardtype *this_board = comedi_board(dev);
442 devpriv->CntrlReg &= Control_CNT0;
443 devpriv->CntrlReg |= Control_SW; /* set software trigger */
444 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
445 outb(0, dev->iobase + PCI171x_CLRFIFO);
446 outb(0, dev->iobase + PCI171x_CLRINT);
448 setup_channel_list(dev, s, &insn->chanspec, 1, 1);
450 for (n = 0; n < insn->n; n++) {
451 outw(0, dev->iobase + PCI171x_SOFTTRG); /* start conversion */
455 if (!(inw(dev->iobase + PCI171x_STATUS) & Status_FE))
458 comedi_error(dev, "A/D insn timeout");
459 outb(0, dev->iobase + PCI171x_CLRFIFO);
460 outb(0, dev->iobase + PCI171x_CLRINT);
465 #ifdef PCI171x_PARANOIDCHECK
466 idata = inw(dev->iobase + PCI171x_AD_DATA);
467 if (this_board->cardtype != TYPE_PCI1713)
468 if ((idata & 0xf000) != devpriv->act_chanlist[0]) {
469 comedi_error(dev, "A/D insn data droput!");
472 data[n] = idata & 0x0fff;
474 data[n] = inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff;
479 outb(0, dev->iobase + PCI171x_CLRFIFO);
480 outb(0, dev->iobase + PCI171x_CLRINT);
486 ==============================================================================
488 static int pci171x_insn_write_ao(struct comedi_device *dev,
489 struct comedi_subdevice *s,
490 struct comedi_insn *insn, unsigned int *data)
492 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);
509 for (n = 0; n < insn->n; n++)
510 outw(data[n], dev->iobase + ofs);
512 devpriv->ao_data[chan] = data[n];
519 ==============================================================================
521 static int pci171x_insn_read_ao(struct comedi_device *dev,
522 struct comedi_subdevice *s,
523 struct comedi_insn *insn, unsigned int *data)
525 struct pci1710_private *devpriv = dev->private;
528 chan = CR_CHAN(insn->chanspec);
529 for (n = 0; n < insn->n; n++)
530 data[n] = devpriv->ao_data[chan];
536 ==============================================================================
538 static int pci171x_insn_bits_di(struct comedi_device *dev,
539 struct comedi_subdevice *s,
540 struct comedi_insn *insn, unsigned int *data)
542 data[1] = inw(dev->iobase + PCI171x_DI);
548 ==============================================================================
550 static int pci171x_insn_bits_do(struct comedi_device *dev,
551 struct comedi_subdevice *s,
552 struct comedi_insn *insn, unsigned int *data)
555 s->state &= ~data[0];
556 s->state |= (data[0] & data[1]);
557 outw(s->state, dev->iobase + PCI171x_DO);
565 ==============================================================================
567 static void start_pacer(struct comedi_device *dev, int mode,
568 unsigned int divisor1, unsigned int divisor2)
570 outw(0xb4, dev->iobase + PCI171x_CNTCTRL);
571 outw(0x74, dev->iobase + PCI171x_CNTCTRL);
574 outw(divisor2 & 0xff, dev->iobase + PCI171x_CNT2);
575 outw((divisor2 >> 8) & 0xff, dev->iobase + PCI171x_CNT2);
576 outw(divisor1 & 0xff, dev->iobase + PCI171x_CNT1);
577 outw((divisor1 >> 8) & 0xff, dev->iobase + PCI171x_CNT1);
582 ==============================================================================
584 static int pci171x_insn_counter_read(struct comedi_device *dev,
585 struct comedi_subdevice *s,
586 struct comedi_insn *insn,
589 unsigned int msb, lsb, ccntrl;
592 ccntrl = 0xD2; /* count only */
593 for (i = 0; i < insn->n; i++) {
594 outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
596 lsb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
597 msb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
599 data[0] = lsb | (msb << 8);
606 ==============================================================================
608 static int pci171x_insn_counter_write(struct comedi_device *dev,
609 struct comedi_subdevice *s,
610 struct comedi_insn *insn,
613 struct pci1710_private *devpriv = dev->private;
614 uint msb, lsb, ccntrl, status;
616 lsb = data[0] & 0x00FF;
617 msb = (data[0] & 0xFF00) >> 8;
619 /* write lsb, then msb */
620 outw(lsb, dev->iobase + PCI171x_CNT0);
621 outw(msb, dev->iobase + PCI171x_CNT0);
623 if (devpriv->cnt0_write_wait) {
624 /* wait for the new count to be loaded */
627 outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
628 status = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
629 } while (status & 0x40);
636 ==============================================================================
638 static int pci171x_insn_counter_config(struct comedi_device *dev,
639 struct comedi_subdevice *s,
640 struct comedi_insn *insn,
644 /* This doesn't work like a normal Comedi counter config */
645 struct pci1710_private *devpriv = dev->private;
648 devpriv->cnt0_write_wait = data[0] & 0x20;
650 /* internal or external clock? */
651 if (!(data[0] & 0x10)) { /* internal */
652 devpriv->CntrlReg &= ~Control_CNT0;
654 devpriv->CntrlReg |= Control_CNT0;
656 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
659 ccntrl |= Counter_M0;
661 ccntrl |= Counter_M1;
663 ccntrl |= Counter_M2;
665 ccntrl |= Counter_BCD;
666 ccntrl |= Counter_RW0; /* set read/write mode */
667 ccntrl |= Counter_RW1;
668 outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
675 ==============================================================================
677 static int pci1720_insn_write_ao(struct comedi_device *dev,
678 struct comedi_subdevice *s,
679 struct comedi_insn *insn, unsigned int *data)
681 struct pci1710_private *devpriv = dev->private;
682 int n, rangereg, chan;
684 chan = CR_CHAN(insn->chanspec);
685 rangereg = devpriv->da_ranges & (~(0x03 << (chan << 1)));
686 rangereg |= (CR_RANGE(insn->chanspec) << (chan << 1));
687 if (rangereg != devpriv->da_ranges) {
688 outb(rangereg, dev->iobase + PCI1720_RANGE);
689 devpriv->da_ranges = rangereg;
692 for (n = 0; n < insn->n; n++) {
693 outw(data[n], dev->iobase + PCI1720_DA0 + (chan << 1));
694 outb(0, dev->iobase + PCI1720_SYNCOUT); /* update outputs */
697 devpriv->ao_data[chan] = data[n];
703 ==============================================================================
705 static int pci171x_ai_cancel(struct comedi_device *dev,
706 struct comedi_subdevice *s)
708 const struct boardtype *this_board = comedi_board(dev);
709 struct pci1710_private *devpriv = dev->private;
711 switch (this_board->cardtype) {
713 devpriv->CntrlReg &= Control_CNT0;
714 devpriv->CntrlReg |= Control_SW;
716 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL); /* reset any operations */
717 start_pacer(dev, -1, 0, 0);
718 outb(0, dev->iobase + PCI171x_CLRFIFO);
719 outb(0, dev->iobase + PCI171x_CLRINT);
724 devpriv->ai_act_scan = 0;
725 s->async->cur_chan = 0;
726 devpriv->ai_buf_ptr = 0;
727 devpriv->neverending_ai = 0;
733 ==============================================================================
735 static void interrupt_pci1710_every_sample(void *d)
737 struct comedi_device *dev = d;
738 struct pci1710_private *devpriv = dev->private;
739 struct comedi_subdevice *s = &dev->subdevices[0];
741 #ifdef PCI171x_PARANOIDCHECK
742 const struct boardtype *this_board = comedi_board(dev);
746 m = inw(dev->iobase + PCI171x_STATUS);
748 printk("comedi%d: A/D FIFO empty (%4x)\n", dev->minor, m);
749 pci171x_ai_cancel(dev, s);
750 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
751 comedi_event(dev, s);
756 ("comedi%d: A/D FIFO Full status (Fatal Error!) (%4x)\n",
758 pci171x_ai_cancel(dev, s);
759 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
760 comedi_event(dev, s);
764 outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
766 for (; !(inw(dev->iobase + PCI171x_STATUS) & Status_FE);) {
767 #ifdef PCI171x_PARANOIDCHECK
768 sampl = inw(dev->iobase + PCI171x_AD_DATA);
769 if (this_board->cardtype != TYPE_PCI1713)
770 if ((sampl & 0xf000) !=
771 devpriv->act_chanlist[s->async->cur_chan]) {
773 ("comedi: A/D data dropout: received data from channel %d, expected %d!\n",
774 (sampl & 0xf000) >> 12,
777 async->cur_chan] & 0xf000) >>
779 pci171x_ai_cancel(dev, s);
781 COMEDI_CB_EOA | COMEDI_CB_ERROR;
782 comedi_event(dev, s);
785 comedi_buf_put(s->async, sampl & 0x0fff);
787 comedi_buf_put(s->async,
788 inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff);
790 ++s->async->cur_chan;
792 if (s->async->cur_chan >= devpriv->ai_n_chan)
793 s->async->cur_chan = 0;
796 if (s->async->cur_chan == 0) { /* one scan done */
797 devpriv->ai_act_scan++;
798 if ((!devpriv->neverending_ai) &&
799 (devpriv->ai_act_scan >= devpriv->ai_scans)) {
800 /* all data sampled */
801 pci171x_ai_cancel(dev, s);
802 s->async->events |= COMEDI_CB_EOA;
803 comedi_event(dev, s);
809 outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
811 comedi_event(dev, s);
815 ==============================================================================
817 static int move_block_from_fifo(struct comedi_device *dev,
818 struct comedi_subdevice *s, int n, int turn)
820 struct pci1710_private *devpriv = dev->private;
822 #ifdef PCI171x_PARANOIDCHECK
823 const struct boardtype *this_board = comedi_board(dev);
827 j = s->async->cur_chan;
828 for (i = 0; i < n; i++) {
829 #ifdef PCI171x_PARANOIDCHECK
830 sampl = inw(dev->iobase + PCI171x_AD_DATA);
831 if (this_board->cardtype != TYPE_PCI1713)
832 if ((sampl & 0xf000) != devpriv->act_chanlist[j]) {
834 ("comedi%d: A/D FIFO data dropout: received data from channel %d, expected %d! (%d/%d/%d/%d/%d/%4x)\n",
835 dev->minor, (sampl & 0xf000) >> 12,
836 (devpriv->act_chanlist[j] & 0xf000) >> 12,
837 i, j, devpriv->ai_act_scan, n, turn,
839 pci171x_ai_cancel(dev, s);
841 COMEDI_CB_EOA | COMEDI_CB_ERROR;
842 comedi_event(dev, s);
845 comedi_buf_put(s->async, sampl & 0x0fff);
847 comedi_buf_put(s->async,
848 inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff);
851 if (j >= devpriv->ai_n_chan) {
853 devpriv->ai_act_scan++;
856 s->async->cur_chan = j;
861 ==============================================================================
863 static void interrupt_pci1710_half_fifo(void *d)
865 struct comedi_device *dev = d;
866 const struct boardtype *this_board = comedi_board(dev);
867 struct pci1710_private *devpriv = dev->private;
868 struct comedi_subdevice *s = &dev->subdevices[0];
871 m = inw(dev->iobase + PCI171x_STATUS);
872 if (!(m & Status_FH)) {
873 printk("comedi%d: A/D FIFO not half full! (%4x)\n",
875 pci171x_ai_cancel(dev, s);
876 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
877 comedi_event(dev, s);
882 ("comedi%d: A/D FIFO Full status (Fatal Error!) (%4x)\n",
884 pci171x_ai_cancel(dev, s);
885 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
886 comedi_event(dev, s);
890 samplesinbuf = this_board->fifo_half_size;
891 if (samplesinbuf * sizeof(short) >= devpriv->ai_data_len) {
892 m = devpriv->ai_data_len / sizeof(short);
893 if (move_block_from_fifo(dev, s, m, 0))
899 if (move_block_from_fifo(dev, s, samplesinbuf, 1))
903 if (!devpriv->neverending_ai)
904 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data
906 pci171x_ai_cancel(dev, s);
907 s->async->events |= COMEDI_CB_EOA;
908 comedi_event(dev, s);
911 outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
913 comedi_event(dev, s);
917 ==============================================================================
919 static irqreturn_t interrupt_service_pci1710(int irq, void *d)
921 struct comedi_device *dev = d;
922 struct pci1710_private *devpriv = dev->private;
924 if (!dev->attached) /* is device attached? */
925 return IRQ_NONE; /* no, exit */
926 /* is this interrupt from our board? */
927 if (!(inw(dev->iobase + PCI171x_STATUS) & Status_IRQ))
928 return IRQ_NONE; /* no, exit */
930 if (devpriv->ai_et) { /* Switch from initial TRIG_EXT to TRIG_xxx. */
932 devpriv->CntrlReg &= Control_CNT0;
933 devpriv->CntrlReg |= Control_SW; /* set software trigger */
934 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
935 devpriv->CntrlReg = devpriv->ai_et_CntrlReg;
936 outb(0, dev->iobase + PCI171x_CLRFIFO);
937 outb(0, dev->iobase + PCI171x_CLRINT);
938 outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
939 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
941 start_pacer(dev, 1, devpriv->ai_et_div1, devpriv->ai_et_div2);
944 if (devpriv->ai_eos) { /* We use FIFO half full INT or not? */
945 interrupt_pci1710_every_sample(d);
947 interrupt_pci1710_half_fifo(d);
953 ==============================================================================
955 static int pci171x_ai_docmd_and_mode(int mode, struct comedi_device *dev,
956 struct comedi_subdevice *s)
958 const struct boardtype *this_board = comedi_board(dev);
959 struct pci1710_private *devpriv = dev->private;
960 unsigned int divisor1 = 0, divisor2 = 0;
963 start_pacer(dev, -1, 0, 0); /* stop pacer */
965 seglen = check_channel_list(dev, s, devpriv->ai_chanlist,
969 setup_channel_list(dev, s, devpriv->ai_chanlist,
970 devpriv->ai_n_chan, seglen);
972 outb(0, dev->iobase + PCI171x_CLRFIFO);
973 outb(0, dev->iobase + PCI171x_CLRINT);
975 devpriv->ai_do = mode;
977 devpriv->ai_act_scan = 0;
978 s->async->cur_chan = 0;
979 devpriv->ai_buf_ptr = 0;
980 devpriv->neverending_ai = 0;
982 devpriv->CntrlReg &= Control_CNT0;
983 /* don't we want wake up every scan? devpriv->ai_eos=1; */
984 if ((devpriv->ai_flags & TRIG_WAKE_EOS)) {
987 devpriv->CntrlReg |= Control_ONEFH;
991 if ((devpriv->ai_scans == 0) || (devpriv->ai_scans == -1))
992 devpriv->neverending_ai = 1;
993 /* well, user want neverending */
995 devpriv->neverending_ai = 0;
1000 if (devpriv->ai_timer1 < this_board->ai_ns_min)
1001 devpriv->ai_timer1 = this_board->ai_ns_min;
1002 devpriv->CntrlReg |= Control_PACER | Control_IRQEN;
1004 devpriv->ai_et_CntrlReg = devpriv->CntrlReg;
1005 devpriv->CntrlReg &=
1006 ~(Control_PACER | Control_ONEFH | Control_GATE);
1007 devpriv->CntrlReg |= Control_EXT;
1012 i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
1013 &divisor2, &devpriv->ai_timer1,
1014 devpriv->ai_flags & TRIG_ROUND_MASK);
1015 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
1018 start_pacer(dev, mode, divisor1, divisor2);
1020 devpriv->ai_et_div1 = divisor1;
1021 devpriv->ai_et_div2 = divisor2;
1025 devpriv->CntrlReg |= Control_EXT | Control_IRQEN;
1026 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
1034 ==============================================================================
1036 static int pci171x_ai_cmdtest(struct comedi_device *dev,
1037 struct comedi_subdevice *s,
1038 struct comedi_cmd *cmd)
1040 const struct boardtype *this_board = comedi_board(dev);
1041 struct pci1710_private *devpriv = dev->private;
1044 unsigned int divisor1 = 0, divisor2 = 0;
1046 /* Step 1 : check if triggers are trivially valid */
1048 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
1049 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
1050 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
1051 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
1052 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
1057 /* step 2a: make sure trigger sources are unique */
1059 err |= cfc_check_trigger_is_unique(cmd->start_src);
1060 err |= cfc_check_trigger_is_unique(cmd->convert_src);
1061 err |= cfc_check_trigger_is_unique(cmd->stop_src);
1063 /* step 2b: and mutually compatible */
1068 /* Step 3: check if arguments are trivially valid */
1070 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
1071 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
1073 if (cmd->convert_src == TRIG_TIMER)
1074 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
1075 this_board->ai_ns_min);
1076 else /* TRIG_FOLLOW */
1077 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
1079 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
1081 if (cmd->stop_src == TRIG_COUNT)
1082 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
1083 else /* TRIG_NONE */
1084 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
1089 /* step 4: fix up any arguments */
1091 if (cmd->convert_src == TRIG_TIMER) {
1092 tmp = cmd->convert_arg;
1093 i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
1094 &divisor2, &cmd->convert_arg,
1095 cmd->flags & TRIG_ROUND_MASK);
1096 if (cmd->convert_arg < this_board->ai_ns_min)
1097 cmd->convert_arg = this_board->ai_ns_min;
1098 if (tmp != cmd->convert_arg)
1105 /* step 5: complain about special chanlist considerations */
1107 if (cmd->chanlist) {
1108 if (!check_channel_list(dev, s, cmd->chanlist,
1110 return 5; /* incorrect channels list */
1117 ==============================================================================
1119 static int pci171x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
1121 struct pci1710_private *devpriv = dev->private;
1122 struct comedi_cmd *cmd = &s->async->cmd;
1124 devpriv->ai_n_chan = cmd->chanlist_len;
1125 devpriv->ai_chanlist = cmd->chanlist;
1126 devpriv->ai_flags = cmd->flags;
1127 devpriv->ai_data_len = s->async->prealloc_bufsz;
1128 devpriv->ai_data = s->async->prealloc_buf;
1129 devpriv->ai_timer1 = 0;
1130 devpriv->ai_timer2 = 0;
1132 if (cmd->stop_src == TRIG_COUNT)
1133 devpriv->ai_scans = cmd->stop_arg;
1135 devpriv->ai_scans = 0;
1138 if (cmd->scan_begin_src == TRIG_FOLLOW) { /* mode 1, 2, 3 */
1139 if (cmd->convert_src == TRIG_TIMER) { /* mode 1 and 2 */
1140 devpriv->ai_timer1 = cmd->convert_arg;
1141 return pci171x_ai_docmd_and_mode(cmd->start_src ==
1142 TRIG_EXT ? 2 : 1, dev,
1145 if (cmd->convert_src == TRIG_EXT) { /* mode 3 */
1146 return pci171x_ai_docmd_and_mode(3, dev, s);
1154 ==============================================================================
1156 static int pci171x_reset(struct comedi_device *dev)
1158 const struct boardtype *this_board = comedi_board(dev);
1159 struct pci1710_private *devpriv = dev->private;
1161 outw(0x30, dev->iobase + PCI171x_CNTCTRL);
1162 devpriv->CntrlReg = Control_SW | Control_CNT0; /* Software trigger, CNT0=external */
1163 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL); /* reset any operations */
1164 outb(0, dev->iobase + PCI171x_CLRFIFO); /* clear FIFO */
1165 outb(0, dev->iobase + PCI171x_CLRINT); /* clear INT request */
1166 start_pacer(dev, -1, 0, 0); /* stop 8254 */
1167 devpriv->da_ranges = 0;
1168 if (this_board->n_aochan) {
1169 outb(devpriv->da_ranges, dev->iobase + PCI171x_DAREF); /* set DACs to 0..5V */
1170 outw(0, dev->iobase + PCI171x_DA1); /* set DA outputs to 0V */
1171 devpriv->ao_data[0] = 0x0000;
1172 if (this_board->n_aochan > 1) {
1173 outw(0, dev->iobase + PCI171x_DA2);
1174 devpriv->ao_data[1] = 0x0000;
1177 outw(0, dev->iobase + PCI171x_DO); /* digital outputs to 0 */
1178 outb(0, dev->iobase + PCI171x_CLRFIFO); /* clear FIFO */
1179 outb(0, dev->iobase + PCI171x_CLRINT); /* clear INT request */
1185 ==============================================================================
1187 static int pci1720_reset(struct comedi_device *dev)
1189 struct pci1710_private *devpriv = dev->private;
1191 outb(Syncont_SC0, dev->iobase + PCI1720_SYNCONT); /* set synchronous output mode */
1192 devpriv->da_ranges = 0xAA;
1193 outb(devpriv->da_ranges, dev->iobase + PCI1720_RANGE); /* set all ranges to +/-5V */
1194 outw(0x0800, dev->iobase + PCI1720_DA0); /* set outputs to 0V */
1195 outw(0x0800, dev->iobase + PCI1720_DA1);
1196 outw(0x0800, dev->iobase + PCI1720_DA2);
1197 outw(0x0800, dev->iobase + PCI1720_DA3);
1198 outb(0, dev->iobase + PCI1720_SYNCOUT); /* update outputs */
1199 devpriv->ao_data[0] = 0x0800;
1200 devpriv->ao_data[1] = 0x0800;
1201 devpriv->ao_data[2] = 0x0800;
1202 devpriv->ao_data[3] = 0x0800;
1207 ==============================================================================
1209 static int pci1710_reset(struct comedi_device *dev)
1211 const struct boardtype *this_board = comedi_board(dev);
1213 switch (this_board->cardtype) {
1215 return pci1720_reset(dev);
1217 return pci171x_reset(dev);
1221 static int pci1710_auto_attach(struct comedi_device *dev,
1222 unsigned long context)
1224 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
1225 const struct boardtype *this_board = NULL;
1226 struct pci1710_private *devpriv;
1227 struct comedi_subdevice *s;
1228 int ret, subdev, n_subdevices;
1230 if (context < ARRAY_SIZE(boardtypes))
1231 this_board = &boardtypes[context];
1234 dev->board_ptr = this_board;
1235 dev->board_name = this_board->name;
1237 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
1241 ret = comedi_pci_enable(dev);
1244 dev->iobase = pci_resource_start(pcidev, 2);
1247 if (this_board->n_aichan)
1249 if (this_board->n_aochan)
1251 if (this_board->n_dichan)
1253 if (this_board->n_dochan)
1255 if (this_board->n_counter)
1258 ret = comedi_alloc_subdevices(dev, n_subdevices);
1264 if (this_board->have_irq && pcidev->irq) {
1265 ret = request_irq(pcidev->irq, interrupt_service_pci1710,
1266 IRQF_SHARED, dev->board_name, dev);
1268 dev->irq = pcidev->irq;
1273 if (this_board->n_aichan) {
1274 s = &dev->subdevices[subdev];
1275 dev->read_subdev = s;
1276 s->type = COMEDI_SUBD_AI;
1277 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
1278 if (this_board->n_aichand)
1279 s->subdev_flags |= SDF_DIFF;
1280 s->n_chan = this_board->n_aichan;
1281 s->maxdata = this_board->ai_maxdata;
1282 s->len_chanlist = this_board->n_aichan;
1283 s->range_table = this_board->rangelist_ai;
1284 s->cancel = pci171x_ai_cancel;
1285 s->insn_read = pci171x_insn_read_ai;
1287 s->subdev_flags |= SDF_CMD_READ;
1288 s->do_cmdtest = pci171x_ai_cmdtest;
1289 s->do_cmd = pci171x_ai_cmd;
1291 devpriv->i8254_osc_base = 100; /* 100ns=10MHz */
1295 if (this_board->n_aochan) {
1296 s = &dev->subdevices[subdev];
1297 s->type = COMEDI_SUBD_AO;
1298 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1299 s->n_chan = this_board->n_aochan;
1300 s->maxdata = this_board->ao_maxdata;
1301 s->len_chanlist = this_board->n_aochan;
1302 s->range_table = this_board->rangelist_ao;
1303 switch (this_board->cardtype) {
1305 s->insn_write = pci1720_insn_write_ao;
1308 s->insn_write = pci171x_insn_write_ao;
1311 s->insn_read = pci171x_insn_read_ao;
1315 if (this_board->n_dichan) {
1316 s = &dev->subdevices[subdev];
1317 s->type = COMEDI_SUBD_DI;
1318 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
1319 s->n_chan = this_board->n_dichan;
1321 s->len_chanlist = this_board->n_dichan;
1322 s->range_table = &range_digital;
1323 s->io_bits = 0; /* all bits input */
1324 s->insn_bits = pci171x_insn_bits_di;
1328 if (this_board->n_dochan) {
1329 s = &dev->subdevices[subdev];
1330 s->type = COMEDI_SUBD_DO;
1331 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1332 s->n_chan = this_board->n_dochan;
1334 s->len_chanlist = this_board->n_dochan;
1335 s->range_table = &range_digital;
1336 /* all bits output */
1337 s->io_bits = (1 << this_board->n_dochan) - 1;
1339 s->insn_bits = pci171x_insn_bits_do;
1343 if (this_board->n_counter) {
1344 s = &dev->subdevices[subdev];
1345 s->type = COMEDI_SUBD_COUNTER;
1346 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
1347 s->n_chan = this_board->n_counter;
1348 s->len_chanlist = this_board->n_counter;
1349 s->maxdata = 0xffff;
1350 s->range_table = &range_unknown;
1351 s->insn_read = pci171x_insn_counter_read;
1352 s->insn_write = pci171x_insn_counter_write;
1353 s->insn_config = pci171x_insn_counter_config;
1357 dev_info(dev->class_dev, "%s attached, irq %sabled\n",
1358 dev->board_name, dev->irq ? "en" : "dis");
1363 static void pci1710_detach(struct comedi_device *dev)
1368 free_irq(dev->irq, dev);
1369 comedi_pci_disable(dev);
1372 static struct comedi_driver adv_pci1710_driver = {
1373 .driver_name = "adv_pci1710",
1374 .module = THIS_MODULE,
1375 .auto_attach = pci1710_auto_attach,
1376 .detach = pci1710_detach,
1379 static int adv_pci1710_pci_probe(struct pci_dev *dev,
1380 const struct pci_device_id *id)
1382 return comedi_pci_auto_config(dev, &adv_pci1710_driver,
1386 static DEFINE_PCI_DEVICE_TABLE(adv_pci1710_pci_table) = {
1388 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1389 PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050),
1390 .driver_data = BOARD_PCI1710,
1392 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1393 PCI_VENDOR_ID_ADVANTECH, 0x0000),
1394 .driver_data = BOARD_PCI1710,
1396 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1397 PCI_VENDOR_ID_ADVANTECH, 0xb100),
1398 .driver_data = BOARD_PCI1710,
1400 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1401 PCI_VENDOR_ID_ADVANTECH, 0xb200),
1402 .driver_data = BOARD_PCI1710,
1404 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1405 PCI_VENDOR_ID_ADVANTECH, 0xc100),
1406 .driver_data = BOARD_PCI1710,
1408 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1409 PCI_VENDOR_ID_ADVANTECH, 0xc200),
1410 .driver_data = BOARD_PCI1710,
1412 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd100),
1413 .driver_data = BOARD_PCI1710,
1415 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1416 PCI_VENDOR_ID_ADVANTECH, 0x0002),
1417 .driver_data = BOARD_PCI1710HG,
1419 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1420 PCI_VENDOR_ID_ADVANTECH, 0xb102),
1421 .driver_data = BOARD_PCI1710HG,
1423 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1424 PCI_VENDOR_ID_ADVANTECH, 0xb202),
1425 .driver_data = BOARD_PCI1710HG,
1427 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1428 PCI_VENDOR_ID_ADVANTECH, 0xc102),
1429 .driver_data = BOARD_PCI1710HG,
1431 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1432 PCI_VENDOR_ID_ADVANTECH, 0xc202),
1433 .driver_data = BOARD_PCI1710HG,
1435 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd102),
1436 .driver_data = BOARD_PCI1710HG,
1438 { PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 },
1439 { PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 },
1440 { PCI_VDEVICE(ADVANTECH, 0x1720), BOARD_PCI1720 },
1441 { PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 },
1444 MODULE_DEVICE_TABLE(pci, adv_pci1710_pci_table);
1446 static struct pci_driver adv_pci1710_pci_driver = {
1447 .name = "adv_pci1710",
1448 .id_table = adv_pci1710_pci_table,
1449 .probe = adv_pci1710_pci_probe,
1450 .remove = comedi_pci_auto_unconfig,
1452 module_comedi_pci_driver(adv_pci1710_driver, adv_pci1710_pci_driver);
1454 MODULE_AUTHOR("Comedi http://www.comedi.org");
1455 MODULE_DESCRIPTION("Comedi low-level driver");
1456 MODULE_LICENSE("GPL");