Commit | Line | Data |
---|---|---|
a4c87948 DS |
1 | /* |
2 | * comedi/drivers/dt2801.c | |
3 | * Device Driver for DataTranslation DT2801 | |
4 | * | |
5 | */ | |
6 | /* | |
7 | Driver: dt2801 | |
8 | Description: Data Translation DT2801 series and DT01-EZ | |
9 | Author: ds | |
10 | Status: works | |
11 | Devices: [Data Translation] DT2801 (dt2801), DT2801-A, DT2801/5716A, | |
12 | DT2805, DT2805/5716A, DT2808, DT2818, DT2809, DT01-EZ | |
13 | ||
14 | This driver can autoprobe the type of board. | |
15 | ||
16 | Configuration options: | |
17 | [0] - I/O port base address | |
18 | [1] - unused | |
19 | [2] - A/D reference 0=differential, 1=single-ended | |
20 | [3] - A/D range | |
21 | 0 = [-10,10] | |
22 | 1 = [0,10] | |
23 | [4] - D/A 0 range | |
24 | 0 = [-10,10] | |
25 | 1 = [-5,5] | |
26 | 2 = [-2.5,2.5] | |
27 | 3 = [0,10] | |
28 | 4 = [0,5] | |
29 | [5] - D/A 1 range (same choices) | |
30 | */ | |
31 | ||
32 | #include "../comedidev.h" | |
33 | #include <linux/delay.h> | |
34 | #include <linux/ioport.h> | |
35 | ||
36 | #define DT2801_TIMEOUT 1000 | |
37 | ||
38 | /* Hardware Configuration */ | |
39 | /* ====================== */ | |
40 | ||
41 | #define DT2801_MAX_DMA_SIZE (64 * 1024) | |
42 | ||
43 | /* Ports */ | |
44 | #define DT2801_IOSIZE 2 | |
45 | ||
46 | /* define's & typedef's */ | |
47 | /* ====================== */ | |
48 | ||
49 | /* Commands */ | |
50 | #define DT_C_RESET 0x0 | |
51 | #define DT_C_CLEAR_ERR 0x1 | |
52 | #define DT_C_READ_ERRREG 0x2 | |
53 | #define DT_C_SET_CLOCK 0x3 | |
54 | ||
55 | #define DT_C_TEST 0xb | |
56 | #define DT_C_STOP 0xf | |
57 | ||
58 | #define DT_C_SET_DIGIN 0x4 | |
59 | #define DT_C_SET_DIGOUT 0x5 | |
60 | #define DT_C_READ_DIG 0x6 | |
61 | #define DT_C_WRITE_DIG 0x7 | |
62 | ||
63 | #define DT_C_WRITE_DAIM 0x8 | |
64 | #define DT_C_SET_DA 0x9 | |
65 | #define DT_C_WRITE_DA 0xa | |
66 | ||
67 | #define DT_C_READ_ADIM 0xc | |
68 | #define DT_C_SET_AD 0xd | |
69 | #define DT_C_READ_AD 0xe | |
70 | ||
71 | /* Command modifiers (only used with read/write), EXTTRIG can be | |
72 | used with some other commands. | |
73 | */ | |
74 | #define DT_MOD_DMA (1<<4) | |
75 | #define DT_MOD_CONT (1<<5) | |
76 | #define DT_MOD_EXTCLK (1<<6) | |
77 | #define DT_MOD_EXTTRIG (1<<7) | |
78 | ||
79 | /* Bits in status register */ | |
80 | #define DT_S_DATA_OUT_READY (1<<0) | |
81 | #define DT_S_DATA_IN_FULL (1<<1) | |
82 | #define DT_S_READY (1<<2) | |
83 | #define DT_S_COMMAND (1<<3) | |
84 | #define DT_S_COMPOSITE_ERROR (1<<7) | |
85 | ||
86 | /* registers */ | |
87 | #define DT2801_DATA 0 | |
88 | #define DT2801_STATUS 1 | |
89 | #define DT2801_CMD 1 | |
90 | ||
91 | static int dt2801_attach(comedi_device * dev, comedi_devconfig * it); | |
92 | static int dt2801_detach(comedi_device * dev); | |
93 | static comedi_driver driver_dt2801 = { | |
94 | driver_name:"dt2801", | |
95 | module:THIS_MODULE, | |
96 | attach:dt2801_attach, | |
97 | detach:dt2801_detach, | |
98 | }; | |
99 | ||
100 | COMEDI_INITCLEANUP(driver_dt2801); | |
101 | ||
102 | #if 0 | |
103 | // ignore 'defined but not used' warning | |
104 | static const comedi_lrange range_dt2801_ai_pgh_bipolar = { 4, { | |
105 | RANGE(-10, 10), | |
106 | RANGE(-5, 5), | |
107 | RANGE(-2.5, 2.5), | |
108 | RANGE(-1.25, 1.25), | |
109 | } | |
110 | }; | |
111 | #endif | |
112 | static const comedi_lrange range_dt2801_ai_pgl_bipolar = { 4, { | |
113 | RANGE(-10, 10), | |
114 | RANGE(-1, 1), | |
115 | RANGE(-0.1, 0.1), | |
116 | RANGE(-0.02, 0.02), | |
117 | } | |
118 | }; | |
119 | ||
120 | #if 0 | |
121 | // ignore 'defined but not used' warning | |
122 | static const comedi_lrange range_dt2801_ai_pgh_unipolar = { 4, { | |
123 | RANGE(0, 10), | |
124 | RANGE(0, 5), | |
125 | RANGE(0, 2.5), | |
126 | RANGE(0, 1.25), | |
127 | } | |
128 | }; | |
129 | #endif | |
130 | static const comedi_lrange range_dt2801_ai_pgl_unipolar = { 4, { | |
131 | RANGE(0, 10), | |
132 | RANGE(0, 1), | |
133 | RANGE(0, 0.1), | |
134 | RANGE(0, 0.02), | |
135 | } | |
136 | }; | |
137 | ||
138 | typedef struct { | |
139 | const char *name; | |
140 | int boardcode; | |
141 | int ad_diff; | |
142 | int ad_chan; | |
143 | int adbits; | |
144 | int adrangetype; | |
145 | int dabits; | |
146 | } boardtype_t; | |
147 | ||
148 | /* Typeid's for the different boards of the DT2801-series | |
149 | (taken from the test-software, that comes with the board) | |
150 | */ | |
151 | static const boardtype_t boardtypes[] = { | |
152 | { | |
153 | name: "dt2801", | |
154 | boardcode:0x09, | |
155 | ad_diff: 2, | |
156 | ad_chan: 16, | |
157 | adbits: 12, | |
158 | adrangetype:0, | |
159 | dabits: 12}, | |
160 | { | |
161 | name: "dt2801-a", | |
162 | boardcode:0x52, | |
163 | ad_diff: 2, | |
164 | ad_chan: 16, | |
165 | adbits: 12, | |
166 | adrangetype:0, | |
167 | dabits: 12}, | |
168 | { | |
169 | name: "dt2801/5716a", | |
170 | boardcode:0x82, | |
171 | ad_diff: 1, | |
172 | ad_chan: 16, | |
173 | adbits: 16, | |
174 | adrangetype:1, | |
175 | dabits: 12}, | |
176 | { | |
177 | name: "dt2805", | |
178 | boardcode:0x12, | |
179 | ad_diff: 1, | |
180 | ad_chan: 16, | |
181 | adbits: 12, | |
182 | adrangetype:0, | |
183 | dabits: 12}, | |
184 | { | |
185 | name: "dt2805/5716a", | |
186 | boardcode:0x92, | |
187 | ad_diff: 1, | |
188 | ad_chan: 16, | |
189 | adbits: 16, | |
190 | adrangetype:1, | |
191 | dabits: 12}, | |
192 | { | |
193 | name: "dt2808", | |
194 | boardcode:0x20, | |
195 | ad_diff: 0, | |
196 | ad_chan: 16, | |
197 | adbits: 12, | |
198 | adrangetype:2, | |
199 | dabits: 8}, | |
200 | { | |
201 | name: "dt2818", | |
202 | boardcode:0xa2, | |
203 | ad_diff: 0, | |
204 | ad_chan: 4, | |
205 | adbits: 12, | |
206 | adrangetype:0, | |
207 | dabits: 12}, | |
208 | { | |
209 | name: "dt2809", | |
210 | boardcode:0xb0, | |
211 | ad_diff: 0, | |
212 | ad_chan: 8, | |
213 | adbits: 12, | |
214 | adrangetype:1, | |
215 | dabits: 12}, | |
216 | }; | |
217 | ||
218 | #define n_boardtypes ((sizeof(boardtypes))/(sizeof(boardtypes[0]))) | |
219 | #define boardtype (*(const boardtype_t *)dev->board_ptr) | |
220 | ||
221 | typedef struct { | |
222 | const comedi_lrange *dac_range_types[2]; | |
790c5541 | 223 | unsigned int ao_readback[2]; |
a4c87948 DS |
224 | } dt2801_private; |
225 | #define devpriv ((dt2801_private *)dev->private) | |
226 | ||
227 | static int dt2801_ai_insn_read(comedi_device * dev, comedi_subdevice * s, | |
790c5541 | 228 | comedi_insn * insn, unsigned int * data); |
a4c87948 | 229 | static int dt2801_ao_insn_read(comedi_device * dev, comedi_subdevice * s, |
790c5541 | 230 | comedi_insn * insn, unsigned int * data); |
a4c87948 | 231 | static int dt2801_ao_insn_write(comedi_device * dev, comedi_subdevice * s, |
790c5541 | 232 | comedi_insn * insn, unsigned int * data); |
a4c87948 | 233 | static int dt2801_dio_insn_bits(comedi_device * dev, comedi_subdevice * s, |
790c5541 | 234 | comedi_insn * insn, unsigned int * data); |
a4c87948 | 235 | static int dt2801_dio_insn_config(comedi_device * dev, comedi_subdevice * s, |
790c5541 | 236 | comedi_insn * insn, unsigned int * data); |
a4c87948 DS |
237 | |
238 | /* These are the low-level routines: | |
239 | writecommand: write a command to the board | |
240 | writedata: write data byte | |
241 | readdata: read data byte | |
242 | */ | |
243 | ||
244 | /* Only checks DataOutReady-flag, not the Ready-flag as it is done | |
245 | in the examples of the manual. I don't see why this should be | |
246 | necessary. */ | |
247 | static int dt2801_readdata(comedi_device * dev, int *data) | |
248 | { | |
249 | int stat = 0; | |
250 | int timeout = DT2801_TIMEOUT; | |
251 | ||
252 | do { | |
253 | stat = inb_p(dev->iobase + DT2801_STATUS); | |
254 | if (stat & (DT_S_COMPOSITE_ERROR | DT_S_READY)) { | |
255 | return stat; | |
256 | } | |
257 | if (stat & DT_S_DATA_OUT_READY) { | |
258 | *data = inb_p(dev->iobase + DT2801_DATA); | |
259 | return 0; | |
260 | } | |
261 | } while (--timeout > 0); | |
262 | ||
263 | return -ETIME; | |
264 | } | |
265 | ||
266 | static int dt2801_readdata2(comedi_device * dev, int *data) | |
267 | { | |
268 | int lb, hb; | |
269 | int ret; | |
270 | ||
271 | ret = dt2801_readdata(dev, &lb); | |
272 | if (ret) | |
273 | return ret; | |
274 | ret = dt2801_readdata(dev, &hb); | |
275 | if (ret) | |
276 | return ret; | |
277 | ||
278 | *data = (hb << 8) + lb; | |
279 | return 0; | |
280 | } | |
281 | ||
282 | static int dt2801_writedata(comedi_device * dev, unsigned int data) | |
283 | { | |
284 | int stat = 0; | |
285 | int timeout = DT2801_TIMEOUT; | |
286 | ||
287 | do { | |
288 | stat = inb_p(dev->iobase + DT2801_STATUS); | |
289 | ||
290 | if (stat & DT_S_COMPOSITE_ERROR) { | |
291 | return stat; | |
292 | } | |
293 | if (!(stat & DT_S_DATA_IN_FULL)) { | |
294 | outb_p(data & 0xff, dev->iobase + DT2801_DATA); | |
295 | return 0; | |
296 | } | |
297 | #if 0 | |
298 | if (stat & DT_S_READY) { | |
299 | printk("dt2801: ready flag set (bad!) in dt2801_writedata()\n"); | |
300 | return -EIO; | |
301 | } | |
302 | #endif | |
303 | } while (--timeout > 0); | |
304 | ||
305 | return -ETIME; | |
306 | } | |
307 | ||
308 | static int dt2801_writedata2(comedi_device * dev, unsigned int data) | |
309 | { | |
310 | int ret; | |
311 | ||
312 | ret = dt2801_writedata(dev, data & 0xff); | |
313 | if (ret < 0) | |
314 | return ret; | |
315 | ret = dt2801_writedata(dev, (data >> 8)); | |
316 | if (ret < 0) | |
317 | return ret; | |
318 | ||
319 | return 0; | |
320 | } | |
321 | ||
322 | static int dt2801_wait_for_ready(comedi_device * dev) | |
323 | { | |
324 | int timeout = DT2801_TIMEOUT; | |
325 | int stat; | |
326 | ||
327 | stat = inb_p(dev->iobase + DT2801_STATUS); | |
328 | if (stat & DT_S_READY) { | |
329 | return 0; | |
330 | } | |
331 | do { | |
332 | stat = inb_p(dev->iobase + DT2801_STATUS); | |
333 | ||
334 | if (stat & DT_S_COMPOSITE_ERROR) { | |
335 | return stat; | |
336 | } | |
337 | if (stat & DT_S_READY) { | |
338 | return 0; | |
339 | } | |
340 | } while (--timeout > 0); | |
341 | ||
342 | return -ETIME; | |
343 | } | |
344 | ||
345 | static int dt2801_writecmd(comedi_device * dev, int command) | |
346 | { | |
347 | int stat; | |
348 | ||
349 | dt2801_wait_for_ready(dev); | |
350 | ||
351 | stat = inb_p(dev->iobase + DT2801_STATUS); | |
352 | if (stat & DT_S_COMPOSITE_ERROR) { | |
353 | printk("dt2801: composite-error in dt2801_writecmd(), ignoring\n"); | |
354 | } | |
355 | if (!(stat & DT_S_READY)) { | |
356 | printk("dt2801: !ready in dt2801_writecmd(), ignoring\n"); | |
357 | } | |
358 | outb_p(command, dev->iobase + DT2801_CMD); | |
359 | ||
360 | return 0; | |
361 | } | |
362 | ||
363 | static int dt2801_reset(comedi_device * dev) | |
364 | { | |
365 | int board_code = 0; | |
366 | unsigned int stat; | |
367 | int timeout; | |
368 | ||
369 | DPRINTK("dt2801: resetting board...\n"); | |
370 | DPRINTK("fingerprint: 0x%02x 0x%02x\n", inb_p(dev->iobase), | |
371 | inb_p(dev->iobase + 1)); | |
372 | ||
373 | /* pull random data from data port */ | |
374 | inb_p(dev->iobase + DT2801_DATA); | |
375 | inb_p(dev->iobase + DT2801_DATA); | |
376 | inb_p(dev->iobase + DT2801_DATA); | |
377 | inb_p(dev->iobase + DT2801_DATA); | |
378 | ||
379 | DPRINTK("dt2801: stop\n"); | |
380 | //dt2801_writecmd(dev,DT_C_STOP); | |
381 | outb_p(DT_C_STOP, dev->iobase + DT2801_CMD); | |
382 | ||
383 | //dt2801_wait_for_ready(dev); | |
384 | comedi_udelay(100); | |
385 | timeout = 10000; | |
386 | do { | |
387 | stat = inb_p(dev->iobase + DT2801_STATUS); | |
388 | if (stat & DT_S_READY) | |
389 | break; | |
390 | } while (timeout--); | |
391 | if (!timeout) { | |
392 | printk("dt2801: timeout 1 status=0x%02x\n", stat); | |
393 | } | |
394 | //printk("dt2801: reading dummy\n"); | |
395 | //dt2801_readdata(dev,&board_code); | |
396 | ||
397 | DPRINTK("dt2801: reset\n"); | |
398 | outb_p(DT_C_RESET, dev->iobase + DT2801_CMD); | |
399 | //dt2801_writecmd(dev,DT_C_RESET); | |
400 | ||
401 | comedi_udelay(100); | |
402 | timeout = 10000; | |
403 | do { | |
404 | stat = inb_p(dev->iobase + DT2801_STATUS); | |
405 | if (stat & DT_S_READY) | |
406 | break; | |
407 | } while (timeout--); | |
408 | if (!timeout) { | |
409 | printk("dt2801: timeout 2 status=0x%02x\n", stat); | |
410 | } | |
411 | ||
412 | DPRINTK("dt2801: reading code\n"); | |
413 | dt2801_readdata(dev, &board_code); | |
414 | ||
415 | DPRINTK("dt2801: ok. code=0x%02x\n", board_code); | |
416 | ||
417 | return board_code; | |
418 | } | |
419 | ||
420 | static int probe_number_of_ai_chans(comedi_device * dev) | |
421 | { | |
422 | int n_chans; | |
423 | int stat; | |
424 | int data; | |
425 | ||
426 | for (n_chans = 0; n_chans < 16; n_chans++) { | |
427 | stat = dt2801_writecmd(dev, DT_C_READ_ADIM); | |
428 | dt2801_writedata(dev, 0); | |
429 | dt2801_writedata(dev, n_chans); | |
430 | stat = dt2801_readdata2(dev, &data); | |
431 | ||
432 | if (stat) | |
433 | break; | |
434 | } | |
435 | ||
436 | dt2801_reset(dev); | |
437 | dt2801_reset(dev); | |
438 | ||
439 | return n_chans; | |
440 | } | |
441 | ||
442 | static const comedi_lrange *dac_range_table[] = { | |
443 | &range_bipolar10, | |
444 | &range_bipolar5, | |
445 | &range_bipolar2_5, | |
446 | &range_unipolar10, | |
447 | &range_unipolar5 | |
448 | }; | |
449 | ||
450 | static const comedi_lrange *dac_range_lkup(int opt) | |
451 | { | |
452 | if (opt < 0 || opt > 5) | |
453 | return &range_unknown; | |
454 | return dac_range_table[opt]; | |
455 | } | |
456 | ||
457 | static const comedi_lrange *ai_range_lkup(int type, int opt) | |
458 | { | |
459 | switch (type) { | |
460 | case 0: | |
461 | return (opt) ? | |
462 | &range_dt2801_ai_pgl_unipolar : | |
463 | &range_dt2801_ai_pgl_bipolar; | |
464 | case 1: | |
465 | return (opt) ? &range_unipolar10 : &range_bipolar10; | |
466 | case 2: | |
467 | return &range_unipolar5; | |
468 | } | |
469 | return &range_unknown; | |
470 | } | |
471 | ||
472 | /* | |
473 | options: | |
474 | [0] - i/o base | |
475 | [1] - unused | |
476 | [2] - a/d 0=differential, 1=single-ended | |
477 | [3] - a/d range 0=[-10,10], 1=[0,10] | |
478 | [4] - dac0 range 0=[-10,10], 1=[-5,5], 2=[-2.5,2.5] 3=[0,10], 4=[0,5] | |
479 | [5] - dac1 range 0=[-10,10], 1=[-5,5], 2=[-2.5,2.5] 3=[0,10], 4=[0,5] | |
480 | */ | |
481 | static int dt2801_attach(comedi_device * dev, comedi_devconfig * it) | |
482 | { | |
483 | comedi_subdevice *s; | |
484 | unsigned long iobase; | |
485 | int board_code, type; | |
486 | int ret = 0; | |
487 | int n_ai_chans; | |
488 | ||
489 | iobase = it->options[0]; | |
490 | if (!request_region(iobase, DT2801_IOSIZE, "dt2801")) { | |
491 | comedi_error(dev, "I/O port conflict"); | |
492 | return -EIO; | |
493 | } | |
494 | dev->iobase = iobase; | |
495 | ||
496 | /* do some checking */ | |
497 | ||
498 | board_code = dt2801_reset(dev); | |
499 | ||
500 | /* heh. if it didn't work, try it again. */ | |
501 | if (!board_code) | |
502 | board_code = dt2801_reset(dev); | |
503 | ||
504 | for (type = 0; type < n_boardtypes; type++) { | |
505 | if (boardtypes[type].boardcode == board_code) | |
506 | goto havetype; | |
507 | } | |
508 | printk("dt2801: unrecognized board code=0x%02x, contact author\n", | |
509 | board_code); | |
510 | type = 0; | |
511 | ||
512 | havetype: | |
513 | dev->board_ptr = boardtypes + type; | |
514 | printk("dt2801: %s at port 0x%lx", boardtype.name, iobase); | |
515 | ||
516 | n_ai_chans = probe_number_of_ai_chans(dev); | |
517 | printk(" (ai channels = %d)", n_ai_chans); | |
518 | ||
519 | if ((ret = alloc_subdevices(dev, 4)) < 0) | |
520 | goto out; | |
521 | ||
522 | if ((ret = alloc_private(dev, sizeof(dt2801_private))) < 0) | |
523 | goto out; | |
524 | ||
525 | dev->board_name = boardtype.name; | |
526 | ||
527 | s = dev->subdevices + 0; | |
528 | /* ai subdevice */ | |
529 | s->type = COMEDI_SUBD_AI; | |
530 | s->subdev_flags = SDF_READABLE | SDF_GROUND; | |
531 | #if 1 | |
532 | s->n_chan = n_ai_chans; | |
533 | #else | |
534 | if (it->options[2]) | |
535 | s->n_chan = boardtype.ad_chan; | |
536 | else | |
537 | s->n_chan = boardtype.ad_chan / 2; | |
538 | #endif | |
539 | s->maxdata = (1 << boardtype.adbits) - 1; | |
540 | s->range_table = ai_range_lkup(boardtype.adrangetype, it->options[3]); | |
541 | s->insn_read = dt2801_ai_insn_read; | |
542 | ||
543 | s++; | |
544 | /* ao subdevice */ | |
545 | s->type = COMEDI_SUBD_AO; | |
546 | s->subdev_flags = SDF_WRITABLE; | |
547 | s->n_chan = 2; | |
548 | s->maxdata = (1 << boardtype.dabits) - 1; | |
549 | s->range_table_list = devpriv->dac_range_types; | |
550 | devpriv->dac_range_types[0] = dac_range_lkup(it->options[4]); | |
551 | devpriv->dac_range_types[1] = dac_range_lkup(it->options[5]); | |
552 | s->insn_read = dt2801_ao_insn_read; | |
553 | s->insn_write = dt2801_ao_insn_write; | |
554 | ||
555 | s++; | |
556 | /* 1st digital subdevice */ | |
557 | s->type = COMEDI_SUBD_DIO; | |
558 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
559 | s->n_chan = 8; | |
560 | s->maxdata = 1; | |
561 | s->range_table = &range_digital; | |
562 | s->insn_bits = dt2801_dio_insn_bits; | |
563 | s->insn_config = dt2801_dio_insn_config; | |
564 | ||
565 | s++; | |
566 | /* 2nd digital subdevice */ | |
567 | s->type = COMEDI_SUBD_DIO; | |
568 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
569 | s->n_chan = 8; | |
570 | s->maxdata = 1; | |
571 | s->range_table = &range_digital; | |
572 | s->insn_bits = dt2801_dio_insn_bits; | |
573 | s->insn_config = dt2801_dio_insn_config; | |
574 | ||
575 | ret = 0; | |
576 | out: | |
577 | printk("\n"); | |
578 | ||
579 | return ret; | |
580 | } | |
581 | ||
582 | static int dt2801_detach(comedi_device * dev) | |
583 | { | |
584 | if (dev->iobase) | |
585 | release_region(dev->iobase, DT2801_IOSIZE); | |
586 | ||
587 | return 0; | |
588 | } | |
589 | ||
590 | static int dt2801_error(comedi_device * dev, int stat) | |
591 | { | |
592 | if (stat < 0) { | |
593 | if (stat == -ETIME) { | |
594 | printk("dt2801: timeout\n"); | |
595 | } else { | |
596 | printk("dt2801: error %d\n", stat); | |
597 | } | |
598 | return stat; | |
599 | } | |
600 | printk("dt2801: error status 0x%02x, resetting...\n", stat); | |
601 | ||
602 | dt2801_reset(dev); | |
603 | dt2801_reset(dev); | |
604 | ||
605 | return -EIO; | |
606 | } | |
607 | ||
608 | static int dt2801_ai_insn_read(comedi_device * dev, comedi_subdevice * s, | |
790c5541 | 609 | comedi_insn * insn, unsigned int * data) |
a4c87948 DS |
610 | { |
611 | int d; | |
612 | int stat; | |
613 | int i; | |
614 | ||
615 | for (i = 0; i < insn->n; i++) { | |
616 | stat = dt2801_writecmd(dev, DT_C_READ_ADIM); | |
617 | dt2801_writedata(dev, CR_RANGE(insn->chanspec)); | |
618 | dt2801_writedata(dev, CR_CHAN(insn->chanspec)); | |
619 | stat = dt2801_readdata2(dev, &d); | |
620 | ||
621 | if (stat != 0) | |
622 | return dt2801_error(dev, stat); | |
623 | ||
624 | data[i] = d; | |
625 | } | |
626 | ||
627 | return i; | |
628 | } | |
629 | ||
630 | static int dt2801_ao_insn_read(comedi_device * dev, comedi_subdevice * s, | |
790c5541 | 631 | comedi_insn * insn, unsigned int * data) |
a4c87948 DS |
632 | { |
633 | data[0] = devpriv->ao_readback[CR_CHAN(insn->chanspec)]; | |
634 | ||
635 | return 1; | |
636 | } | |
637 | ||
638 | static int dt2801_ao_insn_write(comedi_device * dev, comedi_subdevice * s, | |
790c5541 | 639 | comedi_insn * insn, unsigned int * data) |
a4c87948 DS |
640 | { |
641 | dt2801_writecmd(dev, DT_C_WRITE_DAIM); | |
642 | dt2801_writedata(dev, CR_CHAN(insn->chanspec)); | |
643 | dt2801_writedata2(dev, data[0]); | |
644 | ||
645 | devpriv->ao_readback[CR_CHAN(insn->chanspec)] = data[0]; | |
646 | ||
647 | return 1; | |
648 | } | |
649 | ||
650 | static int dt2801_dio_insn_bits(comedi_device * dev, comedi_subdevice * s, | |
790c5541 | 651 | comedi_insn * insn, unsigned int * data) |
a4c87948 DS |
652 | { |
653 | int which = 0; | |
654 | ||
655 | if (s == dev->subdevices + 4) | |
656 | which = 1; | |
657 | ||
658 | if (insn->n != 2) | |
659 | return -EINVAL; | |
660 | if (data[0]) { | |
661 | s->state &= ~data[0]; | |
662 | s->state |= (data[0] & data[1]); | |
663 | dt2801_writecmd(dev, DT_C_WRITE_DIG); | |
664 | dt2801_writedata(dev, which); | |
665 | dt2801_writedata(dev, s->state); | |
666 | } | |
667 | dt2801_writecmd(dev, DT_C_READ_DIG); | |
668 | dt2801_writedata(dev, which); | |
669 | dt2801_readdata(dev, data + 1); | |
670 | ||
671 | return 2; | |
672 | } | |
673 | ||
674 | static int dt2801_dio_insn_config(comedi_device * dev, comedi_subdevice * s, | |
790c5541 | 675 | comedi_insn * insn, unsigned int * data) |
a4c87948 DS |
676 | { |
677 | int which = 0; | |
678 | ||
679 | if (s == dev->subdevices + 4) | |
680 | which = 1; | |
681 | ||
682 | /* configure */ | |
683 | if (data[0]) { | |
684 | s->io_bits = 0xff; | |
685 | dt2801_writecmd(dev, DT_C_SET_DIGOUT); | |
686 | } else { | |
687 | s->io_bits = 0; | |
688 | dt2801_writecmd(dev, DT_C_SET_DIGIN); | |
689 | } | |
690 | dt2801_writedata(dev, which); | |
691 | ||
692 | return 1; | |
693 | } |