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
52
53
54
55
56
57
58
59
60
61#include <linux/module.h>
62#include <linux/delay.h>
63#include <linux/interrupt.h>
64#include <linux/slab.h>
65#include "../comedidev.h"
66
67#include <linux/io.h>
68
69#include <asm/dma.h>
70
71#include "8253.h"
72#include "comedi_fc.h"
73
74#define A2150_DMA_BUFFER_SIZE 0xff00
75
76
77#define CONFIG_REG 0x0
78#define CHANNEL_BITS(x) ((x) & 0x7)
79#define CHANNEL_MASK 0x7
80#define CLOCK_SELECT_BITS(x) (((x) & 0x3) << 3)
81#define CLOCK_DIVISOR_BITS(x) (((x) & 0x3) << 5)
82#define CLOCK_MASK (0xf << 3)
83#define ENABLE0_BIT 0x80
84#define ENABLE1_BIT 0x100
85#define AC0_BIT 0x200
86#define AC1_BIT 0x400
87#define APD_BIT 0x800
88#define DPD_BIT 0x1000
89#define TRIGGER_REG 0x2
90#define POST_TRIGGER_BITS 0x2
91#define DELAY_TRIGGER_BITS 0x3
92#define HW_TRIG_EN 0x10
93#define FIFO_START_REG 0x6
94#define FIFO_RESET_REG 0x8
95#define FIFO_DATA_REG 0xa
96#define DMA_TC_CLEAR_REG 0xe
97#define STATUS_REG 0x12
98#define FNE_BIT 0x1
99#define OVFL_BIT 0x8
100#define EDAQ_BIT 0x10
101#define DCAL_BIT 0x20
102#define INTR_BIT 0x40
103#define DMA_TC_BIT 0x80
104#define ID_BITS(x) (((x) >> 8) & 0x3)
105#define IRQ_DMA_CNTRL_REG 0x12
106#define DMA_CHAN_BITS(x) ((x) & 0x7)
107#define DMA_EN_BIT 0x8
108#define IRQ_LVL_BITS(x) (((x) & 0xf) << 4)
109#define FIFO_INTR_EN_BIT 0x100
110#define FIFO_INTR_FHF_BIT 0x200
111#define DMA_INTR_EN_BIT 0x800
112#define DMA_DEM_EN_BIT 0x1000
113#define I8253_BASE_REG 0x14
114#define I8253_MODE_REG 0x17
115#define HW_COUNT_DISABLE 0x30
116
117struct a2150_board {
118 const char *name;
119 int clock[4];
120 int num_clocks;
121 int ai_speed;
122};
123
124
125static const struct comedi_lrange range_a2150 = {
126 1, {
127 BIP_RANGE(2.828)
128 }
129};
130
131
132enum { a2150_c, a2150_s };
133static const struct a2150_board a2150_boards[] = {
134 {
135 .name = "at-a2150c",
136 .clock = {31250, 22676, 20833, 19531},
137 .num_clocks = 4,
138 .ai_speed = 19531,
139 },
140 {
141 .name = "at-a2150s",
142 .clock = {62500, 50000, 41667, 0},
143 .num_clocks = 3,
144 .ai_speed = 41667,
145 },
146};
147
148struct a2150_private {
149
150 volatile unsigned int count;
151 unsigned int dma;
152 uint16_t *dma_buffer;
153 unsigned int dma_transfer_size;
154 int irq_dma_bits;
155 int config_bits;
156};
157
158
159static irqreturn_t a2150_interrupt(int irq, void *d)
160{
161 int i;
162 int status;
163 unsigned long flags;
164 struct comedi_device *dev = d;
165 struct a2150_private *devpriv = dev->private;
166 struct comedi_subdevice *s = dev->read_subdev;
167 struct comedi_async *async;
168 struct comedi_cmd *cmd;
169 unsigned int max_points, num_points, residue, leftover;
170 unsigned short dpnt;
171 static const int sample_size = sizeof(devpriv->dma_buffer[0]);
172
173 if (!dev->attached) {
174 dev_err(dev->class_dev, "premature interrupt\n");
175 return IRQ_HANDLED;
176 }
177
178 async = s->async;
179 cmd = &async->cmd;
180
181 status = inw(dev->iobase + STATUS_REG);
182
183 if ((status & INTR_BIT) == 0) {
184 dev_err(dev->class_dev, "spurious interrupt\n");
185 return IRQ_NONE;
186 }
187
188 if (status & OVFL_BIT) {
189 dev_err(dev->class_dev, "fifo overflow\n");
190 async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
191 cfc_handle_events(dev, s);
192 }
193
194 if ((status & DMA_TC_BIT) == 0) {
195 dev_err(dev->class_dev,
196 "caught non-dma interrupt? Aborting.\n");
197 async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
198 cfc_handle_events(dev, s);
199 return IRQ_HANDLED;
200 }
201
202 flags = claim_dma_lock();
203 disable_dma(devpriv->dma);
204
205
206 clear_dma_ff(devpriv->dma);
207
208
209 max_points = devpriv->dma_transfer_size / sample_size;
210
211
212
213
214 residue = get_dma_residue(devpriv->dma) / sample_size;
215 num_points = max_points - residue;
216 if (devpriv->count < num_points && cmd->stop_src == TRIG_COUNT)
217 num_points = devpriv->count;
218
219
220 leftover = 0;
221 if (cmd->stop_src == TRIG_NONE) {
222 leftover = devpriv->dma_transfer_size / sample_size;
223 } else if (devpriv->count > max_points) {
224 leftover = devpriv->count - max_points;
225 if (leftover > max_points)
226 leftover = max_points;
227 }
228
229
230
231
232 if (residue)
233 leftover = 0;
234
235 for (i = 0; i < num_points; i++) {
236
237 dpnt = devpriv->dma_buffer[i];
238
239 dpnt ^= 0x8000;
240 cfc_write_to_buffer(s, dpnt);
241 if (cmd->stop_src == TRIG_COUNT) {
242 if (--devpriv->count == 0) {
243 async->events |= COMEDI_CB_EOA;
244 break;
245 }
246 }
247 }
248
249 if (leftover) {
250 set_dma_addr(devpriv->dma, virt_to_bus(devpriv->dma_buffer));
251 set_dma_count(devpriv->dma, leftover * sample_size);
252 enable_dma(devpriv->dma);
253 }
254 release_dma_lock(flags);
255
256 async->events |= COMEDI_CB_BLOCK;
257
258 cfc_handle_events(dev, s);
259
260
261 outw(0x00, dev->iobase + DMA_TC_CLEAR_REG);
262
263 return IRQ_HANDLED;
264}
265
266static int a2150_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
267{
268 struct a2150_private *devpriv = dev->private;
269
270
271 devpriv->irq_dma_bits &= ~DMA_INTR_EN_BIT & ~DMA_EN_BIT;
272 outw(devpriv->irq_dma_bits, dev->iobase + IRQ_DMA_CNTRL_REG);
273
274
275 disable_dma(devpriv->dma);
276
277
278 outw(0, dev->iobase + FIFO_RESET_REG);
279
280 return 0;
281}
282
283
284
285
286
287static int a2150_get_timing(struct comedi_device *dev, unsigned int *period,
288 unsigned int flags)
289{
290 const struct a2150_board *thisboard = comedi_board(dev);
291 struct a2150_private *devpriv = dev->private;
292 int lub, glb, temp;
293 int lub_divisor_shift, lub_index, glb_divisor_shift, glb_index;
294 int i, j;
295
296
297 lub_divisor_shift = 3;
298 lub_index = 0;
299 lub = thisboard->clock[lub_index] * (1 << lub_divisor_shift);
300 glb_divisor_shift = 0;
301 glb_index = thisboard->num_clocks - 1;
302 glb = thisboard->clock[glb_index] * (1 << glb_divisor_shift);
303
304
305 if (*period < glb)
306 *period = glb;
307 if (*period > lub)
308 *period = lub;
309
310
311 for (i = 0; i < 4; i++) {
312
313 for (j = 0; j < thisboard->num_clocks; j++) {
314
315 temp = thisboard->clock[j] * (1 << i);
316
317 if (temp < lub && temp >= *period) {
318 lub_divisor_shift = i;
319 lub_index = j;
320 lub = temp;
321 }
322 if (temp > glb && temp <= *period) {
323 glb_divisor_shift = i;
324 glb_index = j;
325 glb = temp;
326 }
327 }
328 }
329 switch (flags & TRIG_ROUND_MASK) {
330 case TRIG_ROUND_NEAREST:
331 default:
332
333 if (lub - *period < *period - glb)
334 *period = lub;
335 else
336 *period = glb;
337 break;
338 case TRIG_ROUND_UP:
339 *period = lub;
340 break;
341 case TRIG_ROUND_DOWN:
342 *period = glb;
343 break;
344 }
345
346
347 devpriv->config_bits &= ~CLOCK_MASK;
348 if (*period == lub) {
349 devpriv->config_bits |=
350 CLOCK_SELECT_BITS(lub_index) |
351 CLOCK_DIVISOR_BITS(lub_divisor_shift);
352 } else {
353 devpriv->config_bits |=
354 CLOCK_SELECT_BITS(glb_index) |
355 CLOCK_DIVISOR_BITS(glb_divisor_shift);
356 }
357
358 return 0;
359}
360
361static int a2150_set_chanlist(struct comedi_device *dev,
362 unsigned int start_channel,
363 unsigned int num_channels)
364{
365 struct a2150_private *devpriv = dev->private;
366
367 if (start_channel + num_channels > 4)
368 return -1;
369
370 devpriv->config_bits &= ~CHANNEL_MASK;
371
372 switch (num_channels) {
373 case 1:
374 devpriv->config_bits |= CHANNEL_BITS(0x4 | start_channel);
375 break;
376 case 2:
377 if (start_channel == 0)
378 devpriv->config_bits |= CHANNEL_BITS(0x2);
379 else if (start_channel == 2)
380 devpriv->config_bits |= CHANNEL_BITS(0x3);
381 else
382 return -1;
383 break;
384 case 4:
385 devpriv->config_bits |= CHANNEL_BITS(0x1);
386 break;
387 default:
388 return -1;
389 }
390
391 return 0;
392}
393
394static int a2150_ai_check_chanlist(struct comedi_device *dev,
395 struct comedi_subdevice *s,
396 struct comedi_cmd *cmd)
397{
398 unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
399 unsigned int aref0 = CR_AREF(cmd->chanlist[0]);
400 int i;
401
402 if (cmd->chanlist_len == 2 && (chan0 == 1 || chan0 == 3)) {
403 dev_dbg(dev->class_dev,
404 "length 2 chanlist must be channels 0,1 or channels 2,3\n");
405 return -EINVAL;
406 }
407
408 if (cmd->chanlist_len == 3) {
409 dev_dbg(dev->class_dev,
410 "chanlist must have 1,2 or 4 channels\n");
411 return -EINVAL;
412 }
413
414 for (i = 1; i < cmd->chanlist_len; i++) {
415 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
416 unsigned int aref = CR_AREF(cmd->chanlist[i]);
417
418 if (chan != (chan0 + i)) {
419 dev_dbg(dev->class_dev,
420 "entries in chanlist must be consecutive channels, counting upwards\n");
421 return -EINVAL;
422 }
423
424 if (chan == 2)
425 aref0 = aref;
426 if (aref != aref0) {
427 dev_dbg(dev->class_dev,
428 "channels 0/1 and 2/3 must have the same analog reference\n");
429 return -EINVAL;
430 }
431 }
432
433 return 0;
434}
435
436static int a2150_ai_cmdtest(struct comedi_device *dev,
437 struct comedi_subdevice *s, struct comedi_cmd *cmd)
438{
439 const struct a2150_board *thisboard = comedi_board(dev);
440 int err = 0;
441 unsigned int arg;
442
443
444
445 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
446 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
447 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
448 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
449 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
450
451 if (err)
452 return 1;
453
454
455
456 err |= cfc_check_trigger_is_unique(cmd->start_src);
457 err |= cfc_check_trigger_is_unique(cmd->stop_src);
458
459
460
461 if (err)
462 return 2;
463
464
465
466 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
467
468 if (cmd->convert_src == TRIG_TIMER)
469 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
470 thisboard->ai_speed);
471
472 err |= cfc_check_trigger_arg_min(&cmd->chanlist_len, 1);
473 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
474
475 if (cmd->stop_src == TRIG_COUNT)
476 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
477 else
478 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
479
480 if (err)
481 return 3;
482
483
484
485 if (cmd->scan_begin_src == TRIG_TIMER) {
486 arg = cmd->scan_begin_arg;
487 a2150_get_timing(dev, &arg, cmd->flags);
488 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
489 }
490
491 if (err)
492 return 4;
493
494
495 if (cmd->chanlist && cmd->chanlist_len > 0)
496 err |= a2150_ai_check_chanlist(dev, s, cmd);
497
498 if (err)
499 return 5;
500
501 return 0;
502}
503
504static int a2150_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
505{
506 struct a2150_private *devpriv = dev->private;
507 struct comedi_async *async = s->async;
508 struct comedi_cmd *cmd = &async->cmd;
509 unsigned long timer_base = dev->iobase + I8253_BASE_REG;
510 unsigned long lock_flags;
511 unsigned int old_config_bits = devpriv->config_bits;
512 unsigned int trigger_bits;
513
514 if (cmd->flags & TRIG_RT) {
515 dev_err(dev->class_dev,
516 "dma incompatible with hard real-time interrupt (TRIG_RT), aborting\n");
517 return -1;
518 }
519
520 outw(0, dev->iobase + FIFO_RESET_REG);
521
522
523 if (a2150_set_chanlist(dev, CR_CHAN(cmd->chanlist[0]),
524 cmd->chanlist_len) < 0)
525 return -1;
526
527
528 if (CR_AREF(cmd->chanlist[0]) == AREF_OTHER)
529 devpriv->config_bits |= AC0_BIT;
530 else
531 devpriv->config_bits &= ~AC0_BIT;
532 if (CR_AREF(cmd->chanlist[2]) == AREF_OTHER)
533 devpriv->config_bits |= AC1_BIT;
534 else
535 devpriv->config_bits &= ~AC1_BIT;
536
537
538 a2150_get_timing(dev, &cmd->scan_begin_arg, cmd->flags);
539
540
541 outw(devpriv->config_bits, dev->iobase + CONFIG_REG);
542
543
544 devpriv->count = cmd->stop_arg * cmd->chanlist_len;
545
546
547 lock_flags = claim_dma_lock();
548 disable_dma(devpriv->dma);
549
550
551 clear_dma_ff(devpriv->dma);
552 set_dma_addr(devpriv->dma, virt_to_bus(devpriv->dma_buffer));
553
554#define ONE_THIRD_SECOND 333333333
555 devpriv->dma_transfer_size =
556 sizeof(devpriv->dma_buffer[0]) * cmd->chanlist_len *
557 ONE_THIRD_SECOND / cmd->scan_begin_arg;
558 if (devpriv->dma_transfer_size > A2150_DMA_BUFFER_SIZE)
559 devpriv->dma_transfer_size = A2150_DMA_BUFFER_SIZE;
560 if (devpriv->dma_transfer_size < sizeof(devpriv->dma_buffer[0]))
561 devpriv->dma_transfer_size = sizeof(devpriv->dma_buffer[0]);
562 devpriv->dma_transfer_size -=
563 devpriv->dma_transfer_size % sizeof(devpriv->dma_buffer[0]);
564 set_dma_count(devpriv->dma, devpriv->dma_transfer_size);
565 enable_dma(devpriv->dma);
566 release_dma_lock(lock_flags);
567
568
569
570 outw(0x00, dev->iobase + DMA_TC_CLEAR_REG);
571
572
573 devpriv->irq_dma_bits |= DMA_INTR_EN_BIT | DMA_EN_BIT;
574 outw(devpriv->irq_dma_bits, dev->iobase + IRQ_DMA_CNTRL_REG);
575
576
577 i8254_set_mode(timer_base, 0, 2, I8254_MODE0 | I8254_BINARY);
578 i8254_write(timer_base, 0, 2, 72);
579
580
581 trigger_bits = 0;
582
583 if (cmd->start_src == TRIG_NOW &&
584 (old_config_bits & CLOCK_MASK) !=
585 (devpriv->config_bits & CLOCK_MASK)) {
586
587 trigger_bits |= DELAY_TRIGGER_BITS;
588 } else {
589
590 trigger_bits |= POST_TRIGGER_BITS;
591 }
592
593 if (cmd->start_src == TRIG_EXT) {
594 trigger_bits |= HW_TRIG_EN;
595 } else if (cmd->start_src == TRIG_OTHER) {
596
597 dev_err(dev->class_dev, "you shouldn't see this?\n");
598 }
599
600 outw(trigger_bits, dev->iobase + TRIGGER_REG);
601
602
603 if (cmd->start_src == TRIG_NOW)
604 outw(0, dev->iobase + FIFO_START_REG);
605
606 return 0;
607}
608
609static int a2150_ai_eoc(struct comedi_device *dev,
610 struct comedi_subdevice *s,
611 struct comedi_insn *insn,
612 unsigned long context)
613{
614 unsigned int status;
615
616 status = inw(dev->iobase + STATUS_REG);
617 if (status & FNE_BIT)
618 return 0;
619 return -EBUSY;
620}
621
622static int a2150_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
623 struct comedi_insn *insn, unsigned int *data)
624{
625 struct a2150_private *devpriv = dev->private;
626 unsigned int n;
627 int ret;
628
629
630 outw(0, dev->iobase + FIFO_RESET_REG);
631
632
633 if (a2150_set_chanlist(dev, CR_CHAN(insn->chanspec), 1) < 0)
634 return -1;
635
636
637 devpriv->config_bits &= ~AC0_BIT;
638 devpriv->config_bits &= ~AC1_BIT;
639
640
641 outw(devpriv->config_bits, dev->iobase + CONFIG_REG);
642
643
644 devpriv->irq_dma_bits &= ~DMA_INTR_EN_BIT & ~DMA_EN_BIT;
645 outw(devpriv->irq_dma_bits, dev->iobase + IRQ_DMA_CNTRL_REG);
646
647
648 outw(0, dev->iobase + TRIGGER_REG);
649
650
651 outw(0, dev->iobase + FIFO_START_REG);
652
653
654
655
656
657 for (n = 0; n < 36; n++) {
658 ret = comedi_timeout(dev, s, insn, a2150_ai_eoc, 0);
659 if (ret)
660 return ret;
661
662 inw(dev->iobase + FIFO_DATA_REG);
663 }
664
665
666 for (n = 0; n < insn->n; n++) {
667 ret = comedi_timeout(dev, s, insn, a2150_ai_eoc, 0);
668 if (ret)
669 return ret;
670
671 data[n] = inw(dev->iobase + FIFO_DATA_REG);
672 data[n] ^= 0x8000;
673 }
674
675
676 outw(0, dev->iobase + FIFO_RESET_REG);
677
678 return n;
679}
680
681
682static int a2150_probe(struct comedi_device *dev)
683{
684 int status = inw(dev->iobase + STATUS_REG);
685
686 return ID_BITS(status);
687}
688
689static int a2150_attach(struct comedi_device *dev, struct comedi_devconfig *it)
690{
691 const struct a2150_board *thisboard;
692 struct a2150_private *devpriv;
693 struct comedi_subdevice *s;
694 unsigned int irq = it->options[1];
695 unsigned int dma = it->options[2];
696 static const int timeout = 2000;
697 int i;
698 int ret;
699
700 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
701 if (!devpriv)
702 return -ENOMEM;
703
704 ret = comedi_request_region(dev, it->options[0], 0x1c);
705 if (ret)
706 return ret;
707
708 dev->board_ptr = a2150_boards + a2150_probe(dev);
709 thisboard = comedi_board(dev);
710 dev->board_name = thisboard->name;
711
712 if ((irq >= 3 && irq <= 7) || (irq >= 9 && irq <= 12) ||
713 irq == 14 || irq == 15) {
714 ret = request_irq(irq, a2150_interrupt, 0,
715 dev->board_name, dev);
716 if (ret == 0) {
717 devpriv->irq_dma_bits |= IRQ_LVL_BITS(irq);
718 dev->irq = irq;
719 }
720 }
721
722 if (dev->irq && dma <= 7 && dma != 4) {
723 ret = request_dma(dma, dev->board_name);
724 if (ret == 0) {
725 devpriv->dma = dma;
726 devpriv->dma_buffer = kmalloc(A2150_DMA_BUFFER_SIZE,
727 GFP_KERNEL | GFP_DMA);
728 if (!devpriv->dma_buffer)
729 return -ENOMEM;
730
731 disable_dma(dma);
732 set_dma_mode(dma, DMA_MODE_READ);
733
734 devpriv->irq_dma_bits |= DMA_CHAN_BITS(dma);
735 }
736 }
737
738 ret = comedi_alloc_subdevices(dev, 1);
739 if (ret)
740 return ret;
741
742
743 s = &dev->subdevices[0];
744 s->type = COMEDI_SUBD_AI;
745 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_OTHER;
746 s->n_chan = 4;
747 s->maxdata = 0xffff;
748 s->range_table = &range_a2150;
749 s->insn_read = a2150_ai_rinsn;
750 if (dev->irq && devpriv->dma) {
751 dev->read_subdev = s;
752 s->subdev_flags |= SDF_CMD_READ;
753 s->len_chanlist = s->n_chan;
754 s->do_cmd = a2150_ai_cmd;
755 s->do_cmdtest = a2150_ai_cmdtest;
756 s->cancel = a2150_cancel;
757 }
758
759
760
761 outw(HW_COUNT_DISABLE, dev->iobase + I8253_MODE_REG);
762
763
764 outw(devpriv->irq_dma_bits, dev->iobase + IRQ_DMA_CNTRL_REG);
765
766
767 outw_p(DPD_BIT | APD_BIT, dev->iobase + CONFIG_REG);
768 outw_p(DPD_BIT, dev->iobase + CONFIG_REG);
769
770 devpriv->config_bits = 0;
771 outw(devpriv->config_bits, dev->iobase + CONFIG_REG);
772
773 for (i = 0; i < timeout; i++) {
774 if ((DCAL_BIT & inw(dev->iobase + STATUS_REG)) == 0)
775 break;
776 udelay(1000);
777 }
778 if (i == timeout) {
779 dev_err(dev->class_dev,
780 "timed out waiting for offset calibration to complete\n");
781 return -ETIME;
782 }
783 devpriv->config_bits |= ENABLE0_BIT | ENABLE1_BIT;
784 outw(devpriv->config_bits, dev->iobase + CONFIG_REG);
785
786 return 0;
787};
788
789static void a2150_detach(struct comedi_device *dev)
790{
791 struct a2150_private *devpriv = dev->private;
792
793 if (dev->iobase)
794 outw(APD_BIT | DPD_BIT, dev->iobase + CONFIG_REG);
795 if (devpriv) {
796 if (devpriv->dma)
797 free_dma(devpriv->dma);
798 kfree(devpriv->dma_buffer);
799 }
800 comedi_legacy_detach(dev);
801};
802
803static struct comedi_driver ni_at_a2150_driver = {
804 .driver_name = "ni_at_a2150",
805 .module = THIS_MODULE,
806 .attach = a2150_attach,
807 .detach = a2150_detach,
808};
809module_comedi_driver(ni_at_a2150_driver);
810
811MODULE_AUTHOR("Comedi http://www.comedi.org");
812MODULE_DESCRIPTION("Comedi low-level driver");
813MODULE_LICENSE("GPL");
814