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