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#define DEBUG 1
57
58#include <linux/pci.h>
59#include <linux/delay.h>
60#include <linux/interrupt.h>
61
62#include "../comedidev.h"
63
64#include "comedi_fc.h"
65
66static const struct comedi_lrange range_dt3000_ai = {
67 4, {
68 BIP_RANGE(10),
69 BIP_RANGE(5),
70 BIP_RANGE(2.5),
71 BIP_RANGE(1.25)
72 }
73};
74
75static const struct comedi_lrange range_dt3000_ai_pgl = {
76 4, {
77 BIP_RANGE(10),
78 BIP_RANGE(1),
79 BIP_RANGE(0.1),
80 BIP_RANGE(0.02)
81 }
82};
83
84enum dt3k_boardid {
85 BOARD_DT3001,
86 BOARD_DT3001_PGL,
87 BOARD_DT3002,
88 BOARD_DT3003,
89 BOARD_DT3003_PGL,
90 BOARD_DT3004,
91 BOARD_DT3005,
92};
93
94struct dt3k_boardtype {
95 const char *name;
96 int adchan;
97 int adbits;
98 int ai_speed;
99 const struct comedi_lrange *adrange;
100 int dachan;
101 int dabits;
102};
103
104static const struct dt3k_boardtype dt3k_boardtypes[] = {
105 [BOARD_DT3001] = {
106 .name = "dt3001",
107 .adchan = 16,
108 .adbits = 12,
109 .adrange = &range_dt3000_ai,
110 .ai_speed = 3000,
111 .dachan = 2,
112 .dabits = 12,
113 },
114 [BOARD_DT3001_PGL] = {
115 .name = "dt3001-pgl",
116 .adchan = 16,
117 .adbits = 12,
118 .adrange = &range_dt3000_ai_pgl,
119 .ai_speed = 3000,
120 .dachan = 2,
121 .dabits = 12,
122 },
123 [BOARD_DT3002] = {
124 .name = "dt3002",
125 .adchan = 32,
126 .adbits = 12,
127 .adrange = &range_dt3000_ai,
128 .ai_speed = 3000,
129 },
130 [BOARD_DT3003] = {
131 .name = "dt3003",
132 .adchan = 64,
133 .adbits = 12,
134 .adrange = &range_dt3000_ai,
135 .ai_speed = 3000,
136 .dachan = 2,
137 .dabits = 12,
138 },
139 [BOARD_DT3003_PGL] = {
140 .name = "dt3003-pgl",
141 .adchan = 64,
142 .adbits = 12,
143 .adrange = &range_dt3000_ai_pgl,
144 .ai_speed = 3000,
145 .dachan = 2,
146 .dabits = 12,
147 },
148 [BOARD_DT3004] = {
149 .name = "dt3004",
150 .adchan = 16,
151 .adbits = 16,
152 .adrange = &range_dt3000_ai,
153 .ai_speed = 10000,
154 .dachan = 2,
155 .dabits = 12,
156 },
157 [BOARD_DT3005] = {
158 .name = "dt3005",
159 .adchan = 16,
160 .adbits = 16,
161 .adrange = &range_dt3000_ai,
162 .ai_speed = 5000,
163 .dachan = 2,
164 .dabits = 12,
165 },
166};
167
168
169
170#define DPR_DAC_buffer (4*0x000)
171#define DPR_ADC_buffer (4*0x800)
172#define DPR_Command (4*0xfd3)
173#define DPR_SubSys (4*0xfd3)
174#define DPR_Encode (4*0xfd4)
175#define DPR_Params(a) (4*(0xfd5+(a)))
176#define DPR_Tick_Reg_Lo (4*0xff5)
177#define DPR_Tick_Reg_Hi (4*0xff6)
178#define DPR_DA_Buf_Front (4*0xff7)
179#define DPR_DA_Buf_Rear (4*0xff8)
180#define DPR_AD_Buf_Front (4*0xff9)
181#define DPR_AD_Buf_Rear (4*0xffa)
182#define DPR_Int_Mask (4*0xffb)
183#define DPR_Intr_Flag (4*0xffc)
184#define DPR_Response_Mbx (4*0xffe)
185#define DPR_Command_Mbx (4*0xfff)
186
187#define AI_FIFO_DEPTH 2003
188#define AO_FIFO_DEPTH 2048
189
190
191
192#define CMD_GETBRDINFO 0
193#define CMD_CONFIG 1
194#define CMD_GETCONFIG 2
195#define CMD_START 3
196#define CMD_STOP 4
197#define CMD_READSINGLE 5
198#define CMD_WRITESINGLE 6
199#define CMD_CALCCLOCK 7
200#define CMD_READEVENTS 8
201#define CMD_WRITECTCTRL 16
202#define CMD_READCTCTRL 17
203#define CMD_WRITECT 18
204#define CMD_READCT 19
205#define CMD_WRITEDATA 32
206#define CMD_READDATA 33
207#define CMD_WRITEIO 34
208#define CMD_READIO 35
209#define CMD_WRITECODE 36
210#define CMD_READCODE 37
211#define CMD_EXECUTE 38
212#define CMD_HALT 48
213
214#define SUBS_AI 0
215#define SUBS_AO 1
216#define SUBS_DIN 2
217#define SUBS_DOUT 3
218#define SUBS_MEM 4
219#define SUBS_CT 5
220
221
222#define DT3000_CMDONE 0x80
223#define DT3000_CTDONE 0x40
224#define DT3000_DAHWERR 0x20
225#define DT3000_DASWERR 0x10
226#define DT3000_DAEMPTY 0x08
227#define DT3000_ADHWERR 0x04
228#define DT3000_ADSWERR 0x02
229#define DT3000_ADFULL 0x01
230
231#define DT3000_COMPLETION_MASK 0xff00
232#define DT3000_COMMAND_MASK 0x00ff
233#define DT3000_NOTPROCESSED 0x0000
234#define DT3000_NOERROR 0x5500
235#define DT3000_ERROR 0xaa00
236#define DT3000_NOTSUPPORTED 0xff00
237
238#define DT3000_EXTERNAL_CLOCK 1
239#define DT3000_RISING_EDGE 2
240
241#define TMODE_MASK 0x1c
242
243#define DT3000_AD_TRIG_INTERNAL (0<<2)
244#define DT3000_AD_TRIG_EXTERNAL (1<<2)
245#define DT3000_AD_RETRIG_INTERNAL (2<<2)
246#define DT3000_AD_RETRIG_EXTERNAL (3<<2)
247#define DT3000_AD_EXTRETRIG (4<<2)
248
249#define DT3000_CHANNEL_MODE_SE 0
250#define DT3000_CHANNEL_MODE_DI 1
251
252struct dt3k_private {
253 void __iomem *io_addr;
254 unsigned int lock;
255 unsigned int ao_readback[2];
256 unsigned int ai_front;
257 unsigned int ai_rear;
258};
259
260#ifdef DEBUG
261static char *intr_flags[] = {
262 "AdFull", "AdSwError", "AdHwError", "DaEmpty",
263 "DaSwError", "DaHwError", "CtDone", "CmDone",
264};
265
266static void debug_intr_flags(unsigned int flags)
267{
268 int i;
269 printk(KERN_DEBUG "dt3k: intr_flags:");
270 for (i = 0; i < 8; i++) {
271 if (flags & (1 << i))
272 printk(KERN_CONT " %s", intr_flags[i]);
273 }
274 printk(KERN_CONT "\n");
275}
276#endif
277
278#define TIMEOUT 100
279
280static void dt3k_send_cmd(struct comedi_device *dev, unsigned int cmd)
281{
282 struct dt3k_private *devpriv = dev->private;
283 int i;
284 unsigned int status = 0;
285
286 writew(cmd, devpriv->io_addr + DPR_Command_Mbx);
287
288 for (i = 0; i < TIMEOUT; i++) {
289 status = readw(devpriv->io_addr + DPR_Command_Mbx);
290 if ((status & DT3000_COMPLETION_MASK) != DT3000_NOTPROCESSED)
291 break;
292 udelay(1);
293 }
294
295 if ((status & DT3000_COMPLETION_MASK) != DT3000_NOERROR)
296 dev_dbg(dev->class_dev, "%s: timeout/error status=0x%04x\n",
297 __func__, status);
298}
299
300static unsigned int dt3k_readsingle(struct comedi_device *dev,
301 unsigned int subsys, unsigned int chan,
302 unsigned int gain)
303{
304 struct dt3k_private *devpriv = dev->private;
305
306 writew(subsys, devpriv->io_addr + DPR_SubSys);
307
308 writew(chan, devpriv->io_addr + DPR_Params(0));
309 writew(gain, devpriv->io_addr + DPR_Params(1));
310
311 dt3k_send_cmd(dev, CMD_READSINGLE);
312
313 return readw(devpriv->io_addr + DPR_Params(2));
314}
315
316static void dt3k_writesingle(struct comedi_device *dev, unsigned int subsys,
317 unsigned int chan, unsigned int data)
318{
319 struct dt3k_private *devpriv = dev->private;
320
321 writew(subsys, devpriv->io_addr + DPR_SubSys);
322
323 writew(chan, devpriv->io_addr + DPR_Params(0));
324 writew(0, devpriv->io_addr + DPR_Params(1));
325 writew(data, devpriv->io_addr + DPR_Params(2));
326
327 dt3k_send_cmd(dev, CMD_WRITESINGLE);
328}
329
330static void dt3k_ai_empty_fifo(struct comedi_device *dev,
331 struct comedi_subdevice *s)
332{
333 struct dt3k_private *devpriv = dev->private;
334 int front;
335 int rear;
336 int count;
337 int i;
338 short data;
339
340 front = readw(devpriv->io_addr + DPR_AD_Buf_Front);
341 count = front - devpriv->ai_front;
342 if (count < 0)
343 count += AI_FIFO_DEPTH;
344
345 rear = devpriv->ai_rear;
346
347 for (i = 0; i < count; i++) {
348 data = readw(devpriv->io_addr + DPR_ADC_buffer + rear);
349 comedi_buf_put(s->async, data);
350 rear++;
351 if (rear >= AI_FIFO_DEPTH)
352 rear = 0;
353 }
354
355 devpriv->ai_rear = rear;
356 writew(rear, devpriv->io_addr + DPR_AD_Buf_Rear);
357}
358
359static int dt3k_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
360{
361 struct dt3k_private *devpriv = dev->private;
362
363 writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
364 dt3k_send_cmd(dev, CMD_STOP);
365
366 writew(0, devpriv->io_addr + DPR_Int_Mask);
367
368 return 0;
369}
370
371static int debug_n_ints;
372
373
374
375static irqreturn_t dt3k_interrupt(int irq, void *d)
376{
377 struct comedi_device *dev = d;
378 struct dt3k_private *devpriv = dev->private;
379 struct comedi_subdevice *s;
380 unsigned int status;
381
382 if (!dev->attached)
383 return IRQ_NONE;
384
385 s = &dev->subdevices[0];
386 status = readw(devpriv->io_addr + DPR_Intr_Flag);
387#ifdef DEBUG
388 debug_intr_flags(status);
389#endif
390
391 if (status & DT3000_ADFULL) {
392 dt3k_ai_empty_fifo(dev, s);
393 s->async->events |= COMEDI_CB_BLOCK;
394 }
395
396 if (status & (DT3000_ADSWERR | DT3000_ADHWERR))
397 s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
398
399 debug_n_ints++;
400 if (debug_n_ints >= 10) {
401 dt3k_ai_cancel(dev, s);
402 s->async->events |= COMEDI_CB_EOA;
403 }
404
405 comedi_event(dev, s);
406 return IRQ_HANDLED;
407}
408
409static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *nanosec,
410 unsigned int round_mode)
411{
412 int divider, base, prescale;
413
414
415
416
417 for (prescale = 0; prescale < 16; prescale++) {
418 base = timer_base * (prescale + 1);
419 switch (round_mode) {
420 case TRIG_ROUND_NEAREST:
421 default:
422 divider = (*nanosec + base / 2) / base;
423 break;
424 case TRIG_ROUND_DOWN:
425 divider = (*nanosec) / base;
426 break;
427 case TRIG_ROUND_UP:
428 divider = (*nanosec) / base;
429 break;
430 }
431 if (divider < 65536) {
432 *nanosec = divider * base;
433 return (prescale << 16) | (divider);
434 }
435 }
436
437 prescale = 15;
438 base = timer_base * (1 << prescale);
439 divider = 65535;
440 *nanosec = divider * base;
441 return (prescale << 16) | (divider);
442}
443
444static int dt3k_ai_cmdtest(struct comedi_device *dev,
445 struct comedi_subdevice *s, struct comedi_cmd *cmd)
446{
447 const struct dt3k_boardtype *this_board = comedi_board(dev);
448 int err = 0;
449 int tmp;
450
451
452
453 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
454 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
455 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
456 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
457 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT);
458
459 if (err)
460 return 1;
461
462
463
464
465 if (err)
466 return 2;
467
468
469
470 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
471
472 if (cmd->scan_begin_src == TRIG_TIMER) {
473 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
474 this_board->ai_speed);
475 err |= cfc_check_trigger_arg_max(&cmd->scan_begin_arg,
476 100 * 16 * 65535);
477 }
478
479 if (cmd->convert_src == TRIG_TIMER) {
480 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
481 this_board->ai_speed);
482 err |= cfc_check_trigger_arg_max(&cmd->convert_arg,
483 50 * 16 * 65535);
484 }
485
486 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
487
488 if (cmd->stop_src == TRIG_COUNT)
489 err |= cfc_check_trigger_arg_max(&cmd->stop_arg, 0x00ffffff);
490 else
491 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
492
493 if (err)
494 return 3;
495
496
497
498 if (cmd->scan_begin_src == TRIG_TIMER) {
499 tmp = cmd->scan_begin_arg;
500 dt3k_ns_to_timer(100, &cmd->scan_begin_arg,
501 cmd->flags & TRIG_ROUND_MASK);
502 if (tmp != cmd->scan_begin_arg)
503 err++;
504 }
505
506 if (cmd->convert_src == TRIG_TIMER) {
507 tmp = cmd->convert_arg;
508 dt3k_ns_to_timer(50, &cmd->convert_arg,
509 cmd->flags & TRIG_ROUND_MASK);
510 if (tmp != cmd->convert_arg)
511 err++;
512 if (cmd->scan_begin_src == TRIG_TIMER &&
513 cmd->scan_begin_arg <
514 cmd->convert_arg * cmd->scan_end_arg) {
515 cmd->scan_begin_arg =
516 cmd->convert_arg * cmd->scan_end_arg;
517 err++;
518 }
519 }
520
521 if (err)
522 return 4;
523
524 return 0;
525}
526
527static int dt3k_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
528{
529 struct dt3k_private *devpriv = dev->private;
530 struct comedi_cmd *cmd = &s->async->cmd;
531 int i;
532 unsigned int chan, range, aref;
533 unsigned int divider;
534 unsigned int tscandiv;
535 unsigned int mode;
536
537 for (i = 0; i < cmd->chanlist_len; i++) {
538 chan = CR_CHAN(cmd->chanlist[i]);
539 range = CR_RANGE(cmd->chanlist[i]);
540
541 writew((range << 6) | chan,
542 devpriv->io_addr + DPR_ADC_buffer + i);
543 }
544 aref = CR_AREF(cmd->chanlist[0]);
545
546 writew(cmd->scan_end_arg, devpriv->io_addr + DPR_Params(0));
547
548 if (cmd->convert_src == TRIG_TIMER) {
549 divider = dt3k_ns_to_timer(50, &cmd->convert_arg,
550 cmd->flags & TRIG_ROUND_MASK);
551 writew((divider >> 16), devpriv->io_addr + DPR_Params(1));
552 writew((divider & 0xffff), devpriv->io_addr + DPR_Params(2));
553 }
554
555 if (cmd->scan_begin_src == TRIG_TIMER) {
556 tscandiv = dt3k_ns_to_timer(100, &cmd->scan_begin_arg,
557 cmd->flags & TRIG_ROUND_MASK);
558 writew((tscandiv >> 16), devpriv->io_addr + DPR_Params(3));
559 writew((tscandiv & 0xffff), devpriv->io_addr + DPR_Params(4));
560 }
561
562 mode = DT3000_AD_RETRIG_INTERNAL | 0 | 0;
563 writew(mode, devpriv->io_addr + DPR_Params(5));
564 writew(aref == AREF_DIFF, devpriv->io_addr + DPR_Params(6));
565
566 writew(AI_FIFO_DEPTH / 2, devpriv->io_addr + DPR_Params(7));
567
568 writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
569 dt3k_send_cmd(dev, CMD_CONFIG);
570
571 writew(DT3000_ADFULL | DT3000_ADSWERR | DT3000_ADHWERR,
572 devpriv->io_addr + DPR_Int_Mask);
573
574 debug_n_ints = 0;
575
576 writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
577 dt3k_send_cmd(dev, CMD_START);
578
579 return 0;
580}
581
582static int dt3k_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
583 struct comedi_insn *insn, unsigned int *data)
584{
585 int i;
586 unsigned int chan, gain, aref;
587
588 chan = CR_CHAN(insn->chanspec);
589 gain = CR_RANGE(insn->chanspec);
590
591 aref = CR_AREF(insn->chanspec);
592
593 for (i = 0; i < insn->n; i++)
594 data[i] = dt3k_readsingle(dev, SUBS_AI, chan, gain);
595
596 return i;
597}
598
599static int dt3k_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
600 struct comedi_insn *insn, unsigned int *data)
601{
602 struct dt3k_private *devpriv = dev->private;
603 int i;
604 unsigned int chan;
605
606 chan = CR_CHAN(insn->chanspec);
607 for (i = 0; i < insn->n; i++) {
608 dt3k_writesingle(dev, SUBS_AO, chan, data[i]);
609 devpriv->ao_readback[chan] = data[i];
610 }
611
612 return i;
613}
614
615static int dt3k_ao_insn_read(struct comedi_device *dev,
616 struct comedi_subdevice *s,
617 struct comedi_insn *insn, unsigned int *data)
618{
619 struct dt3k_private *devpriv = dev->private;
620 int i;
621 unsigned int chan;
622
623 chan = CR_CHAN(insn->chanspec);
624 for (i = 0; i < insn->n; i++)
625 data[i] = devpriv->ao_readback[chan];
626
627 return i;
628}
629
630static void dt3k_dio_config(struct comedi_device *dev, int bits)
631{
632 struct dt3k_private *devpriv = dev->private;
633
634
635 writew(SUBS_DOUT, devpriv->io_addr + DPR_SubSys);
636
637 writew(bits, devpriv->io_addr + DPR_Params(0));
638#if 0
639
640 writew(0, devpriv->io_addr + DPR_Params(1));
641 writew(0, devpriv->io_addr + DPR_Params(2));
642#endif
643
644 dt3k_send_cmd(dev, CMD_CONFIG);
645}
646
647static int dt3k_dio_insn_config(struct comedi_device *dev,
648 struct comedi_subdevice *s,
649 struct comedi_insn *insn, unsigned int *data)
650{
651 int mask;
652
653 mask = (CR_CHAN(insn->chanspec) < 4) ? 0x0f : 0xf0;
654
655 switch (data[0]) {
656 case INSN_CONFIG_DIO_OUTPUT:
657 s->io_bits |= mask;
658 break;
659 case INSN_CONFIG_DIO_INPUT:
660 s->io_bits &= ~mask;
661 break;
662 case INSN_CONFIG_DIO_QUERY:
663 data[1] =
664 (s->
665 io_bits & (1 << CR_CHAN(insn->chanspec))) ? COMEDI_OUTPUT :
666 COMEDI_INPUT;
667 return insn->n;
668 break;
669 default:
670 return -EINVAL;
671 break;
672 }
673 mask = (s->io_bits & 0x01) | ((s->io_bits & 0x10) >> 3);
674 dt3k_dio_config(dev, mask);
675
676 return insn->n;
677}
678
679static int dt3k_dio_insn_bits(struct comedi_device *dev,
680 struct comedi_subdevice *s,
681 struct comedi_insn *insn, unsigned int *data)
682{
683 if (data[0]) {
684 s->state &= ~data[0];
685 s->state |= data[1] & data[0];
686 dt3k_writesingle(dev, SUBS_DOUT, 0, s->state);
687 }
688 data[1] = dt3k_readsingle(dev, SUBS_DIN, 0, 0);
689
690 return insn->n;
691}
692
693static int dt3k_mem_insn_read(struct comedi_device *dev,
694 struct comedi_subdevice *s,
695 struct comedi_insn *insn, unsigned int *data)
696{
697 struct dt3k_private *devpriv = dev->private;
698 unsigned int addr = CR_CHAN(insn->chanspec);
699 int i;
700
701 for (i = 0; i < insn->n; i++) {
702 writew(SUBS_MEM, devpriv->io_addr + DPR_SubSys);
703 writew(addr, devpriv->io_addr + DPR_Params(0));
704 writew(1, devpriv->io_addr + DPR_Params(1));
705
706 dt3k_send_cmd(dev, CMD_READCODE);
707
708 data[i] = readw(devpriv->io_addr + DPR_Params(2));
709 }
710
711 return i;
712}
713
714static int dt3000_auto_attach(struct comedi_device *dev,
715 unsigned long context)
716{
717 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
718 const struct dt3k_boardtype *this_board = NULL;
719 struct dt3k_private *devpriv;
720 struct comedi_subdevice *s;
721 int ret = 0;
722
723 if (context < ARRAY_SIZE(dt3k_boardtypes))
724 this_board = &dt3k_boardtypes[context];
725 if (!this_board)
726 return -ENODEV;
727 dev->board_ptr = this_board;
728 dev->board_name = this_board->name;
729
730 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
731 if (!devpriv)
732 return -ENOMEM;
733 dev->private = devpriv;
734
735 ret = comedi_pci_enable(dev);
736 if (ret < 0)
737 return ret;
738
739 devpriv->io_addr = pci_ioremap_bar(pcidev, 0);
740 if (!devpriv->io_addr)
741 return -ENOMEM;
742
743 ret = request_irq(pcidev->irq, dt3k_interrupt, IRQF_SHARED,
744 dev->board_name, dev);
745 if (ret)
746 return ret;
747 dev->irq = pcidev->irq;
748
749 ret = comedi_alloc_subdevices(dev, 4);
750 if (ret)
751 return ret;
752
753 s = &dev->subdevices[0];
754 dev->read_subdev = s;
755
756 s->type = COMEDI_SUBD_AI;
757 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
758 s->n_chan = this_board->adchan;
759 s->insn_read = dt3k_ai_insn;
760 s->maxdata = (1 << this_board->adbits) - 1;
761 s->len_chanlist = 512;
762 s->range_table = &range_dt3000_ai;
763 s->do_cmd = dt3k_ai_cmd;
764 s->do_cmdtest = dt3k_ai_cmdtest;
765 s->cancel = dt3k_ai_cancel;
766
767 s = &dev->subdevices[1];
768
769 s->type = COMEDI_SUBD_AO;
770 s->subdev_flags = SDF_WRITABLE;
771 s->n_chan = 2;
772 s->insn_read = dt3k_ao_insn_read;
773 s->insn_write = dt3k_ao_insn;
774 s->maxdata = (1 << this_board->dabits) - 1;
775 s->len_chanlist = 1;
776 s->range_table = &range_bipolar10;
777
778 s = &dev->subdevices[2];
779
780 s->type = COMEDI_SUBD_DIO;
781 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
782 s->n_chan = 8;
783 s->insn_config = dt3k_dio_insn_config;
784 s->insn_bits = dt3k_dio_insn_bits;
785 s->maxdata = 1;
786 s->len_chanlist = 8;
787 s->range_table = &range_digital;
788
789 s = &dev->subdevices[3];
790
791 s->type = COMEDI_SUBD_MEMORY;
792 s->subdev_flags = SDF_READABLE;
793 s->n_chan = 0x1000;
794 s->insn_read = dt3k_mem_insn_read;
795 s->maxdata = 0xff;
796 s->len_chanlist = 1;
797 s->range_table = &range_unknown;
798
799#if 0
800 s = &dev->subdevices[4];
801
802 s->type = COMEDI_SUBD_PROC;
803#endif
804
805 dev_info(dev->class_dev, "%s attached\n", dev->board_name);
806
807 return 0;
808}
809
810static void dt3000_detach(struct comedi_device *dev)
811{
812 struct dt3k_private *devpriv = dev->private;
813
814 if (dev->irq)
815 free_irq(dev->irq, dev);
816 if (devpriv) {
817 if (devpriv->io_addr)
818 iounmap(devpriv->io_addr);
819 }
820 comedi_pci_disable(dev);
821}
822
823static struct comedi_driver dt3000_driver = {
824 .driver_name = "dt3000",
825 .module = THIS_MODULE,
826 .auto_attach = dt3000_auto_attach,
827 .detach = dt3000_detach,
828};
829
830static int dt3000_pci_probe(struct pci_dev *dev,
831 const struct pci_device_id *id)
832{
833 return comedi_pci_auto_config(dev, &dt3000_driver, id->driver_data);
834}
835
836static DEFINE_PCI_DEVICE_TABLE(dt3000_pci_table) = {
837 { PCI_VDEVICE(DT, 0x0022), BOARD_DT3001 },
838 { PCI_VDEVICE(DT, 0x0023), BOARD_DT3002 },
839 { PCI_VDEVICE(DT, 0x0024), BOARD_DT3003 },
840 { PCI_VDEVICE(DT, 0x0025), BOARD_DT3004 },
841 { PCI_VDEVICE(DT, 0x0026), BOARD_DT3005 },
842 { PCI_VDEVICE(DT, 0x0027), BOARD_DT3001_PGL },
843 { PCI_VDEVICE(DT, 0x0028), BOARD_DT3003_PGL },
844 { 0 }
845};
846MODULE_DEVICE_TABLE(pci, dt3000_pci_table);
847
848static struct pci_driver dt3000_pci_driver = {
849 .name = "dt3000",
850 .id_table = dt3000_pci_table,
851 .probe = dt3000_pci_probe,
852 .remove = comedi_pci_auto_unconfig,
853};
854module_comedi_pci_driver(dt3000_driver, dt3000_pci_driver);
855
856MODULE_AUTHOR("Comedi http://www.comedi.org");
857MODULE_DESCRIPTION("Comedi low-level driver");
858MODULE_LICENSE("GPL");
859