1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42#include <linux/module.h>
43#include <linux/delay.h>
44#include <linux/interrupt.h>
45
46#include "../comedi_pci.h"
47
48#include "plx9052.h"
49#include "comedi_8254.h"
50
51#define PCI9111_FIFO_HALF_SIZE 512
52
53#define PCI9111_AI_ACQUISITION_PERIOD_MIN_NS 10000
54
55#define PCI9111_RANGE_SETTING_DELAY 10
56#define PCI9111_AI_INSTANT_READ_UDELAY_US 2
57
58
59
60
61#define PCI9111_AI_FIFO_REG 0x00
62#define PCI9111_AO_REG 0x00
63#define PCI9111_DIO_REG 0x02
64#define PCI9111_EDIO_REG 0x04
65#define PCI9111_AI_CHANNEL_REG 0x06
66#define PCI9111_AI_RANGE_STAT_REG 0x08
67#define PCI9111_AI_STAT_AD_BUSY BIT(7)
68#define PCI9111_AI_STAT_FF_FF BIT(6)
69#define PCI9111_AI_STAT_FF_HF BIT(5)
70#define PCI9111_AI_STAT_FF_EF BIT(4)
71#define PCI9111_AI_RANGE(x) (((x) & 0x7) << 0)
72#define PCI9111_AI_RANGE_MASK PCI9111_AI_RANGE(7)
73#define PCI9111_AI_TRIG_CTRL_REG 0x0a
74#define PCI9111_AI_TRIG_CTRL_TRGEVENT BIT(5)
75#define PCI9111_AI_TRIG_CTRL_POTRG BIT(4)
76#define PCI9111_AI_TRIG_CTRL_PTRG BIT(3)
77#define PCI9111_AI_TRIG_CTRL_ETIS BIT(2)
78#define PCI9111_AI_TRIG_CTRL_TPST BIT(1)
79#define PCI9111_AI_TRIG_CTRL_ASCAN BIT(0)
80#define PCI9111_INT_CTRL_REG 0x0c
81#define PCI9111_INT_CTRL_ISC2 BIT(3)
82#define PCI9111_INT_CTRL_FFEN BIT(2)
83#define PCI9111_INT_CTRL_ISC1 BIT(1)
84#define PCI9111_INT_CTRL_ISC0 BIT(0)
85#define PCI9111_SOFT_TRIG_REG 0x0e
86#define PCI9111_8254_BASE_REG 0x40
87#define PCI9111_INT_CLR_REG 0x48
88
89
90#define PCI9111_LI1_ACTIVE (PLX9052_INTCSR_LI1ENAB | \
91 PLX9052_INTCSR_LI1STAT)
92
93
94#define PCI9111_LI2_ACTIVE (PLX9052_INTCSR_LI2ENAB | \
95 PLX9052_INTCSR_LI2STAT)
96
97static const struct comedi_lrange pci9111_ai_range = {
98 5, {
99 BIP_RANGE(10),
100 BIP_RANGE(5),
101 BIP_RANGE(2.5),
102 BIP_RANGE(1.25),
103 BIP_RANGE(0.625)
104 }
105};
106
107struct pci9111_private_data {
108 unsigned long lcr_io_base;
109
110 unsigned int scan_delay;
111 unsigned int chunk_counter;
112 unsigned int chunk_num_samples;
113
114 unsigned short ai_bounce_buffer[2 * PCI9111_FIFO_HALF_SIZE];
115};
116
117static void plx9050_interrupt_control(unsigned long io_base,
118 bool int1_enable,
119 bool int1_active_high,
120 bool int2_enable,
121 bool int2_active_high,
122 bool interrupt_enable)
123{
124 int flags = 0;
125
126 if (int1_enable)
127 flags |= PLX9052_INTCSR_LI1ENAB;
128 if (int1_active_high)
129 flags |= PLX9052_INTCSR_LI1POL;
130 if (int2_enable)
131 flags |= PLX9052_INTCSR_LI2ENAB;
132 if (int2_active_high)
133 flags |= PLX9052_INTCSR_LI2POL;
134
135 if (interrupt_enable)
136 flags |= PLX9052_INTCSR_PCIENAB;
137
138 outb(flags, io_base + PLX9052_INTCSR);
139}
140
141enum pci9111_ISC0_sources {
142 irq_on_eoc,
143 irq_on_fifo_half_full
144};
145
146enum pci9111_ISC1_sources {
147 irq_on_timer_tick,
148 irq_on_external_trigger
149};
150
151static void pci9111_interrupt_source_set(struct comedi_device *dev,
152 enum pci9111_ISC0_sources irq_0_source,
153 enum pci9111_ISC1_sources irq_1_source)
154{
155 int flags;
156
157
158 flags = inb(dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
159
160 flags >>= 4;
161
162 flags &= 0xc0;
163
164
165 if (irq_0_source == irq_on_fifo_half_full)
166 flags |= PCI9111_INT_CTRL_ISC0;
167
168 if (irq_1_source == irq_on_external_trigger)
169 flags |= PCI9111_INT_CTRL_ISC1;
170
171 outb(flags, dev->iobase + PCI9111_INT_CTRL_REG);
172}
173
174static void pci9111_fifo_reset(struct comedi_device *dev)
175{
176 unsigned long int_ctrl_reg = dev->iobase + PCI9111_INT_CTRL_REG;
177
178
179 outb(0, int_ctrl_reg);
180 outb(PCI9111_INT_CTRL_FFEN, int_ctrl_reg);
181 outb(0, int_ctrl_reg);
182}
183
184static int pci9111_ai_cancel(struct comedi_device *dev,
185 struct comedi_subdevice *s)
186{
187 struct pci9111_private_data *dev_private = dev->private;
188
189
190 plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true,
191 true, false);
192
193
194 outb(0, dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
195
196 pci9111_fifo_reset(dev);
197
198 return 0;
199}
200
201static int pci9111_ai_check_chanlist(struct comedi_device *dev,
202 struct comedi_subdevice *s,
203 struct comedi_cmd *cmd)
204{
205 unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
206 unsigned int aref0 = CR_AREF(cmd->chanlist[0]);
207 int i;
208
209 for (i = 1; i < cmd->chanlist_len; i++) {
210 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
211 unsigned int range = CR_RANGE(cmd->chanlist[i]);
212 unsigned int aref = CR_AREF(cmd->chanlist[i]);
213
214 if (chan != i) {
215 dev_dbg(dev->class_dev,
216 "entries in chanlist must be consecutive channels,counting upwards from 0\n");
217 return -EINVAL;
218 }
219
220 if (range != range0) {
221 dev_dbg(dev->class_dev,
222 "entries in chanlist must all have the same gain\n");
223 return -EINVAL;
224 }
225
226 if (aref != aref0) {
227 dev_dbg(dev->class_dev,
228 "entries in chanlist must all have the same reference\n");
229 return -EINVAL;
230 }
231 }
232
233 return 0;
234}
235
236static int pci9111_ai_do_cmd_test(struct comedi_device *dev,
237 struct comedi_subdevice *s,
238 struct comedi_cmd *cmd)
239{
240 int err = 0;
241 unsigned int arg;
242
243
244
245 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
246 err |= comedi_check_trigger_src(&cmd->scan_begin_src,
247 TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT);
248 err |= comedi_check_trigger_src(&cmd->convert_src,
249 TRIG_TIMER | TRIG_EXT);
250 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
251 err |= comedi_check_trigger_src(&cmd->stop_src,
252 TRIG_COUNT | TRIG_NONE);
253
254 if (err)
255 return 1;
256
257
258
259 err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
260 err |= comedi_check_trigger_is_unique(cmd->convert_src);
261 err |= comedi_check_trigger_is_unique(cmd->stop_src);
262
263
264
265 if (cmd->scan_begin_src != TRIG_FOLLOW) {
266 if (cmd->scan_begin_src != cmd->convert_src)
267 err |= -EINVAL;
268 }
269
270 if (err)
271 return 2;
272
273
274
275 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
276
277 if (cmd->convert_src == TRIG_TIMER) {
278 err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
279 PCI9111_AI_ACQUISITION_PERIOD_MIN_NS);
280 } else {
281 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
282 }
283
284 if (cmd->scan_begin_src == TRIG_TIMER) {
285 err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
286 PCI9111_AI_ACQUISITION_PERIOD_MIN_NS);
287 } else {
288 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
289 }
290
291 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
292 cmd->chanlist_len);
293
294 if (cmd->stop_src == TRIG_COUNT)
295 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
296 else
297 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
298
299 if (err)
300 return 3;
301
302
303
304 if (cmd->convert_src == TRIG_TIMER) {
305 arg = cmd->convert_arg;
306 comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
307 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
308 }
309
310
311
312
313
314 if (cmd->scan_begin_src == TRIG_TIMER) {
315 arg = cmd->chanlist_len * cmd->convert_arg;
316
317 if (arg < cmd->scan_begin_arg)
318 arg *= (cmd->scan_begin_arg / arg);
319
320 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
321 }
322
323 if (err)
324 return 4;
325
326
327 if (cmd->chanlist && cmd->chanlist_len > 0)
328 err |= pci9111_ai_check_chanlist(dev, s, cmd);
329
330 if (err)
331 return 5;
332
333 return 0;
334}
335
336static int pci9111_ai_do_cmd(struct comedi_device *dev,
337 struct comedi_subdevice *s)
338{
339 struct pci9111_private_data *dev_private = dev->private;
340 struct comedi_cmd *cmd = &s->async->cmd;
341 unsigned int last_chan = CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]);
342 unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
343 unsigned int trig = 0;
344
345
346
347
348
349 if (cmd->chanlist_len > 1)
350 trig |= PCI9111_AI_TRIG_CTRL_ASCAN;
351
352 outb(last_chan, dev->iobase + PCI9111_AI_CHANNEL_REG);
353
354
355 outb(PCI9111_AI_RANGE(range0), dev->iobase + PCI9111_AI_RANGE_STAT_REG);
356
357
358 dev_private->scan_delay = 0;
359 if (cmd->convert_src == TRIG_TIMER) {
360 trig |= PCI9111_AI_TRIG_CTRL_TPST;
361 comedi_8254_update_divisors(dev->pacer);
362 comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
363 pci9111_fifo_reset(dev);
364 pci9111_interrupt_source_set(dev, irq_on_fifo_half_full,
365 irq_on_timer_tick);
366 plx9050_interrupt_control(dev_private->lcr_io_base, true, true,
367 false, true, true);
368
369 if (cmd->scan_begin_src == TRIG_TIMER) {
370 dev_private->scan_delay = (cmd->scan_begin_arg /
371 (cmd->convert_arg * cmd->chanlist_len)) - 1;
372 }
373 } else {
374 trig |= PCI9111_AI_TRIG_CTRL_ETIS;
375 pci9111_fifo_reset(dev);
376 pci9111_interrupt_source_set(dev, irq_on_fifo_half_full,
377 irq_on_timer_tick);
378 plx9050_interrupt_control(dev_private->lcr_io_base, true, true,
379 false, true, true);
380 }
381 outb(trig, dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
382
383 dev_private->chunk_counter = 0;
384 dev_private->chunk_num_samples = cmd->chanlist_len *
385 (1 + dev_private->scan_delay);
386
387 return 0;
388}
389
390static void pci9111_ai_munge(struct comedi_device *dev,
391 struct comedi_subdevice *s, void *data,
392 unsigned int num_bytes,
393 unsigned int start_chan_index)
394{
395 unsigned short *array = data;
396 unsigned int maxdata = s->maxdata;
397 unsigned int invert = (maxdata + 1) >> 1;
398 unsigned int shift = (maxdata == 0xffff) ? 0 : 4;
399 unsigned int num_samples = comedi_bytes_to_samples(s, num_bytes);
400 unsigned int i;
401
402 for (i = 0; i < num_samples; i++)
403 array[i] = ((array[i] >> shift) & maxdata) ^ invert;
404}
405
406static void pci9111_handle_fifo_half_full(struct comedi_device *dev,
407 struct comedi_subdevice *s)
408{
409 struct pci9111_private_data *devpriv = dev->private;
410 struct comedi_cmd *cmd = &s->async->cmd;
411 unsigned short *buf = devpriv->ai_bounce_buffer;
412 unsigned int samples;
413
414 samples = comedi_nsamples_left(s, PCI9111_FIFO_HALF_SIZE);
415 insw(dev->iobase + PCI9111_AI_FIFO_REG, buf, samples);
416
417 if (devpriv->scan_delay < 1) {
418 comedi_buf_write_samples(s, buf, samples);
419 } else {
420 unsigned int pos = 0;
421 unsigned int to_read;
422
423 while (pos < samples) {
424 if (devpriv->chunk_counter < cmd->chanlist_len) {
425 to_read = cmd->chanlist_len -
426 devpriv->chunk_counter;
427
428 if (to_read > samples - pos)
429 to_read = samples - pos;
430
431 comedi_buf_write_samples(s, buf + pos, to_read);
432 } else {
433 to_read = devpriv->chunk_num_samples -
434 devpriv->chunk_counter;
435
436 if (to_read > samples - pos)
437 to_read = samples - pos;
438 }
439
440 pos += to_read;
441 devpriv->chunk_counter += to_read;
442
443 if (devpriv->chunk_counter >=
444 devpriv->chunk_num_samples)
445 devpriv->chunk_counter = 0;
446 }
447 }
448}
449
450static irqreturn_t pci9111_interrupt(int irq, void *p_device)
451{
452 struct comedi_device *dev = p_device;
453 struct pci9111_private_data *dev_private = dev->private;
454 struct comedi_subdevice *s = dev->read_subdev;
455 struct comedi_async *async;
456 struct comedi_cmd *cmd;
457 unsigned int status;
458 unsigned long irq_flags;
459 unsigned char intcsr;
460
461 if (!dev->attached) {
462
463
464 return IRQ_NONE;
465 }
466
467 async = s->async;
468 cmd = &async->cmd;
469
470 spin_lock_irqsave(&dev->spinlock, irq_flags);
471
472
473 intcsr = inb(dev_private->lcr_io_base + PLX9052_INTCSR);
474 if (!(((intcsr & PLX9052_INTCSR_PCIENAB) != 0) &&
475 (((intcsr & PCI9111_LI1_ACTIVE) == PCI9111_LI1_ACTIVE) ||
476 ((intcsr & PCI9111_LI2_ACTIVE) == PCI9111_LI2_ACTIVE)))) {
477
478
479 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
480 return IRQ_NONE;
481 }
482
483 if ((intcsr & PCI9111_LI1_ACTIVE) == PCI9111_LI1_ACTIVE) {
484
485
486 status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG);
487
488
489 if (!(status & PCI9111_AI_STAT_FF_FF)) {
490 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
491 dev_dbg(dev->class_dev, "fifo overflow\n");
492 outb(0, dev->iobase + PCI9111_INT_CLR_REG);
493 async->events |= COMEDI_CB_ERROR;
494 comedi_handle_events(dev, s);
495
496 return IRQ_HANDLED;
497 }
498
499
500 if (!(status & PCI9111_AI_STAT_FF_HF))
501 pci9111_handle_fifo_half_full(dev, s);
502 }
503
504 if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg)
505 async->events |= COMEDI_CB_EOA;
506
507 outb(0, dev->iobase + PCI9111_INT_CLR_REG);
508
509 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
510
511 comedi_handle_events(dev, s);
512
513 return IRQ_HANDLED;
514}
515
516static int pci9111_ai_eoc(struct comedi_device *dev,
517 struct comedi_subdevice *s,
518 struct comedi_insn *insn,
519 unsigned long context)
520{
521 unsigned int status;
522
523 status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG);
524 if (status & PCI9111_AI_STAT_FF_EF)
525 return 0;
526 return -EBUSY;
527}
528
529static int pci9111_ai_insn_read(struct comedi_device *dev,
530 struct comedi_subdevice *s,
531 struct comedi_insn *insn, unsigned int *data)
532{
533 unsigned int chan = CR_CHAN(insn->chanspec);
534 unsigned int range = CR_RANGE(insn->chanspec);
535 unsigned int maxdata = s->maxdata;
536 unsigned int invert = (maxdata + 1) >> 1;
537 unsigned int shift = (maxdata == 0xffff) ? 0 : 4;
538 unsigned int status;
539 int ret;
540 int i;
541
542 outb(chan, dev->iobase + PCI9111_AI_CHANNEL_REG);
543
544 status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG);
545 if ((status & PCI9111_AI_RANGE_MASK) != range) {
546 outb(PCI9111_AI_RANGE(range),
547 dev->iobase + PCI9111_AI_RANGE_STAT_REG);
548 }
549
550 pci9111_fifo_reset(dev);
551
552 for (i = 0; i < insn->n; i++) {
553
554 outb(0, dev->iobase + PCI9111_SOFT_TRIG_REG);
555
556 ret = comedi_timeout(dev, s, insn, pci9111_ai_eoc, 0);
557 if (ret) {
558 pci9111_fifo_reset(dev);
559 return ret;
560 }
561
562 data[i] = inw(dev->iobase + PCI9111_AI_FIFO_REG);
563 data[i] = ((data[i] >> shift) & maxdata) ^ invert;
564 }
565
566 return i;
567}
568
569static int pci9111_ao_insn_write(struct comedi_device *dev,
570 struct comedi_subdevice *s,
571 struct comedi_insn *insn,
572 unsigned int *data)
573{
574 unsigned int chan = CR_CHAN(insn->chanspec);
575 unsigned int val = s->readback[chan];
576 int i;
577
578 for (i = 0; i < insn->n; i++) {
579 val = data[i];
580 outw(val, dev->iobase + PCI9111_AO_REG);
581 }
582 s->readback[chan] = val;
583
584 return insn->n;
585}
586
587static int pci9111_di_insn_bits(struct comedi_device *dev,
588 struct comedi_subdevice *s,
589 struct comedi_insn *insn,
590 unsigned int *data)
591{
592 data[1] = inw(dev->iobase + PCI9111_DIO_REG);
593
594 return insn->n;
595}
596
597static int pci9111_do_insn_bits(struct comedi_device *dev,
598 struct comedi_subdevice *s,
599 struct comedi_insn *insn,
600 unsigned int *data)
601{
602 if (comedi_dio_update_state(s, data))
603 outw(s->state, dev->iobase + PCI9111_DIO_REG);
604
605 data[1] = s->state;
606
607 return insn->n;
608}
609
610static int pci9111_reset(struct comedi_device *dev)
611{
612 struct pci9111_private_data *dev_private = dev->private;
613
614
615 plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true,
616 true, false);
617
618
619 outb(0, dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
620
621 return 0;
622}
623
624static int pci9111_auto_attach(struct comedi_device *dev,
625 unsigned long context_unused)
626{
627 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
628 struct pci9111_private_data *dev_private;
629 struct comedi_subdevice *s;
630 int ret;
631
632 dev_private = comedi_alloc_devpriv(dev, sizeof(*dev_private));
633 if (!dev_private)
634 return -ENOMEM;
635
636 ret = comedi_pci_enable(dev);
637 if (ret)
638 return ret;
639 dev_private->lcr_io_base = pci_resource_start(pcidev, 1);
640 dev->iobase = pci_resource_start(pcidev, 2);
641
642 pci9111_reset(dev);
643
644 if (pcidev->irq) {
645 ret = request_irq(pcidev->irq, pci9111_interrupt,
646 IRQF_SHARED, dev->board_name, dev);
647 if (ret == 0)
648 dev->irq = pcidev->irq;
649 }
650
651 dev->pacer = comedi_8254_init(dev->iobase + PCI9111_8254_BASE_REG,
652 I8254_OSC_BASE_2MHZ, I8254_IO16, 0);
653 if (!dev->pacer)
654 return -ENOMEM;
655
656 ret = comedi_alloc_subdevices(dev, 4);
657 if (ret)
658 return ret;
659
660 s = &dev->subdevices[0];
661 s->type = COMEDI_SUBD_AI;
662 s->subdev_flags = SDF_READABLE | SDF_COMMON;
663 s->n_chan = 16;
664 s->maxdata = 0xffff;
665 s->range_table = &pci9111_ai_range;
666 s->insn_read = pci9111_ai_insn_read;
667 if (dev->irq) {
668 dev->read_subdev = s;
669 s->subdev_flags |= SDF_CMD_READ;
670 s->len_chanlist = s->n_chan;
671 s->do_cmdtest = pci9111_ai_do_cmd_test;
672 s->do_cmd = pci9111_ai_do_cmd;
673 s->cancel = pci9111_ai_cancel;
674 s->munge = pci9111_ai_munge;
675 }
676
677 s = &dev->subdevices[1];
678 s->type = COMEDI_SUBD_AO;
679 s->subdev_flags = SDF_WRITABLE | SDF_COMMON;
680 s->n_chan = 1;
681 s->maxdata = 0x0fff;
682 s->len_chanlist = 1;
683 s->range_table = &range_bipolar10;
684 s->insn_write = pci9111_ao_insn_write;
685
686 ret = comedi_alloc_subdev_readback(s);
687 if (ret)
688 return ret;
689
690 s = &dev->subdevices[2];
691 s->type = COMEDI_SUBD_DI;
692 s->subdev_flags = SDF_READABLE;
693 s->n_chan = 16;
694 s->maxdata = 1;
695 s->range_table = &range_digital;
696 s->insn_bits = pci9111_di_insn_bits;
697
698 s = &dev->subdevices[3];
699 s->type = COMEDI_SUBD_DO;
700 s->subdev_flags = SDF_WRITABLE;
701 s->n_chan = 16;
702 s->maxdata = 1;
703 s->range_table = &range_digital;
704 s->insn_bits = pci9111_do_insn_bits;
705
706 return 0;
707}
708
709static void pci9111_detach(struct comedi_device *dev)
710{
711 if (dev->iobase)
712 pci9111_reset(dev);
713 comedi_pci_detach(dev);
714}
715
716static struct comedi_driver adl_pci9111_driver = {
717 .driver_name = "adl_pci9111",
718 .module = THIS_MODULE,
719 .auto_attach = pci9111_auto_attach,
720 .detach = pci9111_detach,
721};
722
723static int pci9111_pci_probe(struct pci_dev *dev,
724 const struct pci_device_id *id)
725{
726 return comedi_pci_auto_config(dev, &adl_pci9111_driver,
727 id->driver_data);
728}
729
730static const struct pci_device_id pci9111_pci_table[] = {
731 { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, 0x9111) },
732
733 { 0 }
734};
735MODULE_DEVICE_TABLE(pci, pci9111_pci_table);
736
737static struct pci_driver adl_pci9111_pci_driver = {
738 .name = "adl_pci9111",
739 .id_table = pci9111_pci_table,
740 .probe = pci9111_pci_probe,
741 .remove = comedi_pci_auto_unconfig,
742};
743module_comedi_pci_driver(adl_pci9111_driver, adl_pci9111_pci_driver);
744
745MODULE_AUTHOR("Comedi http://www.comedi.org");
746MODULE_DESCRIPTION("Comedi low-level driver");
747MODULE_LICENSE("GPL");
748