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#include <linux/interrupt.h>
49#include "../comedidev.h"
50
51#include <linux/ioport.h>
52
53static const char *driver_name = "dt2811";
54
55static const struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = {
56 4, {
57 RANGE(0, 5),
58 RANGE(0, 2.5),
59 RANGE(0, 1.25),
60 RANGE(0, 0.625)
61 }
62};
63
64static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = {
65 4, {
66 RANGE(-2.5, 2.5),
67 RANGE(-1.25, 1.25),
68 RANGE(-0.625, 0.625),
69 RANGE(-0.3125, 0.3125)
70 }
71};
72
73static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = {
74 4, {
75 RANGE(-5, 5),
76 RANGE(-2.5, 2.5),
77 RANGE(-1.25, 1.25),
78 RANGE(-0.625, 0.625)
79 }
80};
81
82static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = {
83 4, {
84 RANGE(0, 5),
85 RANGE(0, 0.5),
86 RANGE(0, 0.05),
87 RANGE(0, 0.01)
88 }
89};
90
91static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = {
92 4, {
93 RANGE(-2.5, 2.5),
94 RANGE(-0.25, 0.25),
95 RANGE(-0.025, 0.025),
96 RANGE(-0.005, 0.005)
97 }
98};
99
100static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = {
101 4, {
102 RANGE(-5, 5),
103 RANGE(-0.5, 0.5),
104 RANGE(-0.05, 0.05),
105 RANGE(-0.01, 0.01)
106 }
107};
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178#define TIMEOUT 10000
179
180#define DT2811_SIZE 8
181
182#define DT2811_ADCSR 0
183#define DT2811_ADGCR 1
184#define DT2811_ADDATLO 2
185#define DT2811_ADDATHI 3
186#define DT2811_DADAT0LO 2
187#define DT2811_DADAT0HI 3
188#define DT2811_DADAT1LO 4
189#define DT2811_DADAT1HI 5
190#define DT2811_DIO 6
191#define DT2811_TMRCTR 7
192
193
194
195
196
197
198
199#define DT2811_ADDONE 0x80
200#define DT2811_ADERROR 0x40
201#define DT2811_ADBUSY 0x20
202#define DT2811_CLRERROR 0x10
203#define DT2811_INTENB 0x04
204#define DT2811_ADMODE 0x03
205
206struct dt2811_board {
207
208 const char *name;
209 const struct comedi_lrange *bip_5;
210 const struct comedi_lrange *bip_2_5;
211 const struct comedi_lrange *unip_5;
212};
213
214enum { card_2811_pgh, card_2811_pgl };
215
216struct dt2811_private {
217 int ntrig;
218 int curadchan;
219 enum {
220 adc_singleended, adc_diff, adc_pseudo_diff
221 } adc_mux;
222 enum {
223 dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
224 } dac_range[2];
225 const struct comedi_lrange *range_type_list[2];
226 unsigned int ao_readback[2];
227};
228
229#define devpriv ((struct dt2811_private *)dev->private)
230
231static const struct comedi_lrange *dac_range_types[] = {
232 &range_bipolar5,
233 &range_bipolar2_5,
234 &range_unipolar5
235};
236
237#define DT2811_TIMEOUT 5
238
239#if 0
240static irqreturn_t dt2811_interrupt(int irq, void *d)
241{
242 int lo, hi;
243 int data;
244 struct comedi_device *dev = d;
245
246 if (!dev->attached) {
247 comedi_error(dev, "spurious interrupt");
248 return IRQ_HANDLED;
249 }
250
251 lo = inb(dev->iobase + DT2811_ADDATLO);
252 hi = inb(dev->iobase + DT2811_ADDATHI);
253
254 data = lo + (hi << 8);
255
256 if (!(--devpriv->ntrig)) {
257
258 s->async->events |= COMEDI_SB_EOA;
259 }
260 comedi_event(dev, s);
261 return IRQ_HANDLED;
262}
263#endif
264
265static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
266 struct comedi_insn *insn, unsigned int *data)
267{
268 int chan = CR_CHAN(insn->chanspec);
269 int timeout = DT2811_TIMEOUT;
270 int i;
271
272 for (i = 0; i < insn->n; i++) {
273 outb(chan, dev->iobase + DT2811_ADGCR);
274
275 while (timeout
276 && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
277 timeout--;
278 if (!timeout)
279 return -ETIME;
280
281 data[i] = inb(dev->iobase + DT2811_ADDATLO);
282 data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
283 data[i] &= 0xfff;
284 }
285
286 return i;
287}
288
289#if 0
290
291
292int dt2811_adtrig(kdev_t minor, comedi_adtrig *adtrig)
293{
294 struct comedi_device *dev = comedi_devices + minor;
295
296 if (adtrig->n < 1)
297 return 0;
298 dev->curadchan = adtrig->chan;
299 switch (dev->i_admode) {
300 case COMEDI_MDEMAND:
301 dev->ntrig = adtrig->n - 1;
302
303
304
305
306 outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
307 do_gettimeofday(&trigtime);
308 break;
309 case COMEDI_MCONTS:
310 dev->ntrig = adtrig->n;
311 break;
312 }
313
314 return 0;
315}
316#endif
317
318static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
319 struct comedi_insn *insn, unsigned int *data)
320{
321 int i;
322 int chan;
323
324 chan = CR_CHAN(insn->chanspec);
325
326 for (i = 0; i < insn->n; i++) {
327 outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
328 outb((data[i] >> 8) & 0xff,
329 dev->iobase + DT2811_DADAT0HI + 2 * chan);
330 devpriv->ao_readback[chan] = data[i];
331 }
332
333 return i;
334}
335
336static int dt2811_ao_insn_read(struct comedi_device *dev,
337 struct comedi_subdevice *s,
338 struct comedi_insn *insn, unsigned int *data)
339{
340 int i;
341 int chan;
342
343 chan = CR_CHAN(insn->chanspec);
344
345 for (i = 0; i < insn->n; i++)
346 data[i] = devpriv->ao_readback[chan];
347
348 return i;
349}
350
351static int dt2811_di_insn_bits(struct comedi_device *dev,
352 struct comedi_subdevice *s,
353 struct comedi_insn *insn, unsigned int *data)
354{
355 data[1] = inb(dev->iobase + DT2811_DIO);
356
357 return insn->n;
358}
359
360static int dt2811_do_insn_bits(struct comedi_device *dev,
361 struct comedi_subdevice *s,
362 struct comedi_insn *insn, unsigned int *data)
363{
364 s->state &= ~data[0];
365 s->state |= data[0] & data[1];
366 outb(s->state, dev->iobase + DT2811_DIO);
367
368 data[1] = s->state;
369
370 return insn->n;
371}
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
394{
395
396
397
398
399 const struct dt2811_board *board = comedi_board(dev);
400 int ret;
401 struct comedi_subdevice *s;
402 unsigned long iobase;
403
404 iobase = it->options[0];
405
406 printk(KERN_INFO "comedi%d: dt2811:base=0x%04lx\n", dev->minor, iobase);
407
408 if (!request_region(iobase, DT2811_SIZE, driver_name)) {
409 printk(KERN_ERR "I/O port conflict\n");
410 return -EIO;
411 }
412
413 dev->iobase = iobase;
414 dev->board_name = board->name;
415
416#if 0
417 outb(0, dev->iobase + DT2811_ADCSR);
418 udelay(100);
419 i = inb(dev->iobase + DT2811_ADDATLO);
420 i = inb(dev->iobase + DT2811_ADDATHI);
421#endif
422
423#if 0
424 irq = it->options[1];
425 if (irq < 0) {
426 save_flags(flags);
427 sti();
428 irqs = probe_irq_on();
429
430 outb(DT2811_CLRERROR | DT2811_INTENB,
431 dev->iobase + DT2811_ADCSR);
432 outb(0, dev->iobase + DT2811_ADGCR);
433
434 udelay(100);
435
436 irq = probe_irq_off(irqs);
437 restore_flags(flags);
438
439
440
441
442 if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR)
443 printk(KERN_ERR "error probing irq (bad)\n");
444 dev->irq = 0;
445 if (irq > 0) {
446 i = inb(dev->iobase + DT2811_ADDATLO);
447 i = inb(dev->iobase + DT2811_ADDATHI);
448 printk(KERN_INFO "(irq = %d)\n", irq);
449 ret = request_irq(irq, dt2811_interrupt, 0,
450 driver_name, dev);
451 if (ret < 0)
452 return -EIO;
453 dev->irq = irq;
454 } else if (irq == 0) {
455 printk(KERN_INFO "(no irq)\n");
456 } else {
457 printk(KERN_ERR "( multiple irq's -- this is bad! )\n");
458 }
459 }
460#endif
461
462 ret = comedi_alloc_subdevices(dev, 4);
463 if (ret)
464 return ret;
465
466 ret = alloc_private(dev, sizeof(struct dt2811_private));
467 if (ret < 0)
468 return ret;
469
470 switch (it->options[2]) {
471 case 0:
472 devpriv->adc_mux = adc_singleended;
473 break;
474 case 1:
475 devpriv->adc_mux = adc_diff;
476 break;
477 case 2:
478 devpriv->adc_mux = adc_pseudo_diff;
479 break;
480 default:
481 devpriv->adc_mux = adc_singleended;
482 break;
483 }
484 switch (it->options[4]) {
485 case 0:
486 devpriv->dac_range[0] = dac_bipolar_5;
487 break;
488 case 1:
489 devpriv->dac_range[0] = dac_bipolar_2_5;
490 break;
491 case 2:
492 devpriv->dac_range[0] = dac_unipolar_5;
493 break;
494 default:
495 devpriv->dac_range[0] = dac_bipolar_5;
496 break;
497 }
498 switch (it->options[5]) {
499 case 0:
500 devpriv->dac_range[1] = dac_bipolar_5;
501 break;
502 case 1:
503 devpriv->dac_range[1] = dac_bipolar_2_5;
504 break;
505 case 2:
506 devpriv->dac_range[1] = dac_unipolar_5;
507 break;
508 default:
509 devpriv->dac_range[1] = dac_bipolar_5;
510 break;
511 }
512
513 s = dev->subdevices + 0;
514
515 s->type = COMEDI_SUBD_AI;
516 s->subdev_flags = SDF_READABLE | SDF_GROUND;
517 s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
518 s->insn_read = dt2811_ai_insn;
519 s->maxdata = 0xfff;
520 switch (it->options[3]) {
521 case 0:
522 default:
523 s->range_table = board->bip_5;
524 break;
525 case 1:
526 s->range_table = board->bip_2_5;
527 break;
528 case 2:
529 s->range_table = board->unip_5;
530 break;
531 }
532
533 s = dev->subdevices + 1;
534
535 s->type = COMEDI_SUBD_AO;
536 s->subdev_flags = SDF_WRITABLE;
537 s->n_chan = 2;
538 s->insn_write = dt2811_ao_insn;
539 s->insn_read = dt2811_ao_insn_read;
540 s->maxdata = 0xfff;
541 s->range_table_list = devpriv->range_type_list;
542 devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
543 devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
544
545 s = dev->subdevices + 2;
546
547 s->type = COMEDI_SUBD_DI;
548 s->subdev_flags = SDF_READABLE;
549 s->n_chan = 8;
550 s->insn_bits = dt2811_di_insn_bits;
551 s->maxdata = 1;
552 s->range_table = &range_digital;
553
554 s = dev->subdevices + 3;
555
556 s->type = COMEDI_SUBD_DO;
557 s->subdev_flags = SDF_WRITABLE;
558 s->n_chan = 8;
559 s->insn_bits = dt2811_do_insn_bits;
560 s->maxdata = 1;
561 s->state = 0;
562 s->range_table = &range_digital;
563
564 return 0;
565}
566
567static void dt2811_detach(struct comedi_device *dev)
568{
569 if (dev->irq)
570 free_irq(dev->irq, dev);
571 if (dev->iobase)
572 release_region(dev->iobase, DT2811_SIZE);
573}
574
575static const struct dt2811_board boardtypes[] = {
576 {
577 .name = "dt2811-pgh",
578 .bip_5 = &range_dt2811_pgh_ai_5_bipolar,
579 .bip_2_5 = &range_dt2811_pgh_ai_2_5_bipolar,
580 .unip_5 = &range_dt2811_pgh_ai_5_unipolar,
581 }, {
582 .name = "dt2811-pgl",
583 .bip_5 = &range_dt2811_pgl_ai_5_bipolar,
584 .bip_2_5 = &range_dt2811_pgl_ai_2_5_bipolar,
585 .unip_5 = &range_dt2811_pgl_ai_5_unipolar,
586 },
587};
588
589static struct comedi_driver dt2811_driver = {
590 .driver_name = "dt2811",
591 .module = THIS_MODULE,
592 .attach = dt2811_attach,
593 .detach = dt2811_detach,
594 .board_name = &boardtypes[0].name,
595 .num_names = ARRAY_SIZE(boardtypes),
596 .offset = sizeof(struct dt2811_board),
597};
598module_comedi_driver(dt2811_driver);
599
600MODULE_AUTHOR("Comedi http://www.comedi.org");
601MODULE_DESCRIPTION("Comedi low-level driver");
602MODULE_LICENSE("GPL");
603