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