1 /*
2 comedi/drivers/das800.c
3 Driver for Keitley das800 series boards and compatibles
4 Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net>
5
6 COMEDI - Linux Control and Measurement Device Interface
7 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18 */
19 /*
20 Driver: das800
21 Description: Keithley Metrabyte DAS800 (& compatibles)
22 Author: Frank Mori Hess <fmhess@users.sourceforge.net>
23 Devices: [Keithley Metrabyte] DAS-800 (das-800), DAS-801 (das-801),
24 DAS-802 (das-802),
25 [Measurement Computing] CIO-DAS800 (cio-das800),
26 CIO-DAS801 (cio-das801), CIO-DAS802 (cio-das802),
27 CIO-DAS802/16 (cio-das802/16)
28 Status: works, cio-das802/16 untested - email me if you have tested it
29
30 Configuration options:
31 [0] - I/O port base address
32 [1] - IRQ (optional, required for timed or externally triggered conversions)
33
34 Notes:
35 IRQ can be omitted, although the cmd interface will not work without it.
36
37 All entries in the channel/gain list must use the same gain and be
38 consecutive channels counting upwards in channel number (these are
39 hardware limitations.)
40
41 I've never tested the gain setting stuff since I only have a
42 DAS-800 board with fixed gain.
43
44 The cio-das802/16 does not have a fifo-empty status bit! Therefore
45 only fifo-half-full transfers are possible with this card.
46
47 cmd triggers supported:
48 start_src: TRIG_NOW | TRIG_EXT
49 scan_begin_src: TRIG_FOLLOW
50 scan_end_src: TRIG_COUNT
51 convert_src: TRIG_TIMER | TRIG_EXT
52 stop_src: TRIG_NONE | TRIG_COUNT
53 */
54
55 #include <linux/module.h>
56 #include <linux/interrupt.h>
57 #include <linux/delay.h>
58
59 #include "../comedidev.h"
60
61 #include "comedi_8254.h"
62
63 #define N_CHAN_AI 8 /* number of analog input channels */
64
65 /* Registers for the das800 */
66
67 #define DAS800_LSB 0
68 #define FIFO_EMPTY 0x1
69 #define FIFO_OVF 0x2
70 #define DAS800_MSB 1
71 #define DAS800_CONTROL1 2
72 #define CONTROL1_INTE 0x8
73 #define DAS800_CONV_CONTROL 2
74 #define ITE 0x1
75 #define CASC 0x2
76 #define DTEN 0x4
77 #define IEOC 0x8
78 #define EACS 0x10
79 #define CONV_HCEN 0x80
80 #define DAS800_SCAN_LIMITS 2
81 #define DAS800_STATUS 2
82 #define IRQ 0x8
83 #define BUSY 0x80
84 #define DAS800_GAIN 3
85 #define CIO_FFOV 0x8 /* cio-das802/16 fifo overflow */
86 #define CIO_ENHF 0x90 /* cio-das802/16 fifo half full int ena */
87 #define CONTROL1 0x80
88 #define CONV_CONTROL 0xa0
89 #define SCAN_LIMITS 0xc0
90 #define ID 0xe0
91 #define DAS800_8254 4
92 #define DAS800_STATUS2 7
93 #define STATUS2_HCEN 0x80
94 #define STATUS2_INTE 0X20
95 #define DAS800_ID 7
96
97 #define DAS802_16_HALF_FIFO_SZ 128
98
99 struct das800_board {
100 const char *name;
101 int ai_speed;
102 const struct comedi_lrange *ai_range;
103 int resolution;
104 };
105
106 static const struct comedi_lrange range_das801_ai = {
107 9, {
108 BIP_RANGE(5),
109 BIP_RANGE(10),
110 UNI_RANGE(10),
111 BIP_RANGE(0.5),
112 UNI_RANGE(1),
113 BIP_RANGE(0.05),
114 UNI_RANGE(0.1),
115 BIP_RANGE(0.01),
116 UNI_RANGE(0.02)
117 }
118 };
119
120 static const struct comedi_lrange range_cio_das801_ai = {
121 9, {
122 BIP_RANGE(5),
123 BIP_RANGE(10),
124 UNI_RANGE(10),
125 BIP_RANGE(0.5),
126 UNI_RANGE(1),
127 BIP_RANGE(0.05),
128 UNI_RANGE(0.1),
129 BIP_RANGE(0.005),
130 UNI_RANGE(0.01)
131 }
132 };
133
134 static const struct comedi_lrange range_das802_ai = {
135 9, {
136 BIP_RANGE(5),
137 BIP_RANGE(10),
138 UNI_RANGE(10),
139 BIP_RANGE(2.5),
140 UNI_RANGE(5),
141 BIP_RANGE(1.25),
142 UNI_RANGE(2.5),
143 BIP_RANGE(0.625),
144 UNI_RANGE(1.25)
145 }
146 };
147
148 static const struct comedi_lrange range_das80216_ai = {
149 8, {
150 BIP_RANGE(10),
151 UNI_RANGE(10),
152 BIP_RANGE(5),
153 UNI_RANGE(5),
154 BIP_RANGE(2.5),
155 UNI_RANGE(2.5),
156 BIP_RANGE(1.25),
157 UNI_RANGE(1.25)
158 }
159 };
160
161 enum das800_boardinfo {
162 BOARD_DAS800,
163 BOARD_CIODAS800,
164 BOARD_DAS801,
165 BOARD_CIODAS801,
166 BOARD_DAS802,
167 BOARD_CIODAS802,
168 BOARD_CIODAS80216,
169 };
170
171 static const struct das800_board das800_boards[] = {
172 [BOARD_DAS800] = {
173 .name = "das-800",
174 .ai_speed = 25000,
175 .ai_range = &range_bipolar5,
176 .resolution = 12,
177 },
178 [BOARD_CIODAS800] = {
179 .name = "cio-das800",
180 .ai_speed = 20000,
181 .ai_range = &range_bipolar5,
182 .resolution = 12,
183 },
184 [BOARD_DAS801] = {
185 .name = "das-801",
186 .ai_speed = 25000,
187 .ai_range = &range_das801_ai,
188 .resolution = 12,
189 },
190 [BOARD_CIODAS801] = {
191 .name = "cio-das801",
192 .ai_speed = 20000,
193 .ai_range = &range_cio_das801_ai,
194 .resolution = 12,
195 },
196 [BOARD_DAS802] = {
197 .name = "das-802",
198 .ai_speed = 25000,
199 .ai_range = &range_das802_ai,
200 .resolution = 12,
201 },
202 [BOARD_CIODAS802] = {
203 .name = "cio-das802",
204 .ai_speed = 20000,
205 .ai_range = &range_das802_ai,
206 .resolution = 12,
207 },
208 [BOARD_CIODAS80216] = {
209 .name = "cio-das802/16",
210 .ai_speed = 10000,
211 .ai_range = &range_das80216_ai,
212 .resolution = 16,
213 },
214 };
215
216 struct das800_private {
217 unsigned int do_bits; /* digital output bits */
218 };
219
das800_ind_write(struct comedi_device * dev,unsigned val,unsigned reg)220 static void das800_ind_write(struct comedi_device *dev,
221 unsigned val, unsigned reg)
222 {
223 /*
224 * Select dev->iobase + 2 to be desired register
225 * then write to that register.
226 */
227 outb(reg, dev->iobase + DAS800_GAIN);
228 outb(val, dev->iobase + 2);
229 }
230
das800_ind_read(struct comedi_device * dev,unsigned reg)231 static unsigned das800_ind_read(struct comedi_device *dev, unsigned reg)
232 {
233 /*
234 * Select dev->iobase + 7 to be desired register
235 * then read from that register.
236 */
237 outb(reg, dev->iobase + DAS800_GAIN);
238 return inb(dev->iobase + 7);
239 }
240
das800_enable(struct comedi_device * dev)241 static void das800_enable(struct comedi_device *dev)
242 {
243 const struct das800_board *thisboard = dev->board_ptr;
244 struct das800_private *devpriv = dev->private;
245 unsigned long irq_flags;
246
247 spin_lock_irqsave(&dev->spinlock, irq_flags);
248 /* enable fifo-half full interrupts for cio-das802/16 */
249 if (thisboard->resolution == 16)
250 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
251 /* enable hardware triggering */
252 das800_ind_write(dev, CONV_HCEN, CONV_CONTROL);
253 /* enable card's interrupt */
254 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
255 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
256 }
257
das800_disable(struct comedi_device * dev)258 static void das800_disable(struct comedi_device *dev)
259 {
260 unsigned long irq_flags;
261
262 spin_lock_irqsave(&dev->spinlock, irq_flags);
263 /* disable hardware triggering of conversions */
264 das800_ind_write(dev, 0x0, CONV_CONTROL);
265 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
266 }
267
das800_cancel(struct comedi_device * dev,struct comedi_subdevice * s)268 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
269 {
270 das800_disable(dev);
271 return 0;
272 }
273
das800_ai_check_chanlist(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)274 static int das800_ai_check_chanlist(struct comedi_device *dev,
275 struct comedi_subdevice *s,
276 struct comedi_cmd *cmd)
277 {
278 unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
279 unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
280 int i;
281
282 for (i = 1; i < cmd->chanlist_len; i++) {
283 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
284 unsigned int range = CR_RANGE(cmd->chanlist[i]);
285
286 if (chan != (chan0 + i) % s->n_chan) {
287 dev_dbg(dev->class_dev,
288 "chanlist must be consecutive, counting upwards\n");
289 return -EINVAL;
290 }
291
292 if (range != range0) {
293 dev_dbg(dev->class_dev,
294 "chanlist must all have the same gain\n");
295 return -EINVAL;
296 }
297 }
298
299 return 0;
300 }
301
das800_ai_do_cmdtest(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)302 static int das800_ai_do_cmdtest(struct comedi_device *dev,
303 struct comedi_subdevice *s,
304 struct comedi_cmd *cmd)
305 {
306 const struct das800_board *thisboard = dev->board_ptr;
307 int err = 0;
308
309 /* Step 1 : check if triggers are trivially valid */
310
311 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
312 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
313 err |= comedi_check_trigger_src(&cmd->convert_src,
314 TRIG_TIMER | TRIG_EXT);
315 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
316 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
317
318 if (err)
319 return 1;
320
321 /* Step 2a : make sure trigger sources are unique */
322
323 err |= comedi_check_trigger_is_unique(cmd->start_src);
324 err |= comedi_check_trigger_is_unique(cmd->convert_src);
325 err |= comedi_check_trigger_is_unique(cmd->stop_src);
326
327 /* Step 2b : and mutually compatible */
328
329 if (err)
330 return 2;
331
332 /* Step 3: check if arguments are trivially valid */
333
334 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
335
336 if (cmd->convert_src == TRIG_TIMER) {
337 err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
338 thisboard->ai_speed);
339 }
340
341 err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
342 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
343 cmd->chanlist_len);
344
345 if (cmd->stop_src == TRIG_COUNT)
346 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
347 else /* TRIG_NONE */
348 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
349
350 if (err)
351 return 3;
352
353 /* step 4: fix up any arguments */
354
355 if (cmd->convert_src == TRIG_TIMER) {
356 unsigned int arg = cmd->convert_arg;
357
358 comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
359 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
360 }
361
362 if (err)
363 return 4;
364
365 /* Step 5: check channel list if it exists */
366 if (cmd->chanlist && cmd->chanlist_len > 0)
367 err |= das800_ai_check_chanlist(dev, s, cmd);
368
369 if (err)
370 return 5;
371
372 return 0;
373 }
374
das800_ai_do_cmd(struct comedi_device * dev,struct comedi_subdevice * s)375 static int das800_ai_do_cmd(struct comedi_device *dev,
376 struct comedi_subdevice *s)
377 {
378 const struct das800_board *thisboard = dev->board_ptr;
379 struct comedi_async *async = s->async;
380 struct comedi_cmd *cmd = &async->cmd;
381 unsigned int gain = CR_RANGE(cmd->chanlist[0]);
382 unsigned int start_chan = CR_CHAN(cmd->chanlist[0]);
383 unsigned int end_chan = (start_chan + cmd->chanlist_len - 1) % 8;
384 unsigned int scan_chans = (end_chan << 3) | start_chan;
385 int conv_bits;
386 unsigned long irq_flags;
387
388 das800_disable(dev);
389
390 spin_lock_irqsave(&dev->spinlock, irq_flags);
391 /* set scan limits */
392 das800_ind_write(dev, scan_chans, SCAN_LIMITS);
393 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
394
395 /* set gain */
396 if (thisboard->resolution == 12 && gain > 0)
397 gain += 0x7;
398 gain &= 0xf;
399 outb(gain, dev->iobase + DAS800_GAIN);
400
401 /* enable auto channel scan, send interrupts on end of conversion
402 * and set clock source to internal or external
403 */
404 conv_bits = 0;
405 conv_bits |= EACS | IEOC;
406 if (cmd->start_src == TRIG_EXT)
407 conv_bits |= DTEN;
408 if (cmd->convert_src == TRIG_TIMER) {
409 conv_bits |= CASC | ITE;
410 comedi_8254_update_divisors(dev->pacer);
411 comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
412 }
413
414 spin_lock_irqsave(&dev->spinlock, irq_flags);
415 das800_ind_write(dev, conv_bits, CONV_CONTROL);
416 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
417
418 das800_enable(dev);
419 return 0;
420 }
421
das800_ai_get_sample(struct comedi_device * dev)422 static unsigned int das800_ai_get_sample(struct comedi_device *dev)
423 {
424 unsigned int lsb = inb(dev->iobase + DAS800_LSB);
425 unsigned int msb = inb(dev->iobase + DAS800_MSB);
426
427 return (msb << 8) | lsb;
428 }
429
das800_interrupt(int irq,void * d)430 static irqreturn_t das800_interrupt(int irq, void *d)
431 {
432 struct comedi_device *dev = d;
433 struct das800_private *devpriv = dev->private;
434 struct comedi_subdevice *s = dev->read_subdev;
435 struct comedi_async *async;
436 struct comedi_cmd *cmd;
437 unsigned long irq_flags;
438 unsigned int status;
439 unsigned int val;
440 bool fifo_empty;
441 bool fifo_overflow;
442 int i;
443
444 status = inb(dev->iobase + DAS800_STATUS);
445 if (!(status & IRQ))
446 return IRQ_NONE;
447 if (!dev->attached)
448 return IRQ_HANDLED;
449
450 async = s->async;
451 cmd = &async->cmd;
452
453 spin_lock_irqsave(&dev->spinlock, irq_flags);
454 status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
455 /*
456 * Don't release spinlock yet since we want to make sure
457 * no one else disables hardware conversions.
458 */
459
460 /* if hardware conversions are not enabled, then quit */
461 if (status == 0) {
462 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
463 return IRQ_HANDLED;
464 }
465
466 for (i = 0; i < DAS802_16_HALF_FIFO_SZ; i++) {
467 val = das800_ai_get_sample(dev);
468 if (s->maxdata == 0x0fff) {
469 fifo_empty = !!(val & FIFO_EMPTY);
470 fifo_overflow = !!(val & FIFO_OVF);
471 } else {
472 /* cio-das802/16 has no fifo empty status bit */
473 fifo_empty = false;
474 fifo_overflow = !!(inb(dev->iobase + DAS800_GAIN) &
475 CIO_FFOV);
476 }
477 if (fifo_empty || fifo_overflow)
478 break;
479
480 if (s->maxdata == 0x0fff)
481 val >>= 4; /* 12-bit sample */
482
483 val &= s->maxdata;
484 comedi_buf_write_samples(s, &val, 1);
485
486 if (cmd->stop_src == TRIG_COUNT &&
487 async->scans_done >= cmd->stop_arg) {
488 async->events |= COMEDI_CB_EOA;
489 break;
490 }
491 }
492
493 if (fifo_overflow) {
494 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
495 async->events |= COMEDI_CB_ERROR;
496 comedi_handle_events(dev, s);
497 return IRQ_HANDLED;
498 }
499
500 if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
501 /*
502 * Re-enable card's interrupt.
503 * We already have spinlock, so indirect addressing is safe
504 */
505 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
506 CONTROL1);
507 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
508 } else {
509 /* otherwise, stop taking data */
510 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
511 das800_disable(dev);
512 }
513 comedi_handle_events(dev, s);
514 return IRQ_HANDLED;
515 }
516
das800_ai_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)517 static int das800_ai_eoc(struct comedi_device *dev,
518 struct comedi_subdevice *s,
519 struct comedi_insn *insn,
520 unsigned long context)
521 {
522 unsigned int status;
523
524 status = inb(dev->iobase + DAS800_STATUS);
525 if ((status & BUSY) == 0)
526 return 0;
527 return -EBUSY;
528 }
529
das800_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)530 static int das800_ai_insn_read(struct comedi_device *dev,
531 struct comedi_subdevice *s,
532 struct comedi_insn *insn,
533 unsigned int *data)
534 {
535 struct das800_private *devpriv = dev->private;
536 unsigned int chan = CR_CHAN(insn->chanspec);
537 unsigned int range = CR_RANGE(insn->chanspec);
538 unsigned long irq_flags;
539 unsigned int val;
540 int ret;
541 int i;
542
543 das800_disable(dev);
544
545 /* set multiplexer */
546 spin_lock_irqsave(&dev->spinlock, irq_flags);
547 das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1);
548 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
549
550 /* set gain / range */
551 if (s->maxdata == 0x0fff && range)
552 range += 0x7;
553 range &= 0xf;
554 outb(range, dev->iobase + DAS800_GAIN);
555
556 udelay(5);
557
558 for (i = 0; i < insn->n; i++) {
559 /* trigger conversion */
560 outb_p(0, dev->iobase + DAS800_MSB);
561
562 ret = comedi_timeout(dev, s, insn, das800_ai_eoc, 0);
563 if (ret)
564 return ret;
565
566 val = das800_ai_get_sample(dev);
567 if (s->maxdata == 0x0fff)
568 val >>= 4; /* 12-bit sample */
569 data[i] = val & s->maxdata;
570 }
571
572 return insn->n;
573 }
574
das800_di_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)575 static int das800_di_insn_bits(struct comedi_device *dev,
576 struct comedi_subdevice *s,
577 struct comedi_insn *insn,
578 unsigned int *data)
579 {
580 data[1] = (inb(dev->iobase + DAS800_STATUS) >> 4) & 0x7;
581
582 return insn->n;
583 }
584
das800_do_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)585 static int das800_do_insn_bits(struct comedi_device *dev,
586 struct comedi_subdevice *s,
587 struct comedi_insn *insn,
588 unsigned int *data)
589 {
590 struct das800_private *devpriv = dev->private;
591 unsigned long irq_flags;
592
593 if (comedi_dio_update_state(s, data)) {
594 devpriv->do_bits = s->state << 4;
595
596 spin_lock_irqsave(&dev->spinlock, irq_flags);
597 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
598 CONTROL1);
599 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
600 }
601
602 data[1] = s->state;
603
604 return insn->n;
605 }
606
das800_probe(struct comedi_device * dev)607 static int das800_probe(struct comedi_device *dev)
608 {
609 const struct das800_board *thisboard = dev->board_ptr;
610 int board = thisboard ? thisboard - das800_boards : -EINVAL;
611 int id_bits;
612 unsigned long irq_flags;
613
614 spin_lock_irqsave(&dev->spinlock, irq_flags);
615 id_bits = das800_ind_read(dev, ID) & 0x3;
616 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
617
618 switch (id_bits) {
619 case 0x0:
620 if (board == BOARD_DAS800 || board == BOARD_CIODAS800)
621 break;
622 dev_dbg(dev->class_dev, "Board model (probed): DAS-800\n");
623 board = BOARD_DAS800;
624 break;
625 case 0x2:
626 if (board == BOARD_DAS801 || board == BOARD_CIODAS801)
627 break;
628 dev_dbg(dev->class_dev, "Board model (probed): DAS-801\n");
629 board = BOARD_DAS801;
630 break;
631 case 0x3:
632 if (board == BOARD_DAS802 || board == BOARD_CIODAS802 ||
633 board == BOARD_CIODAS80216)
634 break;
635 dev_dbg(dev->class_dev, "Board model (probed): DAS-802\n");
636 board = BOARD_DAS802;
637 break;
638 default:
639 dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n",
640 id_bits);
641 board = -EINVAL;
642 break;
643 }
644 return board;
645 }
646
das800_attach(struct comedi_device * dev,struct comedi_devconfig * it)647 static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
648 {
649 const struct das800_board *thisboard;
650 struct das800_private *devpriv;
651 struct comedi_subdevice *s;
652 unsigned int irq = it->options[1];
653 unsigned long irq_flags;
654 int board;
655 int ret;
656
657 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
658 if (!devpriv)
659 return -ENOMEM;
660
661 ret = comedi_request_region(dev, it->options[0], 0x8);
662 if (ret)
663 return ret;
664
665 board = das800_probe(dev);
666 if (board < 0) {
667 dev_dbg(dev->class_dev, "unable to determine board type\n");
668 return -ENODEV;
669 }
670 dev->board_ptr = das800_boards + board;
671 thisboard = dev->board_ptr;
672 dev->board_name = thisboard->name;
673
674 if (irq > 1 && irq <= 7) {
675 ret = request_irq(irq, das800_interrupt, 0, dev->board_name,
676 dev);
677 if (ret == 0)
678 dev->irq = irq;
679 }
680
681 dev->pacer = comedi_8254_init(dev->iobase + DAS800_8254,
682 I8254_OSC_BASE_1MHZ, I8254_IO8, 0);
683 if (!dev->pacer)
684 return -ENOMEM;
685
686 ret = comedi_alloc_subdevices(dev, 3);
687 if (ret)
688 return ret;
689
690 /* Analog Input subdevice */
691 s = &dev->subdevices[0];
692 dev->read_subdev = s;
693 s->type = COMEDI_SUBD_AI;
694 s->subdev_flags = SDF_READABLE | SDF_GROUND;
695 s->n_chan = 8;
696 s->maxdata = (1 << thisboard->resolution) - 1;
697 s->range_table = thisboard->ai_range;
698 s->insn_read = das800_ai_insn_read;
699 if (dev->irq) {
700 s->subdev_flags |= SDF_CMD_READ;
701 s->len_chanlist = 8;
702 s->do_cmdtest = das800_ai_do_cmdtest;
703 s->do_cmd = das800_ai_do_cmd;
704 s->cancel = das800_cancel;
705 }
706
707 /* Digital Input subdevice */
708 s = &dev->subdevices[1];
709 s->type = COMEDI_SUBD_DI;
710 s->subdev_flags = SDF_READABLE;
711 s->n_chan = 3;
712 s->maxdata = 1;
713 s->range_table = &range_digital;
714 s->insn_bits = das800_di_insn_bits;
715
716 /* Digital Output subdevice */
717 s = &dev->subdevices[2];
718 s->type = COMEDI_SUBD_DO;
719 s->subdev_flags = SDF_WRITABLE;
720 s->n_chan = 4;
721 s->maxdata = 1;
722 s->range_table = &range_digital;
723 s->insn_bits = das800_do_insn_bits;
724
725 das800_disable(dev);
726
727 /* initialize digital out channels */
728 spin_lock_irqsave(&dev->spinlock, irq_flags);
729 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
730 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
731
732 return 0;
733 };
734
735 static struct comedi_driver driver_das800 = {
736 .driver_name = "das800",
737 .module = THIS_MODULE,
738 .attach = das800_attach,
739 .detach = comedi_legacy_detach,
740 .num_names = ARRAY_SIZE(das800_boards),
741 .board_name = &das800_boards[0].name,
742 .offset = sizeof(struct das800_board),
743 };
744 module_comedi_driver(driver_das800);
745
746 MODULE_AUTHOR("Comedi http://www.comedi.org");
747 MODULE_DESCRIPTION("Comedi low-level driver");
748 MODULE_LICENSE("GPL");
749