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