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