Commit | Line | Data |
---|---|---|
3c443716 DS |
1 | /* |
2 | comedi/drivers/dt2811.c | |
3 | Hardware driver for Data Translation DT2811 | |
4 | ||
5 | COMEDI - Linux Control and Measurement Device Interface | |
6 | History: | |
7 | Base Version - David A. Schleef <ds@schleef.org> | |
8 | December 1998 - Updated to work. David does not have a DT2811 | |
9 | board any longer so this was suffering from bitrot. | |
10 | Updated performed by ... | |
11 | ||
12 | This program is free software; you can redistribute it and/or modify | |
13 | it under the terms of the GNU General Public License as published by | |
14 | the Free Software Foundation; either version 2 of the License, or | |
15 | (at your option) any later version. | |
16 | ||
17 | This program is distributed in the hope that it will be useful, | |
18 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | GNU General Public License for more details. | |
21 | ||
22 | You should have received a copy of the GNU General Public License | |
23 | along with this program; if not, write to the Free Software | |
24 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
25 | */ | |
26 | /* | |
27 | Driver: dt2811 | |
28 | Description: Data Translation DT2811 | |
29 | Author: ds | |
30 | Devices: [Data Translation] DT2811-PGL (dt2811-pgl), DT2811-PGH (dt2811-pgh) | |
31 | Status: works | |
32 | ||
33 | Configuration options: | |
34 | [0] - I/O port base address | |
35 | [1] - IRQ, although this is currently unused | |
36 | [2] - A/D reference | |
37 | 0 = signle-ended | |
38 | 1 = differential | |
39 | 2 = pseudo-differential (common reference) | |
40 | [3] - A/D range | |
41 | 0 = [-5,5] | |
42 | 1 = [-2.5,2.5] | |
43 | 2 = [0,5] | |
44 | [4] - D/A 0 range (same choices) | |
45 | [4] - D/A 1 range (same choices) | |
46 | */ | |
47 | ||
48 | #include "../comedidev.h" | |
49 | ||
50 | #include <linux/ioport.h> | |
51 | ||
52 | static const char *driver_name = "dt2811"; | |
53 | ||
9ced1de6 | 54 | static const struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = { 4, { |
3c443716 DS |
55 | RANGE(0, 5), |
56 | RANGE(0, 2.5), | |
57 | RANGE(0, 1.25), | |
58 | RANGE(0, 0.625) | |
59 | } | |
60 | }; | |
9ced1de6 | 61 | static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = { 4, { |
3c443716 DS |
62 | RANGE(-2.5, 2.5), |
63 | RANGE(-1.25, 1.25), | |
64 | RANGE(-0.625, 0.625), | |
65 | RANGE(-0.3125, 0.3125) | |
66 | } | |
67 | }; | |
9ced1de6 | 68 | static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = { 4, { |
3c443716 DS |
69 | RANGE(-5, 5), |
70 | RANGE(-2.5, 2.5), | |
71 | RANGE(-1.25, 1.25), | |
72 | RANGE(-0.625, 0.625) | |
73 | } | |
74 | }; | |
9ced1de6 | 75 | static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = { 4, { |
3c443716 DS |
76 | RANGE(0, 5), |
77 | RANGE(0, 0.5), | |
78 | RANGE(0, 0.05), | |
79 | RANGE(0, 0.01) | |
80 | } | |
81 | }; | |
9ced1de6 | 82 | static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = { 4, { |
3c443716 DS |
83 | RANGE(-2.5, 2.5), |
84 | RANGE(-0.25, 0.25), | |
85 | RANGE(-0.025, 0.025), | |
86 | RANGE(-0.005, 0.005) | |
87 | } | |
88 | }; | |
9ced1de6 | 89 | static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = { 4, { |
3c443716 DS |
90 | RANGE(-5, 5), |
91 | RANGE(-0.5, 0.5), | |
92 | RANGE(-0.05, 0.05), | |
93 | RANGE(-0.01, 0.01) | |
94 | } | |
95 | }; | |
96 | ||
97 | /* | |
98 | ||
99 | 0x00 ADCSR R/W A/D Control/Status Register | |
100 | bit 7 - (R) 1 indicates A/D conversion done | |
101 | reading ADDAT clears bit | |
102 | (W) ignored | |
103 | bit 6 - (R) 1 indicates A/D error | |
104 | (W) ignored | |
105 | bit 5 - (R) 1 indicates A/D busy, cleared at end | |
106 | of conversion | |
107 | (W) ignored | |
108 | bit 4 - (R) 0 | |
109 | (W) | |
110 | bit 3 - (R) 0 | |
111 | bit 2 - (R/W) 1 indicates interrupts enabled | |
112 | bits 1,0 - (R/W) mode bits | |
113 | 00 single conversion on ADGCR load | |
114 | 01 continuous conversion, internal clock, | |
115 | (clock enabled on ADGCR load) | |
116 | 10 continuous conversion, internal clock, | |
117 | external trigger | |
118 | 11 continuous conversion, external clock, | |
119 | external trigger | |
120 | ||
121 | 0x01 ADGCR R/W A/D Gain/Channel Register | |
122 | bit 6,7 - (R/W) gain select | |
123 | 00 gain=1, both PGH, PGL models | |
124 | 01 gain=2 PGH, 10 PGL | |
125 | 10 gain=4 PGH, 100 PGL | |
126 | 11 gain=8 PGH, 500 PGL | |
127 | bit 4,5 - reserved | |
128 | bit 3-0 - (R/W) channel select | |
129 | channel number from 0-15 | |
130 | ||
131 | 0x02,0x03 (R) ADDAT A/D Data Register | |
132 | (W) DADAT0 D/A Data Register 0 | |
133 | 0x02 low byte | |
134 | 0x03 high byte | |
135 | ||
136 | 0x04,0x05 (W) DADAT0 D/A Data Register 1 | |
137 | ||
138 | 0x06 (R) DIO0 Digital Input Port 0 | |
139 | (W) DIO1 Digital Output Port 1 | |
140 | ||
141 | 0x07 TMRCTR (R/W) Timer/Counter Register | |
142 | bits 6,7 - reserved | |
143 | bits 5-3 - Timer frequency control (mantissa) | |
144 | 543 divisor freqency (kHz) | |
145 | 000 1 600 | |
146 | 001 10 60 | |
147 | 010 2 300 | |
148 | 011 3 200 | |
149 | 100 4 150 | |
150 | 101 5 120 | |
151 | 110 6 100 | |
152 | 111 12 50 | |
153 | bits 2-0 - Timer frequency control (exponent) | |
154 | 210 multiply divisor/divide frequency by | |
155 | 000 1 | |
156 | 001 10 | |
157 | 010 100 | |
158 | 011 1000 | |
159 | 100 10000 | |
160 | 101 100000 | |
161 | 110 1000000 | |
162 | 111 10000000 | |
163 | ||
164 | */ | |
165 | ||
166 | #define TIMEOUT 10000 | |
167 | ||
168 | #define DT2811_SIZE 8 | |
169 | ||
170 | #define DT2811_ADCSR 0 | |
171 | #define DT2811_ADGCR 1 | |
172 | #define DT2811_ADDATLO 2 | |
173 | #define DT2811_ADDATHI 3 | |
174 | #define DT2811_DADAT0LO 2 | |
175 | #define DT2811_DADAT0HI 3 | |
176 | #define DT2811_DADAT1LO 4 | |
177 | #define DT2811_DADAT1HI 5 | |
178 | #define DT2811_DIO 6 | |
179 | #define DT2811_TMRCTR 7 | |
180 | ||
181 | /* | |
182 | * flags | |
183 | */ | |
184 | ||
185 | /* ADCSR */ | |
186 | ||
187 | #define DT2811_ADDONE 0x80 | |
188 | #define DT2811_ADERROR 0x40 | |
189 | #define DT2811_ADBUSY 0x20 | |
190 | #define DT2811_CLRERROR 0x10 | |
191 | #define DT2811_INTENB 0x04 | |
192 | #define DT2811_ADMODE 0x03 | |
193 | ||
42f1884d BP |
194 | struct dt2811_board { |
195 | ||
3c443716 | 196 | const char *name; |
9ced1de6 BP |
197 | const struct comedi_lrange *bip_5; |
198 | const struct comedi_lrange *bip_2_5; | |
199 | const struct comedi_lrange *unip_5; | |
42f1884d BP |
200 | }; |
201 | ||
202 | static const struct dt2811_board boardtypes[] = { | |
3c443716 DS |
203 | {"dt2811-pgh", |
204 | &range_dt2811_pgh_ai_5_bipolar, | |
205 | &range_dt2811_pgh_ai_2_5_bipolar, | |
206 | &range_dt2811_pgh_ai_5_unipolar, | |
207 | }, | |
208 | {"dt2811-pgl", | |
209 | &range_dt2811_pgl_ai_5_bipolar, | |
210 | &range_dt2811_pgl_ai_2_5_bipolar, | |
211 | &range_dt2811_pgl_ai_5_unipolar, | |
212 | }, | |
213 | }; | |
214 | ||
42f1884d | 215 | #define this_board ((const struct dt2811_board *)dev->board_ptr) |
3c443716 | 216 | |
da91b269 BP |
217 | static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it); |
218 | static int dt2811_detach(struct comedi_device *dev); | |
139dfbdf | 219 | static struct comedi_driver driver_dt2811 = { |
3c443716 DS |
220 | driver_name:"dt2811", |
221 | module:THIS_MODULE, | |
222 | attach:dt2811_attach, | |
223 | detach:dt2811_detach, | |
224 | board_name:&boardtypes[0].name, | |
42f1884d BP |
225 | num_names:sizeof(boardtypes) / sizeof(struct dt2811_board), |
226 | offset:sizeof(struct dt2811_board), | |
3c443716 DS |
227 | }; |
228 | ||
229 | COMEDI_INITCLEANUP(driver_dt2811); | |
230 | ||
da91b269 BP |
231 | static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s, |
232 | struct comedi_insn *insn, unsigned int *data); | |
233 | static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s, | |
234 | struct comedi_insn *insn, unsigned int *data); | |
235 | static int dt2811_ao_insn_read(struct comedi_device *dev, struct comedi_subdevice *s, | |
236 | struct comedi_insn *insn, unsigned int *data); | |
237 | static int dt2811_di_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, | |
238 | struct comedi_insn *insn, unsigned int *data); | |
239 | static int dt2811_do_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, | |
240 | struct comedi_insn *insn, unsigned int *data); | |
3c443716 DS |
241 | |
242 | enum { card_2811_pgh, card_2811_pgl }; | |
d89da617 BP |
243 | |
244 | struct dt2811_private { | |
3c443716 DS |
245 | int ntrig; |
246 | int curadchan; | |
247 | enum { | |
248 | adc_singleended, adc_diff, adc_pseudo_diff | |
249 | } adc_mux; | |
250 | enum { | |
251 | dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5 | |
252 | } dac_range[2]; | |
9ced1de6 | 253 | const struct comedi_lrange *range_type_list[2]; |
790c5541 | 254 | unsigned int ao_readback[2]; |
d89da617 | 255 | }; |
3c443716 | 256 | |
d89da617 | 257 | #define devpriv ((struct dt2811_private *)dev->private) |
3c443716 | 258 | |
9ced1de6 | 259 | static const struct comedi_lrange *dac_range_types[] = { |
3c443716 DS |
260 | &range_bipolar5, |
261 | &range_bipolar2_5, | |
262 | &range_unipolar5 | |
263 | }; | |
264 | ||
265 | #define DT2811_TIMEOUT 5 | |
266 | ||
267 | #if 0 | |
70265d24 | 268 | static irqreturn_t dt2811_interrupt(int irq, void *d) |
3c443716 DS |
269 | { |
270 | int lo, hi; | |
271 | int data; | |
71b5f4f1 | 272 | struct comedi_device *dev = d; |
3c443716 DS |
273 | |
274 | if (!dev->attached) { | |
275 | comedi_error(dev, "spurious interrupt"); | |
276 | return IRQ_HANDLED; | |
277 | } | |
278 | ||
279 | lo = inb(dev->iobase + DT2811_ADDATLO); | |
280 | hi = inb(dev->iobase + DT2811_ADDATHI); | |
281 | ||
282 | data = lo + (hi << 8); | |
283 | ||
284 | if (!(--devpriv->ntrig)) { | |
285 | /* how to turn off acquisition */ | |
286 | s->async->events |= COMEDI_SB_EOA; | |
287 | } | |
288 | comedi_event(dev, s); | |
289 | return IRQ_HANDLED; | |
290 | } | |
291 | #endif | |
292 | ||
293 | /* | |
294 | options[0] Board base address | |
295 | options[1] IRQ | |
296 | options[2] Input configuration | |
297 | 0 == single-ended | |
298 | 1 == differential | |
299 | 2 == pseudo-differential | |
300 | options[3] Analog input range configuration | |
301 | 0 == bipolar 5 (-5V -- +5V) | |
302 | 1 == bipolar 2.5V (-2.5V -- +2.5V) | |
303 | 2 == unipolar 5V (0V -- +5V) | |
304 | options[4] Analog output 0 range configuration | |
305 | 0 == bipolar 5 (-5V -- +5V) | |
306 | 1 == bipolar 2.5V (-2.5V -- +2.5V) | |
307 | 2 == unipolar 5V (0V -- +5V) | |
308 | options[5] Analog output 1 range configuration | |
309 | 0 == bipolar 5 (-5V -- +5V) | |
310 | 1 == bipolar 2.5V (-2.5V -- +2.5V) | |
311 | 2 == unipolar 5V (0V -- +5V) | |
312 | */ | |
313 | ||
da91b269 | 314 | static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it) |
3c443716 | 315 | { |
2696fb57 BP |
316 | /* int i, irq; */ |
317 | /* unsigned long irqs; */ | |
318 | /* long flags; */ | |
319 | ||
3c443716 | 320 | int ret; |
34c43922 | 321 | struct comedi_subdevice *s; |
3c443716 DS |
322 | unsigned long iobase; |
323 | ||
324 | iobase = it->options[0]; | |
325 | ||
326 | printk("comedi%d: dt2811: base=0x%04lx\n", dev->minor, iobase); | |
327 | ||
328 | if (!request_region(iobase, DT2811_SIZE, driver_name)) { | |
329 | printk("I/O port conflict\n"); | |
330 | return -EIO; | |
331 | } | |
332 | ||
333 | dev->iobase = iobase; | |
334 | dev->board_name = this_board->name; | |
335 | ||
336 | #if 0 | |
337 | outb(0, dev->iobase + DT2811_ADCSR); | |
338 | comedi_udelay(100); | |
339 | i = inb(dev->iobase + DT2811_ADDATLO); | |
340 | i = inb(dev->iobase + DT2811_ADDATHI); | |
341 | #endif | |
342 | ||
343 | #if 0 | |
344 | irq = it->options[1]; | |
345 | if (irq < 0) { | |
346 | save_flags(flags); | |
347 | sti(); | |
348 | irqs = probe_irq_on(); | |
349 | ||
350 | outb(DT2811_CLRERROR | DT2811_INTENB, | |
351 | dev->iobase + DT2811_ADCSR); | |
352 | outb(0, dev->iobase + DT2811_ADGCR); | |
353 | ||
354 | comedi_udelay(100); | |
355 | ||
356 | irq = probe_irq_off(irqs); | |
357 | restore_flags(flags); | |
358 | ||
359 | /*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); */ | |
360 | ||
361 | if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR) { | |
362 | printk("error probing irq (bad) \n"); | |
363 | } | |
364 | dev->irq = 0; | |
365 | if (irq > 0) { | |
366 | i = inb(dev->iobase + DT2811_ADDATLO); | |
367 | i = inb(dev->iobase + DT2811_ADDATHI); | |
368 | printk("(irq = %d)\n", irq); | |
369 | ret = comedi_request_irq(irq, dt2811_interrupt, 0, | |
370 | driver_name, dev); | |
371 | if (ret < 0) | |
372 | return -EIO; | |
373 | dev->irq = irq; | |
374 | } else if (irq == 0) { | |
375 | printk("(no irq)\n"); | |
376 | } else { | |
377 | printk("( multiple irq's -- this is bad! )\n"); | |
378 | } | |
379 | } | |
380 | #endif | |
381 | ||
c3744138 BP |
382 | ret = alloc_subdevices(dev, 4); |
383 | if (ret < 0) | |
3c443716 | 384 | return ret; |
c3744138 BP |
385 | |
386 | ret = alloc_private(dev, sizeof(struct dt2811_private)); | |
387 | if (ret < 0) | |
3c443716 | 388 | return ret; |
c3744138 | 389 | |
3c443716 DS |
390 | switch (it->options[2]) { |
391 | case 0: | |
392 | devpriv->adc_mux = adc_singleended; | |
393 | break; | |
394 | case 1: | |
395 | devpriv->adc_mux = adc_diff; | |
396 | break; | |
397 | case 2: | |
398 | devpriv->adc_mux = adc_pseudo_diff; | |
399 | break; | |
400 | default: | |
401 | devpriv->adc_mux = adc_singleended; | |
402 | break; | |
403 | } | |
404 | switch (it->options[4]) { | |
405 | case 0: | |
406 | devpriv->dac_range[0] = dac_bipolar_5; | |
407 | break; | |
408 | case 1: | |
409 | devpriv->dac_range[0] = dac_bipolar_2_5; | |
410 | break; | |
411 | case 2: | |
412 | devpriv->dac_range[0] = dac_unipolar_5; | |
413 | break; | |
414 | default: | |
415 | devpriv->dac_range[0] = dac_bipolar_5; | |
416 | break; | |
417 | } | |
418 | switch (it->options[5]) { | |
419 | case 0: | |
420 | devpriv->dac_range[1] = dac_bipolar_5; | |
421 | break; | |
422 | case 1: | |
423 | devpriv->dac_range[1] = dac_bipolar_2_5; | |
424 | break; | |
425 | case 2: | |
426 | devpriv->dac_range[1] = dac_unipolar_5; | |
427 | break; | |
428 | default: | |
429 | devpriv->dac_range[1] = dac_bipolar_5; | |
430 | break; | |
431 | } | |
432 | ||
433 | s = dev->subdevices + 0; | |
434 | /* initialize the ADC subdevice */ | |
435 | s->type = COMEDI_SUBD_AI; | |
436 | s->subdev_flags = SDF_READABLE | SDF_GROUND; | |
437 | s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16; | |
438 | s->insn_read = dt2811_ai_insn; | |
439 | s->maxdata = 0xfff; | |
440 | switch (it->options[3]) { | |
441 | case 0: | |
442 | default: | |
443 | s->range_table = this_board->bip_5; | |
444 | break; | |
445 | case 1: | |
446 | s->range_table = this_board->bip_2_5; | |
447 | break; | |
448 | case 2: | |
449 | s->range_table = this_board->unip_5; | |
450 | break; | |
451 | } | |
452 | ||
453 | s = dev->subdevices + 1; | |
454 | /* ao subdevice */ | |
455 | s->type = COMEDI_SUBD_AO; | |
456 | s->subdev_flags = SDF_WRITABLE; | |
457 | s->n_chan = 2; | |
458 | s->insn_write = dt2811_ao_insn; | |
459 | s->insn_read = dt2811_ao_insn_read; | |
460 | s->maxdata = 0xfff; | |
461 | s->range_table_list = devpriv->range_type_list; | |
462 | devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]]; | |
463 | devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]]; | |
464 | ||
465 | s = dev->subdevices + 2; | |
466 | /* di subdevice */ | |
467 | s->type = COMEDI_SUBD_DI; | |
468 | s->subdev_flags = SDF_READABLE; | |
469 | s->n_chan = 8; | |
470 | s->insn_bits = dt2811_di_insn_bits; | |
471 | s->maxdata = 1; | |
472 | s->range_table = &range_digital; | |
473 | ||
474 | s = dev->subdevices + 3; | |
475 | /* do subdevice */ | |
476 | s->type = COMEDI_SUBD_DO; | |
477 | s->subdev_flags = SDF_WRITABLE; | |
478 | s->n_chan = 8; | |
479 | s->insn_bits = dt2811_do_insn_bits; | |
480 | s->maxdata = 1; | |
481 | s->state = 0; | |
482 | s->range_table = &range_digital; | |
483 | ||
484 | return 0; | |
485 | } | |
486 | ||
da91b269 | 487 | static int dt2811_detach(struct comedi_device *dev) |
3c443716 DS |
488 | { |
489 | printk("comedi%d: dt2811: remove\n", dev->minor); | |
490 | ||
491 | if (dev->irq) { | |
492 | comedi_free_irq(dev->irq, dev); | |
493 | } | |
494 | if (dev->iobase) { | |
495 | release_region(dev->iobase, DT2811_SIZE); | |
496 | } | |
497 | ||
498 | return 0; | |
499 | } | |
500 | ||
da91b269 BP |
501 | static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s, |
502 | struct comedi_insn *insn, unsigned int *data) | |
3c443716 DS |
503 | { |
504 | int chan = CR_CHAN(insn->chanspec); | |
505 | int timeout = DT2811_TIMEOUT; | |
506 | int i; | |
507 | ||
508 | for (i = 0; i < insn->n; i++) { | |
509 | outb(chan, dev->iobase + DT2811_ADGCR); | |
510 | ||
511 | while (timeout | |
512 | && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY) | |
513 | timeout--; | |
514 | if (!timeout) | |
515 | return -ETIME; | |
516 | ||
517 | data[i] = inb(dev->iobase + DT2811_ADDATLO); | |
518 | data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8; | |
519 | data[i] &= 0xfff; | |
520 | } | |
521 | ||
522 | return i; | |
523 | } | |
524 | ||
525 | #if 0 | |
526 | /* Wow. This is code from the Comedi stone age. But it hasn't been | |
527 | * replaced, so I'll let it stay. */ | |
da91b269 | 528 | int dt2811_adtrig(kdev_t minor, comedi_adtrig *adtrig) |
3c443716 | 529 | { |
71b5f4f1 | 530 | struct comedi_device *dev = comedi_devices + minor; |
3c443716 DS |
531 | |
532 | if (adtrig->n < 1) | |
533 | return 0; | |
534 | dev->curadchan = adtrig->chan; | |
535 | switch (dev->i_admode) { | |
536 | case COMEDI_MDEMAND: | |
537 | dev->ntrig = adtrig->n - 1; | |
538 | /*printk("dt2811: AD soft trigger\n"); */ | |
2696fb57 | 539 | /*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); */ /* not neccessary */ |
3c443716 DS |
540 | outb(dev->curadchan, dev->iobase + DT2811_ADGCR); |
541 | do_gettimeofday(&trigtime); | |
542 | break; | |
543 | case COMEDI_MCONTS: | |
544 | dev->ntrig = adtrig->n; | |
545 | break; | |
546 | } | |
547 | ||
548 | return 0; | |
549 | } | |
550 | #endif | |
551 | ||
da91b269 BP |
552 | static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s, |
553 | struct comedi_insn *insn, unsigned int *data) | |
3c443716 DS |
554 | { |
555 | int i; | |
556 | int chan; | |
557 | ||
558 | chan = CR_CHAN(insn->chanspec); | |
559 | ||
560 | for (i = 0; i < insn->n; i++) { | |
561 | outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan); | |
562 | outb((data[i] >> 8) & 0xff, | |
563 | dev->iobase + DT2811_DADAT0HI + 2 * chan); | |
564 | devpriv->ao_readback[chan] = data[i]; | |
565 | } | |
566 | ||
567 | return i; | |
568 | } | |
569 | ||
da91b269 BP |
570 | static int dt2811_ao_insn_read(struct comedi_device *dev, struct comedi_subdevice *s, |
571 | struct comedi_insn *insn, unsigned int *data) | |
3c443716 DS |
572 | { |
573 | int i; | |
574 | int chan; | |
575 | ||
576 | chan = CR_CHAN(insn->chanspec); | |
577 | ||
578 | for (i = 0; i < insn->n; i++) { | |
579 | data[i] = devpriv->ao_readback[chan]; | |
580 | } | |
581 | ||
582 | return i; | |
583 | } | |
584 | ||
da91b269 BP |
585 | static int dt2811_di_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, |
586 | struct comedi_insn *insn, unsigned int *data) | |
3c443716 DS |
587 | { |
588 | if (insn->n != 2) | |
589 | return -EINVAL; | |
590 | ||
591 | data[1] = inb(dev->iobase + DT2811_DIO); | |
592 | ||
593 | return 2; | |
594 | } | |
595 | ||
da91b269 BP |
596 | static int dt2811_do_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, |
597 | struct comedi_insn *insn, unsigned int *data) | |
3c443716 DS |
598 | { |
599 | if (insn->n != 2) | |
600 | return -EINVAL; | |
601 | ||
602 | s->state &= ~data[0]; | |
603 | s->state |= data[0] & data[1]; | |
604 | outb(s->state, dev->iobase + DT2811_DIO); | |
605 | ||
606 | data[1] = s->state; | |
607 | ||
608 | return 2; | |
609 | } |