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