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 IORANGE_171x 32
63 #define IORANGE_1720 16
65 #define PCI171x_AD_DATA 0 /* R: A/D data */
66 #define PCI171x_SOFTTRG 0 /* W: soft trigger for A/D */
67 #define PCI171x_RANGE 2 /* W: A/D gain/range register */
68 #define PCI171x_MUX 4 /* W: A/D multiplexor control */
69 #define PCI171x_STATUS 6 /* R: status register */
70 #define PCI171x_CONTROL 6 /* W: control register */
71 #define PCI171x_CLRINT 8 /* W: clear interrupts request */
72 #define PCI171x_CLRFIFO 9 /* W: clear FIFO */
73 #define PCI171x_DA1 10 /* W: D/A register */
74 #define PCI171x_DA2 12 /* W: D/A register */
75 #define PCI171x_DAREF 14 /* W: D/A reference control */
76 #define PCI171x_DI 16 /* R: digi inputs */
77 #define PCI171x_DO 16 /* R: digi inputs */
78 #define PCI171x_CNT0 24 /* R/W: 8254 counter 0 */
79 #define PCI171x_CNT1 26 /* R/W: 8254 counter 1 */
80 #define PCI171x_CNT2 28 /* R/W: 8254 counter 2 */
81 #define PCI171x_CNTCTRL 30 /* W: 8254 counter control */
83 /* upper bits from status register (PCI171x_STATUS) (lower is same with control
85 #define Status_FE 0x0100 /* 1=FIFO is empty */
86 #define Status_FH 0x0200 /* 1=FIFO is half full */
87 #define Status_FF 0x0400 /* 1=FIFO is full, fatal error */
88 #define Status_IRQ 0x0800 /* 1=IRQ occurred */
89 /* bits from control register (PCI171x_CONTROL) */
90 #define Control_CNT0 0x0040 /* 1=CNT0 have external source,
91 * 0=have internal 100kHz source */
92 #define Control_ONEFH 0x0020 /* 1=IRQ on FIFO is half full, 0=every sample */
93 #define Control_IRQEN 0x0010 /* 1=enable IRQ */
94 #define Control_GATE 0x0008 /* 1=enable external trigger GATE (8254?) */
95 #define Control_EXT 0x0004 /* 1=external trigger source */
96 #define Control_PACER 0x0002 /* 1=enable internal 8254 trigger source */
97 #define Control_SW 0x0001 /* 1=enable software trigger source */
98 /* bits from counter control register (PCI171x_CNTCTRL) */
99 #define Counter_BCD 0x0001 /* 0 = binary counter, 1 = BCD counter */
100 #define Counter_M0 0x0002 /* M0-M2 select modes 0-5 */
101 #define Counter_M1 0x0004 /* 000 = mode 0, 010 = mode 2 ... */
102 #define Counter_M2 0x0008
103 #define Counter_RW0 0x0010 /* RW0/RW1 select read/write mode */
104 #define Counter_RW1 0x0020
105 #define Counter_SC0 0x0040 /* Select Counter. Only 00 or 11 may */
106 #define Counter_SC1 0x0080 /* be used, 00 for CNT0,
107 * 11 for read-back command */
109 #define PCI1720_DA0 0 /* W: D/A register 0 */
110 #define PCI1720_DA1 2 /* W: D/A register 1 */
111 #define PCI1720_DA2 4 /* W: D/A register 2 */
112 #define PCI1720_DA3 6 /* W: D/A register 3 */
113 #define PCI1720_RANGE 8 /* R/W: D/A range register */
114 #define PCI1720_SYNCOUT 9 /* W: D/A synchronized output register */
115 #define PCI1720_SYNCONT 15 /* R/W: D/A synchronized control */
117 /* D/A synchronized control (PCI1720_SYNCONT) */
118 #define Syncont_SC0 1 /* set synchronous output mode */
120 static const struct comedi_lrange range_pci1710_3 = { 9, {
133 static const char range_codes_pci1710_3[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
134 0x10, 0x11, 0x12, 0x13 };
136 static const struct comedi_lrange range_pci1710hg = { 12, {
152 static const char range_codes_pci1710hg[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
153 0x05, 0x06, 0x07, 0x10, 0x11,
156 static const struct comedi_lrange range_pci17x1 = { 5, {
165 static const char range_codes_pci17x1[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
167 static const struct comedi_lrange range_pci1720 = { 4, {
175 static const struct comedi_lrange range_pci171x_da = { 2, {
181 enum pci1710_boardid {
191 const char *name; /* board name */
192 int iorange; /* I/O range len */
193 char have_irq; /* 1=card support IRQ */
194 char cardtype; /* 0=1710& co. 2=1713, ... */
195 int n_aichan; /* num of A/D chans */
196 int n_aichand; /* num of A/D chans in diff mode */
197 int n_aochan; /* num of D/A chans */
198 int n_dichan; /* num of DI chans */
199 int n_dochan; /* num of DO chans */
200 int n_counter; /* num of counters */
201 int ai_maxdata; /* resolution of A/D */
202 int ao_maxdata; /* resolution of D/A */
203 const struct comedi_lrange *rangelist_ai; /* rangelist for A/D */
204 const char *rangecode_ai; /* range codes for programming */
205 const struct comedi_lrange *rangelist_ao; /* rangelist for D/A */
206 unsigned int ai_ns_min; /* max sample speed of card v ns */
207 unsigned int fifo_half_size; /* size of FIFO/2 */
210 static const struct boardtype boardtypes[] = {
213 .iorange = IORANGE_171x,
215 .cardtype = TYPE_PCI171X,
222 .ai_maxdata = 0x0fff,
223 .ao_maxdata = 0x0fff,
224 .rangelist_ai = &range_pci1710_3,
225 .rangecode_ai = range_codes_pci1710_3,
226 .rangelist_ao = &range_pci171x_da,
228 .fifo_half_size = 2048,
230 [BOARD_PCI1710HG] = {
232 .iorange = IORANGE_171x,
234 .cardtype = TYPE_PCI171X,
241 .ai_maxdata = 0x0fff,
242 .ao_maxdata = 0x0fff,
243 .rangelist_ai = &range_pci1710hg,
244 .rangecode_ai = range_codes_pci1710hg,
245 .rangelist_ao = &range_pci171x_da,
247 .fifo_half_size = 2048,
251 .iorange = IORANGE_171x,
253 .cardtype = TYPE_PCI171X,
259 .ai_maxdata = 0x0fff,
260 .ao_maxdata = 0x0fff,
261 .rangelist_ai = &range_pci17x1,
262 .rangecode_ai = range_codes_pci17x1,
263 .rangelist_ao = &range_pci171x_da,
265 .fifo_half_size = 512,
269 .iorange = IORANGE_171x,
271 .cardtype = TYPE_PCI1713,
274 .ai_maxdata = 0x0fff,
275 .rangelist_ai = &range_pci1710_3,
276 .rangecode_ai = range_codes_pci1710_3,
278 .fifo_half_size = 2048,
282 .iorange = IORANGE_1720,
283 .cardtype = TYPE_PCI1720,
285 .ao_maxdata = 0x0fff,
286 .rangelist_ao = &range_pci1720,
290 .iorange = IORANGE_171x,
292 .cardtype = TYPE_PCI171X,
296 .ai_maxdata = 0x0fff,
297 .rangelist_ai = &range_pci17x1,
298 .rangecode_ai = range_codes_pci17x1,
300 .fifo_half_size = 512,
304 struct pci1710_private {
305 char neverending_ai; /* we do unlimited AI */
306 unsigned int CntrlReg; /* Control register */
307 unsigned int i8254_osc_base; /* frequence of onboard oscilator */
308 unsigned int ai_do; /* what do AI? 0=nothing, 1 to 4 mode */
309 unsigned int ai_act_scan; /* how many scans we finished */
310 unsigned int ai_act_chan; /* actual position in actual scan */
311 unsigned int ai_buf_ptr; /* data buffer ptr in samples */
312 unsigned char ai_eos; /* 1=EOS wake up */
314 unsigned int ai_et_CntrlReg;
315 unsigned int ai_et_MuxVal;
316 unsigned int ai_et_div1, ai_et_div2;
317 unsigned int act_chanlist[32]; /* list of scanned channel */
318 unsigned char act_chanlist_len; /* len of scanlist */
319 unsigned char act_chanlist_pos; /* actual position in MUX list */
320 unsigned char da_ranges; /* copy of D/A outpit range register */
321 unsigned int ai_scans; /* len of scanlist */
322 unsigned int ai_n_chan; /* how many channels is measured */
323 unsigned int *ai_chanlist; /* actaul chanlist */
324 unsigned int ai_flags; /* flaglist */
325 unsigned int ai_data_len; /* len of data buffer */
326 short *ai_data; /* data buffer */
327 unsigned int ai_timer1; /* timers */
328 unsigned int ai_timer2;
329 short ao_data[4]; /* data output buffer */
330 unsigned int cnt0_write_wait; /* after a write, wait for update of the
334 /* used for gain list programming */
335 static const unsigned int muxonechan[] = {
336 0x0000, 0x0101, 0x0202, 0x0303, 0x0404, 0x0505, 0x0606, 0x0707,
337 0x0808, 0x0909, 0x0a0a, 0x0b0b, 0x0c0c, 0x0d0d, 0x0e0e, 0x0f0f,
338 0x1010, 0x1111, 0x1212, 0x1313, 0x1414, 0x1515, 0x1616, 0x1717,
339 0x1818, 0x1919, 0x1a1a, 0x1b1b, 0x1c1c, 0x1d1d, 0x1e1e, 0x1f1f
343 ==============================================================================
344 Check if channel list from user is built correctly
345 If it's ok, then program scan/gain logic.
346 This works for all cards.
348 static int check_channel_list(struct comedi_device *dev,
349 struct comedi_subdevice *s,
350 unsigned int *chanlist, unsigned int n_chan)
352 unsigned int chansegment[32];
353 unsigned int i, nowmustbechan, seglen, segpos;
355 /* correct channel and range number check itself comedi/range.c */
357 comedi_error(dev, "range/channel list is empty!");
362 return 1; /* seglen=1 */
364 chansegment[0] = chanlist[0]; /* first channel is every time ok */
365 for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {
366 if (chanlist[0] == chanlist[i])
367 break; /* we detected a loop, stop */
368 if ((CR_CHAN(chanlist[i]) & 1) &&
369 (CR_AREF(chanlist[i]) == AREF_DIFF)) {
370 comedi_error(dev, "Odd channel cannot be differential input!\n");
373 nowmustbechan = (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
374 if (CR_AREF(chansegment[i - 1]) == AREF_DIFF)
375 nowmustbechan = (nowmustbechan + 1) % s->n_chan;
376 if (nowmustbechan != CR_CHAN(chanlist[i])) {
377 printk("channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
378 i, CR_CHAN(chanlist[i]), nowmustbechan,
379 CR_CHAN(chanlist[0]));
382 chansegment[i] = chanlist[i]; /* next correct channel in list */
385 for (i = 0, segpos = 0; i < n_chan; i++) {
386 if (chanlist[i] != chansegment[i % seglen]) {
387 printk("bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
388 i, CR_CHAN(chansegment[i]),
389 CR_RANGE(chansegment[i]),
390 CR_AREF(chansegment[i]),
391 CR_CHAN(chanlist[i % seglen]),
392 CR_RANGE(chanlist[i % seglen]),
393 CR_AREF(chansegment[i % seglen]));
400 static void setup_channel_list(struct comedi_device *dev,
401 struct comedi_subdevice *s,
402 unsigned int *chanlist, unsigned int n_chan,
405 const struct boardtype *this_board = comedi_board(dev);
406 struct pci1710_private *devpriv = dev->private;
407 unsigned int i, range, chanprog;
409 devpriv->act_chanlist_len = seglen;
410 devpriv->act_chanlist_pos = 0;
412 for (i = 0; i < seglen; i++) { /* store range list to card */
413 chanprog = muxonechan[CR_CHAN(chanlist[i])];
414 outw(chanprog, dev->iobase + PCI171x_MUX); /* select channel */
415 range = this_board->rangecode_ai[CR_RANGE(chanlist[i])];
416 if (CR_AREF(chanlist[i]) == AREF_DIFF)
418 outw(range, dev->iobase + PCI171x_RANGE); /* select gain */
419 #ifdef PCI171x_PARANOIDCHECK
420 devpriv->act_chanlist[i] =
421 (CR_CHAN(chanlist[i]) << 12) & 0xf000;
424 #ifdef PCI171x_PARANOIDCHECK
425 for ( ; i < n_chan; i++) { /* store remainder of channel list */
426 devpriv->act_chanlist[i] =
427 (CR_CHAN(chanlist[i]) << 12) & 0xf000;
431 devpriv->ai_et_MuxVal =
432 CR_CHAN(chanlist[0]) | (CR_CHAN(chanlist[seglen - 1]) << 8);
433 /* select channel interval to scan */
434 outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
438 ==============================================================================
440 static int pci171x_insn_read_ai(struct comedi_device *dev,
441 struct comedi_subdevice *s,
442 struct comedi_insn *insn, unsigned int *data)
444 struct pci1710_private *devpriv = dev->private;
446 #ifdef PCI171x_PARANOIDCHECK
447 const struct boardtype *this_board = comedi_board(dev);
451 devpriv->CntrlReg &= Control_CNT0;
452 devpriv->CntrlReg |= Control_SW; /* set software trigger */
453 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
454 outb(0, dev->iobase + PCI171x_CLRFIFO);
455 outb(0, dev->iobase + PCI171x_CLRINT);
457 setup_channel_list(dev, s, &insn->chanspec, 1, 1);
459 for (n = 0; n < insn->n; n++) {
460 outw(0, dev->iobase + PCI171x_SOFTTRG); /* start conversion */
464 if (!(inw(dev->iobase + PCI171x_STATUS) & Status_FE))
467 comedi_error(dev, "A/D insn timeout");
468 outb(0, dev->iobase + PCI171x_CLRFIFO);
469 outb(0, dev->iobase + PCI171x_CLRINT);
474 #ifdef PCI171x_PARANOIDCHECK
475 idata = inw(dev->iobase + PCI171x_AD_DATA);
476 if (this_board->cardtype != TYPE_PCI1713)
477 if ((idata & 0xf000) != devpriv->act_chanlist[0]) {
478 comedi_error(dev, "A/D insn data droput!");
481 data[n] = idata & 0x0fff;
483 data[n] = inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff;
488 outb(0, dev->iobase + PCI171x_CLRFIFO);
489 outb(0, dev->iobase + PCI171x_CLRINT);
495 ==============================================================================
497 static int pci171x_insn_write_ao(struct comedi_device *dev,
498 struct comedi_subdevice *s,
499 struct comedi_insn *insn, unsigned int *data)
501 struct pci1710_private *devpriv = dev->private;
502 int n, chan, range, ofs;
504 chan = CR_CHAN(insn->chanspec);
505 range = CR_RANGE(insn->chanspec);
507 devpriv->da_ranges &= 0xfb;
508 devpriv->da_ranges |= (range << 2);
509 outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
512 devpriv->da_ranges &= 0xfe;
513 devpriv->da_ranges |= range;
514 outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
518 for (n = 0; n < insn->n; n++)
519 outw(data[n], dev->iobase + ofs);
521 devpriv->ao_data[chan] = data[n];
528 ==============================================================================
530 static int pci171x_insn_read_ao(struct comedi_device *dev,
531 struct comedi_subdevice *s,
532 struct comedi_insn *insn, unsigned int *data)
534 struct pci1710_private *devpriv = dev->private;
537 chan = CR_CHAN(insn->chanspec);
538 for (n = 0; n < insn->n; n++)
539 data[n] = devpriv->ao_data[chan];
545 ==============================================================================
547 static int pci171x_insn_bits_di(struct comedi_device *dev,
548 struct comedi_subdevice *s,
549 struct comedi_insn *insn, unsigned int *data)
551 data[1] = inw(dev->iobase + PCI171x_DI);
557 ==============================================================================
559 static int pci171x_insn_bits_do(struct comedi_device *dev,
560 struct comedi_subdevice *s,
561 struct comedi_insn *insn, unsigned int *data)
564 s->state &= ~data[0];
565 s->state |= (data[0] & data[1]);
566 outw(s->state, dev->iobase + PCI171x_DO);
574 ==============================================================================
576 static void start_pacer(struct comedi_device *dev, int mode,
577 unsigned int divisor1, unsigned int divisor2)
579 outw(0xb4, dev->iobase + PCI171x_CNTCTRL);
580 outw(0x74, dev->iobase + PCI171x_CNTCTRL);
583 outw(divisor2 & 0xff, dev->iobase + PCI171x_CNT2);
584 outw((divisor2 >> 8) & 0xff, dev->iobase + PCI171x_CNT2);
585 outw(divisor1 & 0xff, dev->iobase + PCI171x_CNT1);
586 outw((divisor1 >> 8) & 0xff, dev->iobase + PCI171x_CNT1);
591 ==============================================================================
593 static int pci171x_insn_counter_read(struct comedi_device *dev,
594 struct comedi_subdevice *s,
595 struct comedi_insn *insn,
598 unsigned int msb, lsb, ccntrl;
601 ccntrl = 0xD2; /* count only */
602 for (i = 0; i < insn->n; i++) {
603 outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
605 lsb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
606 msb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
608 data[0] = lsb | (msb << 8);
615 ==============================================================================
617 static int pci171x_insn_counter_write(struct comedi_device *dev,
618 struct comedi_subdevice *s,
619 struct comedi_insn *insn,
622 struct pci1710_private *devpriv = dev->private;
623 uint msb, lsb, ccntrl, status;
625 lsb = data[0] & 0x00FF;
626 msb = (data[0] & 0xFF00) >> 8;
628 /* write lsb, then msb */
629 outw(lsb, dev->iobase + PCI171x_CNT0);
630 outw(msb, dev->iobase + PCI171x_CNT0);
632 if (devpriv->cnt0_write_wait) {
633 /* wait for the new count to be loaded */
636 outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
637 status = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
638 } while (status & 0x40);
645 ==============================================================================
647 static int pci171x_insn_counter_config(struct comedi_device *dev,
648 struct comedi_subdevice *s,
649 struct comedi_insn *insn,
653 /* This doesn't work like a normal Comedi counter config */
654 struct pci1710_private *devpriv = dev->private;
657 devpriv->cnt0_write_wait = data[0] & 0x20;
659 /* internal or external clock? */
660 if (!(data[0] & 0x10)) { /* internal */
661 devpriv->CntrlReg &= ~Control_CNT0;
663 devpriv->CntrlReg |= Control_CNT0;
665 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
668 ccntrl |= Counter_M0;
670 ccntrl |= Counter_M1;
672 ccntrl |= Counter_M2;
674 ccntrl |= Counter_BCD;
675 ccntrl |= Counter_RW0; /* set read/write mode */
676 ccntrl |= Counter_RW1;
677 outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
684 ==============================================================================
686 static int pci1720_insn_write_ao(struct comedi_device *dev,
687 struct comedi_subdevice *s,
688 struct comedi_insn *insn, unsigned int *data)
690 struct pci1710_private *devpriv = dev->private;
691 int n, rangereg, chan;
693 chan = CR_CHAN(insn->chanspec);
694 rangereg = devpriv->da_ranges & (~(0x03 << (chan << 1)));
695 rangereg |= (CR_RANGE(insn->chanspec) << (chan << 1));
696 if (rangereg != devpriv->da_ranges) {
697 outb(rangereg, dev->iobase + PCI1720_RANGE);
698 devpriv->da_ranges = rangereg;
701 for (n = 0; n < insn->n; n++) {
702 outw(data[n], dev->iobase + PCI1720_DA0 + (chan << 1));
703 outb(0, dev->iobase + PCI1720_SYNCOUT); /* update outputs */
706 devpriv->ao_data[chan] = data[n];
712 ==============================================================================
714 static int pci171x_ai_cancel(struct comedi_device *dev,
715 struct comedi_subdevice *s)
717 const struct boardtype *this_board = comedi_board(dev);
718 struct pci1710_private *devpriv = dev->private;
720 switch (this_board->cardtype) {
722 devpriv->CntrlReg &= Control_CNT0;
723 devpriv->CntrlReg |= Control_SW;
725 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL); /* reset any operations */
726 start_pacer(dev, -1, 0, 0);
727 outb(0, dev->iobase + PCI171x_CLRFIFO);
728 outb(0, dev->iobase + PCI171x_CLRINT);
733 devpriv->ai_act_scan = 0;
734 s->async->cur_chan = 0;
735 devpriv->ai_buf_ptr = 0;
736 devpriv->neverending_ai = 0;
742 ==============================================================================
744 static void interrupt_pci1710_every_sample(void *d)
746 struct comedi_device *dev = d;
747 struct pci1710_private *devpriv = dev->private;
748 struct comedi_subdevice *s = &dev->subdevices[0];
750 #ifdef PCI171x_PARANOIDCHECK
751 const struct boardtype *this_board = comedi_board(dev);
755 m = inw(dev->iobase + PCI171x_STATUS);
757 printk("comedi%d: A/D FIFO empty (%4x)\n", dev->minor, m);
758 pci171x_ai_cancel(dev, s);
759 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
760 comedi_event(dev, s);
765 ("comedi%d: A/D FIFO Full status (Fatal Error!) (%4x)\n",
767 pci171x_ai_cancel(dev, s);
768 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
769 comedi_event(dev, s);
773 outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
775 for (; !(inw(dev->iobase + PCI171x_STATUS) & Status_FE);) {
776 #ifdef PCI171x_PARANOIDCHECK
777 sampl = inw(dev->iobase + PCI171x_AD_DATA);
778 if (this_board->cardtype != TYPE_PCI1713)
779 if ((sampl & 0xf000) !=
780 devpriv->act_chanlist[s->async->cur_chan]) {
782 ("comedi: A/D data dropout: received data from channel %d, expected %d!\n",
783 (sampl & 0xf000) >> 12,
786 async->cur_chan] & 0xf000) >>
788 pci171x_ai_cancel(dev, s);
790 COMEDI_CB_EOA | COMEDI_CB_ERROR;
791 comedi_event(dev, s);
794 comedi_buf_put(s->async, sampl & 0x0fff);
796 comedi_buf_put(s->async,
797 inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff);
799 ++s->async->cur_chan;
801 if (s->async->cur_chan >= devpriv->ai_n_chan)
802 s->async->cur_chan = 0;
805 if (s->async->cur_chan == 0) { /* one scan done */
806 devpriv->ai_act_scan++;
807 if ((!devpriv->neverending_ai) &&
808 (devpriv->ai_act_scan >= devpriv->ai_scans)) {
809 /* all data sampled */
810 pci171x_ai_cancel(dev, s);
811 s->async->events |= COMEDI_CB_EOA;
812 comedi_event(dev, s);
818 outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
820 comedi_event(dev, s);
824 ==============================================================================
826 static int move_block_from_fifo(struct comedi_device *dev,
827 struct comedi_subdevice *s, int n, int turn)
829 struct pci1710_private *devpriv = dev->private;
831 #ifdef PCI171x_PARANOIDCHECK
832 const struct boardtype *this_board = comedi_board(dev);
836 j = s->async->cur_chan;
837 for (i = 0; i < n; i++) {
838 #ifdef PCI171x_PARANOIDCHECK
839 sampl = inw(dev->iobase + PCI171x_AD_DATA);
840 if (this_board->cardtype != TYPE_PCI1713)
841 if ((sampl & 0xf000) != devpriv->act_chanlist[j]) {
843 ("comedi%d: A/D FIFO data dropout: received data from channel %d, expected %d! (%d/%d/%d/%d/%d/%4x)\n",
844 dev->minor, (sampl & 0xf000) >> 12,
845 (devpriv->act_chanlist[j] & 0xf000) >> 12,
846 i, j, devpriv->ai_act_scan, n, turn,
848 pci171x_ai_cancel(dev, s);
850 COMEDI_CB_EOA | COMEDI_CB_ERROR;
851 comedi_event(dev, s);
854 comedi_buf_put(s->async, sampl & 0x0fff);
856 comedi_buf_put(s->async,
857 inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff);
860 if (j >= devpriv->ai_n_chan) {
862 devpriv->ai_act_scan++;
865 s->async->cur_chan = j;
870 ==============================================================================
872 static void interrupt_pci1710_half_fifo(void *d)
874 struct comedi_device *dev = d;
875 const struct boardtype *this_board = comedi_board(dev);
876 struct pci1710_private *devpriv = dev->private;
877 struct comedi_subdevice *s = &dev->subdevices[0];
880 m = inw(dev->iobase + PCI171x_STATUS);
881 if (!(m & Status_FH)) {
882 printk("comedi%d: A/D FIFO not half full! (%4x)\n",
884 pci171x_ai_cancel(dev, s);
885 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
886 comedi_event(dev, s);
891 ("comedi%d: A/D FIFO Full status (Fatal Error!) (%4x)\n",
893 pci171x_ai_cancel(dev, s);
894 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
895 comedi_event(dev, s);
899 samplesinbuf = this_board->fifo_half_size;
900 if (samplesinbuf * sizeof(short) >= devpriv->ai_data_len) {
901 m = devpriv->ai_data_len / sizeof(short);
902 if (move_block_from_fifo(dev, s, m, 0))
908 if (move_block_from_fifo(dev, s, samplesinbuf, 1))
912 if (!devpriv->neverending_ai)
913 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data
915 pci171x_ai_cancel(dev, s);
916 s->async->events |= COMEDI_CB_EOA;
917 comedi_event(dev, s);
920 outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
922 comedi_event(dev, s);
926 ==============================================================================
928 static irqreturn_t interrupt_service_pci1710(int irq, void *d)
930 struct comedi_device *dev = d;
931 struct pci1710_private *devpriv = dev->private;
933 if (!dev->attached) /* is device attached? */
934 return IRQ_NONE; /* no, exit */
935 /* is this interrupt from our board? */
936 if (!(inw(dev->iobase + PCI171x_STATUS) & Status_IRQ))
937 return IRQ_NONE; /* no, exit */
939 if (devpriv->ai_et) { /* Switch from initial TRIG_EXT to TRIG_xxx. */
941 devpriv->CntrlReg &= Control_CNT0;
942 devpriv->CntrlReg |= Control_SW; /* set software trigger */
943 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
944 devpriv->CntrlReg = devpriv->ai_et_CntrlReg;
945 outb(0, dev->iobase + PCI171x_CLRFIFO);
946 outb(0, dev->iobase + PCI171x_CLRINT);
947 outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
948 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
950 start_pacer(dev, 1, devpriv->ai_et_div1, devpriv->ai_et_div2);
953 if (devpriv->ai_eos) { /* We use FIFO half full INT or not? */
954 interrupt_pci1710_every_sample(d);
956 interrupt_pci1710_half_fifo(d);
962 ==============================================================================
964 static int pci171x_ai_docmd_and_mode(int mode, struct comedi_device *dev,
965 struct comedi_subdevice *s)
967 const struct boardtype *this_board = comedi_board(dev);
968 struct pci1710_private *devpriv = dev->private;
969 unsigned int divisor1 = 0, divisor2 = 0;
972 start_pacer(dev, -1, 0, 0); /* stop pacer */
974 seglen = check_channel_list(dev, s, devpriv->ai_chanlist,
978 setup_channel_list(dev, s, devpriv->ai_chanlist,
979 devpriv->ai_n_chan, seglen);
981 outb(0, dev->iobase + PCI171x_CLRFIFO);
982 outb(0, dev->iobase + PCI171x_CLRINT);
984 devpriv->ai_do = mode;
986 devpriv->ai_act_scan = 0;
987 s->async->cur_chan = 0;
988 devpriv->ai_buf_ptr = 0;
989 devpriv->neverending_ai = 0;
991 devpriv->CntrlReg &= Control_CNT0;
992 /* don't we want wake up every scan? devpriv->ai_eos=1; */
993 if ((devpriv->ai_flags & TRIG_WAKE_EOS)) {
996 devpriv->CntrlReg |= Control_ONEFH;
1000 if ((devpriv->ai_scans == 0) || (devpriv->ai_scans == -1))
1001 devpriv->neverending_ai = 1;
1002 /* well, user want neverending */
1004 devpriv->neverending_ai = 0;
1009 if (devpriv->ai_timer1 < this_board->ai_ns_min)
1010 devpriv->ai_timer1 = this_board->ai_ns_min;
1011 devpriv->CntrlReg |= Control_PACER | Control_IRQEN;
1013 devpriv->ai_et_CntrlReg = devpriv->CntrlReg;
1014 devpriv->CntrlReg &=
1015 ~(Control_PACER | Control_ONEFH | Control_GATE);
1016 devpriv->CntrlReg |= Control_EXT;
1021 i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
1022 &divisor2, &devpriv->ai_timer1,
1023 devpriv->ai_flags & TRIG_ROUND_MASK);
1024 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
1027 start_pacer(dev, mode, divisor1, divisor2);
1029 devpriv->ai_et_div1 = divisor1;
1030 devpriv->ai_et_div2 = divisor2;
1034 devpriv->CntrlReg |= Control_EXT | Control_IRQEN;
1035 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
1043 ==============================================================================
1045 static int pci171x_ai_cmdtest(struct comedi_device *dev,
1046 struct comedi_subdevice *s,
1047 struct comedi_cmd *cmd)
1049 const struct boardtype *this_board = comedi_board(dev);
1050 struct pci1710_private *devpriv = dev->private;
1053 unsigned int divisor1 = 0, divisor2 = 0;
1055 /* Step 1 : check if triggers are trivially valid */
1057 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
1058 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
1059 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
1060 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
1061 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
1066 /* step 2a: make sure trigger sources are unique */
1068 err |= cfc_check_trigger_is_unique(cmd->start_src);
1069 err |= cfc_check_trigger_is_unique(cmd->convert_src);
1070 err |= cfc_check_trigger_is_unique(cmd->stop_src);
1072 /* step 2b: and mutually compatible */
1077 /* Step 3: check if arguments are trivially valid */
1079 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
1080 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
1082 if (cmd->convert_src == TRIG_TIMER)
1083 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
1084 this_board->ai_ns_min);
1085 else /* TRIG_FOLLOW */
1086 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
1088 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
1090 if (cmd->stop_src == TRIG_COUNT)
1091 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
1092 else /* TRIG_NONE */
1093 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
1098 /* step 4: fix up any arguments */
1100 if (cmd->convert_src == TRIG_TIMER) {
1101 tmp = cmd->convert_arg;
1102 i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
1103 &divisor2, &cmd->convert_arg,
1104 cmd->flags & TRIG_ROUND_MASK);
1105 if (cmd->convert_arg < this_board->ai_ns_min)
1106 cmd->convert_arg = this_board->ai_ns_min;
1107 if (tmp != cmd->convert_arg)
1114 /* step 5: complain about special chanlist considerations */
1116 if (cmd->chanlist) {
1117 if (!check_channel_list(dev, s, cmd->chanlist,
1119 return 5; /* incorrect channels list */
1126 ==============================================================================
1128 static int pci171x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
1130 struct pci1710_private *devpriv = dev->private;
1131 struct comedi_cmd *cmd = &s->async->cmd;
1133 devpriv->ai_n_chan = cmd->chanlist_len;
1134 devpriv->ai_chanlist = cmd->chanlist;
1135 devpriv->ai_flags = cmd->flags;
1136 devpriv->ai_data_len = s->async->prealloc_bufsz;
1137 devpriv->ai_data = s->async->prealloc_buf;
1138 devpriv->ai_timer1 = 0;
1139 devpriv->ai_timer2 = 0;
1141 if (cmd->stop_src == TRIG_COUNT)
1142 devpriv->ai_scans = cmd->stop_arg;
1144 devpriv->ai_scans = 0;
1147 if (cmd->scan_begin_src == TRIG_FOLLOW) { /* mode 1, 2, 3 */
1148 if (cmd->convert_src == TRIG_TIMER) { /* mode 1 and 2 */
1149 devpriv->ai_timer1 = cmd->convert_arg;
1150 return pci171x_ai_docmd_and_mode(cmd->start_src ==
1151 TRIG_EXT ? 2 : 1, dev,
1154 if (cmd->convert_src == TRIG_EXT) { /* mode 3 */
1155 return pci171x_ai_docmd_and_mode(3, dev, s);
1163 ==============================================================================
1165 static int pci171x_reset(struct comedi_device *dev)
1167 const struct boardtype *this_board = comedi_board(dev);
1168 struct pci1710_private *devpriv = dev->private;
1170 outw(0x30, dev->iobase + PCI171x_CNTCTRL);
1171 devpriv->CntrlReg = Control_SW | Control_CNT0; /* Software trigger, CNT0=external */
1172 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL); /* reset any operations */
1173 outb(0, dev->iobase + PCI171x_CLRFIFO); /* clear FIFO */
1174 outb(0, dev->iobase + PCI171x_CLRINT); /* clear INT request */
1175 start_pacer(dev, -1, 0, 0); /* stop 8254 */
1176 devpriv->da_ranges = 0;
1177 if (this_board->n_aochan) {
1178 outb(devpriv->da_ranges, dev->iobase + PCI171x_DAREF); /* set DACs to 0..5V */
1179 outw(0, dev->iobase + PCI171x_DA1); /* set DA outputs to 0V */
1180 devpriv->ao_data[0] = 0x0000;
1181 if (this_board->n_aochan > 1) {
1182 outw(0, dev->iobase + PCI171x_DA2);
1183 devpriv->ao_data[1] = 0x0000;
1186 outw(0, dev->iobase + PCI171x_DO); /* digital outputs to 0 */
1187 outb(0, dev->iobase + PCI171x_CLRFIFO); /* clear FIFO */
1188 outb(0, dev->iobase + PCI171x_CLRINT); /* clear INT request */
1194 ==============================================================================
1196 static int pci1720_reset(struct comedi_device *dev)
1198 struct pci1710_private *devpriv = dev->private;
1200 outb(Syncont_SC0, dev->iobase + PCI1720_SYNCONT); /* set synchronous output mode */
1201 devpriv->da_ranges = 0xAA;
1202 outb(devpriv->da_ranges, dev->iobase + PCI1720_RANGE); /* set all ranges to +/-5V */
1203 outw(0x0800, dev->iobase + PCI1720_DA0); /* set outputs to 0V */
1204 outw(0x0800, dev->iobase + PCI1720_DA1);
1205 outw(0x0800, dev->iobase + PCI1720_DA2);
1206 outw(0x0800, dev->iobase + PCI1720_DA3);
1207 outb(0, dev->iobase + PCI1720_SYNCOUT); /* update outputs */
1208 devpriv->ao_data[0] = 0x0800;
1209 devpriv->ao_data[1] = 0x0800;
1210 devpriv->ao_data[2] = 0x0800;
1211 devpriv->ao_data[3] = 0x0800;
1216 ==============================================================================
1218 static int pci1710_reset(struct comedi_device *dev)
1220 const struct boardtype *this_board = comedi_board(dev);
1222 switch (this_board->cardtype) {
1224 return pci1720_reset(dev);
1226 return pci171x_reset(dev);
1230 static int pci1710_auto_attach(struct comedi_device *dev,
1231 unsigned long context)
1233 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
1234 const struct boardtype *this_board = NULL;
1235 struct pci1710_private *devpriv;
1236 struct comedi_subdevice *s;
1237 int ret, subdev, n_subdevices;
1239 if (context < ARRAY_SIZE(boardtypes))
1240 this_board = &boardtypes[context];
1243 dev->board_ptr = this_board;
1244 dev->board_name = this_board->name;
1246 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
1249 dev->private = devpriv;
1251 ret = comedi_pci_enable(dev);
1254 dev->iobase = pci_resource_start(pcidev, 2);
1257 if (this_board->n_aichan)
1259 if (this_board->n_aochan)
1261 if (this_board->n_dichan)
1263 if (this_board->n_dochan)
1265 if (this_board->n_counter)
1268 ret = comedi_alloc_subdevices(dev, n_subdevices);
1274 if (this_board->have_irq && pcidev->irq) {
1275 ret = request_irq(pcidev->irq, interrupt_service_pci1710,
1276 IRQF_SHARED, dev->board_name, dev);
1278 dev->irq = pcidev->irq;
1283 if (this_board->n_aichan) {
1284 s = &dev->subdevices[subdev];
1285 dev->read_subdev = s;
1286 s->type = COMEDI_SUBD_AI;
1287 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
1288 if (this_board->n_aichand)
1289 s->subdev_flags |= SDF_DIFF;
1290 s->n_chan = this_board->n_aichan;
1291 s->maxdata = this_board->ai_maxdata;
1292 s->len_chanlist = this_board->n_aichan;
1293 s->range_table = this_board->rangelist_ai;
1294 s->cancel = pci171x_ai_cancel;
1295 s->insn_read = pci171x_insn_read_ai;
1297 s->subdev_flags |= SDF_CMD_READ;
1298 s->do_cmdtest = pci171x_ai_cmdtest;
1299 s->do_cmd = pci171x_ai_cmd;
1301 devpriv->i8254_osc_base = 100; /* 100ns=10MHz */
1305 if (this_board->n_aochan) {
1306 s = &dev->subdevices[subdev];
1307 s->type = COMEDI_SUBD_AO;
1308 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1309 s->n_chan = this_board->n_aochan;
1310 s->maxdata = this_board->ao_maxdata;
1311 s->len_chanlist = this_board->n_aochan;
1312 s->range_table = this_board->rangelist_ao;
1313 switch (this_board->cardtype) {
1315 s->insn_write = pci1720_insn_write_ao;
1318 s->insn_write = pci171x_insn_write_ao;
1321 s->insn_read = pci171x_insn_read_ao;
1325 if (this_board->n_dichan) {
1326 s = &dev->subdevices[subdev];
1327 s->type = COMEDI_SUBD_DI;
1328 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
1329 s->n_chan = this_board->n_dichan;
1331 s->len_chanlist = this_board->n_dichan;
1332 s->range_table = &range_digital;
1333 s->io_bits = 0; /* all bits input */
1334 s->insn_bits = pci171x_insn_bits_di;
1338 if (this_board->n_dochan) {
1339 s = &dev->subdevices[subdev];
1340 s->type = COMEDI_SUBD_DO;
1341 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1342 s->n_chan = this_board->n_dochan;
1344 s->len_chanlist = this_board->n_dochan;
1345 s->range_table = &range_digital;
1346 /* all bits output */
1347 s->io_bits = (1 << this_board->n_dochan) - 1;
1349 s->insn_bits = pci171x_insn_bits_do;
1353 if (this_board->n_counter) {
1354 s = &dev->subdevices[subdev];
1355 s->type = COMEDI_SUBD_COUNTER;
1356 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
1357 s->n_chan = this_board->n_counter;
1358 s->len_chanlist = this_board->n_counter;
1359 s->maxdata = 0xffff;
1360 s->range_table = &range_unknown;
1361 s->insn_read = pci171x_insn_counter_read;
1362 s->insn_write = pci171x_insn_counter_write;
1363 s->insn_config = pci171x_insn_counter_config;
1367 dev_info(dev->class_dev, "%s attached, irq %sabled\n",
1368 dev->board_name, dev->irq ? "en" : "dis");
1373 static void pci1710_detach(struct comedi_device *dev)
1378 free_irq(dev->irq, dev);
1379 comedi_pci_disable(dev);
1382 static struct comedi_driver adv_pci1710_driver = {
1383 .driver_name = "adv_pci1710",
1384 .module = THIS_MODULE,
1385 .auto_attach = pci1710_auto_attach,
1386 .detach = pci1710_detach,
1389 static int adv_pci1710_pci_probe(struct pci_dev *dev,
1390 const struct pci_device_id *id)
1392 return comedi_pci_auto_config(dev, &adv_pci1710_driver,
1396 static DEFINE_PCI_DEVICE_TABLE(adv_pci1710_pci_table) = {
1398 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1399 PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050),
1400 .driver_data = BOARD_PCI1710,
1402 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1403 PCI_VENDOR_ID_ADVANTECH, 0x0000),
1404 .driver_data = BOARD_PCI1710,
1406 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1407 PCI_VENDOR_ID_ADVANTECH, 0xb100),
1408 .driver_data = BOARD_PCI1710,
1410 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1411 PCI_VENDOR_ID_ADVANTECH, 0xb200),
1412 .driver_data = BOARD_PCI1710,
1414 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1415 PCI_VENDOR_ID_ADVANTECH, 0xc100),
1416 .driver_data = BOARD_PCI1710,
1418 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1419 PCI_VENDOR_ID_ADVANTECH, 0xc200),
1420 .driver_data = BOARD_PCI1710,
1422 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd100),
1423 .driver_data = BOARD_PCI1710,
1425 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1426 PCI_VENDOR_ID_ADVANTECH, 0x0002),
1427 .driver_data = BOARD_PCI1710HG,
1429 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1430 PCI_VENDOR_ID_ADVANTECH, 0xb102),
1431 .driver_data = BOARD_PCI1710HG,
1433 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1434 PCI_VENDOR_ID_ADVANTECH, 0xb202),
1435 .driver_data = BOARD_PCI1710HG,
1437 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1438 PCI_VENDOR_ID_ADVANTECH, 0xc102),
1439 .driver_data = BOARD_PCI1710HG,
1441 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1442 PCI_VENDOR_ID_ADVANTECH, 0xc202),
1443 .driver_data = BOARD_PCI1710HG,
1445 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd102),
1446 .driver_data = BOARD_PCI1710HG,
1448 { PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 },
1449 { PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 },
1450 { PCI_VDEVICE(ADVANTECH, 0x1720), BOARD_PCI1720 },
1451 { PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 },
1454 MODULE_DEVICE_TABLE(pci, adv_pci1710_pci_table);
1456 static struct pci_driver adv_pci1710_pci_driver = {
1457 .name = "adv_pci1710",
1458 .id_table = adv_pci1710_pci_table,
1459 .probe = adv_pci1710_pci_probe,
1460 .remove = comedi_pci_auto_unconfig,
1462 module_comedi_pci_driver(adv_pci1710_driver, adv_pci1710_pci_driver);
1464 MODULE_AUTHOR("Comedi http://www.comedi.org");
1465 MODULE_DESCRIPTION("Comedi low-level driver");
1466 MODULE_LICENSE("GPL");