Staging: comedi: Remove comedi_device typedef
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / staging / comedi / drivers / das16m1.c
1 /*
2 comedi/drivers/das16m1.c
3 CIO-DAS16/M1 driver
4 Author: Frank Mori Hess, based on code from the das16
5 driver.
6 Copyright (C) 2001 Frank Mori Hess <fmhess@users.sourceforge.net>
7
8 COMEDI - Linux Control and Measurement Device Interface
9 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
10
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24
25 ************************************************************************
26 */
27 /*
28 Driver: das16m1
29 Description: CIO-DAS16/M1
30 Author: Frank Mori Hess <fmhess@users.sourceforge.net>
31 Devices: [Measurement Computing] CIO-DAS16/M1 (cio-das16/m1)
32 Status: works
33
34 This driver supports a single board - the CIO-DAS16/M1.
35 As far as I know, there are no other boards that have
36 the same register layout. Even the CIO-DAS16/M1/16 is
37 significantly different.
38
39 I was _barely_ able to reach the full 1 MHz capability
40 of this board, using a hard real-time interrupt
41 (set the TRIG_RT flag in your comedi_cmd and use
42 rtlinux or RTAI). The board can't do dma, so the bottleneck is
43 pulling the data across the ISA bus. I timed the interrupt
44 handler, and it took my computer ~470 microseconds to pull 512
45 samples from the board. So at 1 Mhz sampling rate,
46 expect your CPU to be spending almost all of its
47 time in the interrupt handler.
48
49 This board has some unusual restrictions for its channel/gain list. If the
50 list has 2 or more channels in it, then two conditions must be satisfied:
51 (1) - even/odd channels must appear at even/odd indices in the list
52 (2) - the list must have an even number of entries.
53
54 Options:
55 [0] - base io address
56 [1] - irq (optional, but you probably want it)
57
58 irq can be omitted, although the cmd interface will not work without it.
59 */
60
61 #include <linux/ioport.h>
62 #include "../comedidev.h"
63
64 #include "8255.h"
65 #include "8253.h"
66 #include "comedi_fc.h"
67
68 #define DAS16M1_SIZE 16
69 #define DAS16M1_SIZE2 8
70
71 #define DAS16M1_XTAL 100 //10 MHz master clock
72
73 #define FIFO_SIZE 1024 // 1024 sample fifo
74
75 /*
76 CIO-DAS16_M1.pdf
77
78 "cio-das16/m1"
79
80 0 a/d bits 0-3, mux start 12 bit
81 1 a/d bits 4-11 unused
82 2 status control
83 3 di 4 bit do 4 bit
84 4 unused clear interrupt
85 5 interrupt, pacer
86 6 channel/gain queue address
87 7 channel/gain queue data
88 89ab 8254
89 cdef 8254
90 400 8255
91 404-407 8254
92
93 */
94
95 #define DAS16M1_AI 0 // 16-bit wide register
96 #define AI_CHAN(x) ((x) & 0xf)
97 #define DAS16M1_CS 2
98 #define EXT_TRIG_BIT 0x1
99 #define OVRUN 0x20
100 #define IRQDATA 0x80
101 #define DAS16M1_DIO 3
102 #define DAS16M1_CLEAR_INTR 4
103 #define DAS16M1_INTR_CONTROL 5
104 #define EXT_PACER 0x2
105 #define INT_PACER 0x3
106 #define PACER_MASK 0x3
107 #define INTE 0x80
108 #define DAS16M1_QUEUE_ADDR 6
109 #define DAS16M1_QUEUE_DATA 7
110 #define Q_CHAN(x) ((x) & 0x7)
111 #define Q_RANGE(x) (((x) & 0xf) << 4)
112 #define UNIPOLAR 0x40
113 #define DAS16M1_8254_FIRST 0x8
114 #define DAS16M1_8254_FIRST_CNTRL 0xb
115 #define TOTAL_CLEAR 0x30
116 #define DAS16M1_8254_SECOND 0xc
117 #define DAS16M1_82C55 0x400
118 #define DAS16M1_8254_THIRD 0x404
119
120 static const comedi_lrange range_das16m1 = { 9,
121 {
122 BIP_RANGE(5),
123 BIP_RANGE(2.5),
124 BIP_RANGE(1.25),
125 BIP_RANGE(0.625),
126 UNI_RANGE(10),
127 UNI_RANGE(5),
128 UNI_RANGE(2.5),
129 UNI_RANGE(1.25),
130 BIP_RANGE(10),
131 }
132 };
133
134 static int das16m1_do_wbits(struct comedi_device * dev, comedi_subdevice * s,
135 comedi_insn * insn, unsigned int * data);
136 static int das16m1_di_rbits(struct comedi_device * dev, comedi_subdevice * s,
137 comedi_insn * insn, unsigned int * data);
138 static int das16m1_ai_rinsn(struct comedi_device * dev, comedi_subdevice * s,
139 comedi_insn * insn, unsigned int * data);
140
141 static int das16m1_cmd_test(struct comedi_device * dev, comedi_subdevice * s,
142 comedi_cmd * cmd);
143 static int das16m1_cmd_exec(struct comedi_device * dev, comedi_subdevice * s);
144 static int das16m1_cancel(struct comedi_device * dev, comedi_subdevice * s);
145
146 static int das16m1_poll(struct comedi_device * dev, comedi_subdevice * s);
147 static irqreturn_t das16m1_interrupt(int irq, void *d PT_REGS_ARG);
148 static void das16m1_handler(struct comedi_device * dev, unsigned int status);
149
150 static unsigned int das16m1_set_pacer(struct comedi_device * dev, unsigned int ns,
151 int round_flag);
152
153 static int das16m1_irq_bits(unsigned int irq);
154
155 typedef struct das16m1_board_struct {
156 const char *name;
157 unsigned int ai_speed;
158 } das16m1_board;
159
160 static const das16m1_board das16m1_boards[] = {
161 {
162 name: "cio-das16/m1", // CIO-DAS16_M1.pdf
163 ai_speed:1000, // 1MHz max speed
164 },
165 };
166
167 #define das16m1_num_boards ((sizeof(das16m1_boards)) / (sizeof(das16m1_boards[0])))
168
169 static int das16m1_attach(struct comedi_device * dev, comedi_devconfig * it);
170 static int das16m1_detach(struct comedi_device * dev);
171 static comedi_driver driver_das16m1 = {
172 driver_name:"das16m1",
173 module:THIS_MODULE,
174 attach:das16m1_attach,
175 detach:das16m1_detach,
176 board_name:&das16m1_boards[0].name,
177 num_names:das16m1_num_boards,
178 offset:sizeof(das16m1_boards[0]),
179 };
180
181 struct das16m1_private_struct {
182 unsigned int control_state;
183 volatile unsigned int adc_count; // number of samples completed
184 /* initial value in lower half of hardware conversion counter,
185 * needed to keep track of whether new count has been loaded into
186 * counter yet (loaded by first sample conversion) */
187 u16 initial_hw_count;
188 short ai_buffer[FIFO_SIZE];
189 unsigned int do_bits; // saves status of digital output bits
190 unsigned int divisor1; // divides master clock to obtain conversion speed
191 unsigned int divisor2; // divides master clock to obtain conversion speed
192 };
193 #define devpriv ((struct das16m1_private_struct *)(dev->private))
194 #define thisboard ((const das16m1_board *)(dev->board_ptr))
195
196 COMEDI_INITCLEANUP(driver_das16m1);
197
198 static inline short munge_sample(short data)
199 {
200 return (data >> 4) & 0xfff;
201 }
202
203 static int das16m1_cmd_test(struct comedi_device * dev, comedi_subdevice * s,
204 comedi_cmd * cmd)
205 {
206 unsigned int err = 0, tmp, i;
207
208 /* make sure triggers are valid */
209 tmp = cmd->start_src;
210 cmd->start_src &= TRIG_NOW | TRIG_EXT;
211 if (!cmd->start_src || tmp != cmd->start_src)
212 err++;
213
214 tmp = cmd->scan_begin_src;
215 cmd->scan_begin_src &= TRIG_FOLLOW;
216 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
217 err++;
218
219 tmp = cmd->convert_src;
220 cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
221 if (!cmd->convert_src || tmp != cmd->convert_src)
222 err++;
223
224 tmp = cmd->scan_end_src;
225 cmd->scan_end_src &= TRIG_COUNT;
226 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
227 err++;
228
229 tmp = cmd->stop_src;
230 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
231 if (!cmd->stop_src || tmp != cmd->stop_src)
232 err++;
233
234 if (err)
235 return 1;
236
237 /* step 2: make sure trigger sources are unique and mutually compatible */
238 if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
239 err++;
240 if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
241 err++;
242 if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
243 err++;
244
245 if (err)
246 return 2;
247
248 /* step 3: make sure arguments are trivially compatible */
249 if (cmd->start_arg != 0) {
250 cmd->start_arg = 0;
251 err++;
252 }
253
254 if (cmd->scan_begin_src == TRIG_FOLLOW) {
255 /* internal trigger */
256 if (cmd->scan_begin_arg != 0) {
257 cmd->scan_begin_arg = 0;
258 err++;
259 }
260 }
261
262 if (cmd->convert_src == TRIG_TIMER) {
263 if (cmd->convert_arg < thisboard->ai_speed) {
264 cmd->convert_arg = thisboard->ai_speed;
265 err++;
266 }
267 }
268
269 if (cmd->scan_end_arg != cmd->chanlist_len) {
270 cmd->scan_end_arg = cmd->chanlist_len;
271 err++;
272 }
273
274 if (cmd->stop_src == TRIG_COUNT) {
275 /* any count is allowed */
276 } else {
277 /* TRIG_NONE */
278 if (cmd->stop_arg != 0) {
279 cmd->stop_arg = 0;
280 err++;
281 }
282 }
283
284 if (err)
285 return 3;
286
287 /* step 4: fix up arguments */
288
289 if (cmd->convert_src == TRIG_TIMER) {
290 tmp = cmd->convert_arg;
291 /* calculate counter values that give desired timing */
292 i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL,
293 &(devpriv->divisor1), &(devpriv->divisor2),
294 &(cmd->convert_arg), cmd->flags & TRIG_ROUND_MASK);
295 if (tmp != cmd->convert_arg)
296 err++;
297 }
298
299 if (err)
300 return 4;
301
302 // check chanlist against board's peculiarities
303 if (cmd->chanlist && cmd->chanlist_len > 1) {
304 for (i = 0; i < cmd->chanlist_len; i++) {
305 // even/odd channels must go into even/odd queue addresses
306 if ((i % 2) != (CR_CHAN(cmd->chanlist[i]) % 2)) {
307 comedi_error(dev, "bad chanlist:\n"
308 " even/odd channels must go have even/odd chanlist indices");
309 err++;
310 }
311 }
312 if ((cmd->chanlist_len % 2) != 0) {
313 comedi_error(dev,
314 "chanlist must be of even length or length 1");
315 err++;
316 }
317 }
318
319 if (err)
320 return 5;
321
322 return 0;
323 }
324
325 static int das16m1_cmd_exec(struct comedi_device * dev, comedi_subdevice * s)
326 {
327 comedi_async *async = s->async;
328 comedi_cmd *cmd = &async->cmd;
329 unsigned int byte, i;
330
331 if (dev->irq == 0) {
332 comedi_error(dev, "irq required to execute comedi_cmd");
333 return -1;
334 }
335
336 /* disable interrupts and internal pacer */
337 devpriv->control_state &= ~INTE & ~PACER_MASK;
338 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
339
340 // set software count
341 devpriv->adc_count = 0;
342 /* Initialize lower half of hardware counter, used to determine how
343 * many samples are in fifo. Value doesn't actually load into counter
344 * until counter's next clock (the next a/d conversion) */
345 i8254_load(dev->iobase + DAS16M1_8254_FIRST, 0, 1, 0, 2);
346 /* remember current reading of counter so we know when counter has
347 * actually been loaded */
348 devpriv->initial_hw_count =
349 i8254_read(dev->iobase + DAS16M1_8254_FIRST, 0, 1);
350 /* setup channel/gain queue */
351 for (i = 0; i < cmd->chanlist_len; i++) {
352 outb(i, dev->iobase + DAS16M1_QUEUE_ADDR);
353 byte = Q_CHAN(CR_CHAN(cmd->
354 chanlist[i])) | Q_RANGE(CR_RANGE(cmd->
355 chanlist[i]));
356 outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
357 }
358
359 /* set counter mode and counts */
360 cmd->convert_arg =
361 das16m1_set_pacer(dev, cmd->convert_arg,
362 cmd->flags & TRIG_ROUND_MASK);
363
364 // set control & status register
365 byte = 0;
366 /* if we are using external start trigger (also board dislikes having
367 * both start and conversion triggers external simultaneously) */
368 if (cmd->start_src == TRIG_EXT && cmd->convert_src != TRIG_EXT) {
369 byte |= EXT_TRIG_BIT;
370 }
371 outb(byte, dev->iobase + DAS16M1_CS);
372 /* clear interrupt bit */
373 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
374
375 /* enable interrupts and internal pacer */
376 devpriv->control_state &= ~PACER_MASK;
377 if (cmd->convert_src == TRIG_TIMER) {
378 devpriv->control_state |= INT_PACER;
379 } else {
380 devpriv->control_state |= EXT_PACER;
381 }
382 devpriv->control_state |= INTE;
383 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
384
385 return 0;
386 }
387
388 static int das16m1_cancel(struct comedi_device * dev, comedi_subdevice * s)
389 {
390 devpriv->control_state &= ~INTE & ~PACER_MASK;
391 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
392
393 return 0;
394 }
395
396 static int das16m1_ai_rinsn(struct comedi_device * dev, comedi_subdevice * s,
397 comedi_insn * insn, unsigned int * data)
398 {
399 int i, n;
400 int byte;
401 const int timeout = 1000;
402
403 /* disable interrupts and internal pacer */
404 devpriv->control_state &= ~INTE & ~PACER_MASK;
405 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
406
407 /* setup channel/gain queue */
408 outb(0, dev->iobase + DAS16M1_QUEUE_ADDR);
409 byte = Q_CHAN(CR_CHAN(insn->chanspec)) | Q_RANGE(CR_RANGE(insn->
410 chanspec));
411 outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
412
413 for (n = 0; n < insn->n; n++) {
414 /* clear IRQDATA bit */
415 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
416 /* trigger conversion */
417 outb(0, dev->iobase);
418
419 for (i = 0; i < timeout; i++) {
420 if (inb(dev->iobase + DAS16M1_CS) & IRQDATA)
421 break;
422 }
423 if (i == timeout) {
424 comedi_error(dev, "timeout");
425 return -ETIME;
426 }
427 data[n] = munge_sample(inw(dev->iobase));
428 }
429
430 return n;
431 }
432
433 static int das16m1_di_rbits(struct comedi_device * dev, comedi_subdevice * s,
434 comedi_insn * insn, unsigned int * data)
435 {
436 unsigned int bits;
437
438 bits = inb(dev->iobase + DAS16M1_DIO) & 0xf;
439 data[1] = bits;
440 data[0] = 0;
441
442 return 2;
443 }
444
445 static int das16m1_do_wbits(struct comedi_device * dev, comedi_subdevice * s,
446 comedi_insn * insn, unsigned int * data)
447 {
448 unsigned int wbits;
449
450 // only set bits that have been masked
451 data[0] &= 0xf;
452 wbits = devpriv->do_bits;
453 // zero bits that have been masked
454 wbits &= ~data[0];
455 // set masked bits
456 wbits |= data[0] & data[1];
457 devpriv->do_bits = wbits;
458 data[1] = wbits;
459
460 outb(devpriv->do_bits, dev->iobase + DAS16M1_DIO);
461
462 return 2;
463 }
464
465 static int das16m1_poll(struct comedi_device * dev, comedi_subdevice * s)
466 {
467 unsigned long flags;
468 unsigned int status;
469
470 // prevent race with interrupt handler
471 comedi_spin_lock_irqsave(&dev->spinlock, flags);
472 status = inb(dev->iobase + DAS16M1_CS);
473 das16m1_handler(dev, status);
474 comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
475
476 return s->async->buf_write_count - s->async->buf_read_count;
477 }
478
479 static irqreturn_t das16m1_interrupt(int irq, void *d PT_REGS_ARG)
480 {
481 int status;
482 struct comedi_device *dev = d;
483
484 if (dev->attached == 0) {
485 comedi_error(dev, "premature interrupt");
486 return IRQ_HANDLED;
487 }
488 // prevent race with comedi_poll()
489 spin_lock(&dev->spinlock);
490
491 status = inb(dev->iobase + DAS16M1_CS);
492
493 if ((status & (IRQDATA | OVRUN)) == 0) {
494 comedi_error(dev, "spurious interrupt");
495 spin_unlock(&dev->spinlock);
496 return IRQ_NONE;
497 }
498
499 das16m1_handler(dev, status);
500
501 /* clear interrupt */
502 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
503
504 spin_unlock(&dev->spinlock);
505 return IRQ_HANDLED;
506 }
507
508 static void munge_sample_array(short * array, unsigned int num_elements)
509 {
510 unsigned int i;
511
512 for (i = 0; i < num_elements; i++) {
513 array[i] = munge_sample(array[i]);
514 }
515 }
516
517 static void das16m1_handler(struct comedi_device * dev, unsigned int status)
518 {
519 comedi_subdevice *s;
520 comedi_async *async;
521 comedi_cmd *cmd;
522 u16 num_samples;
523 u16 hw_counter;
524
525 s = dev->read_subdev;
526 async = s->async;
527 async->events = 0;
528 cmd = &async->cmd;
529
530 // figure out how many samples are in fifo
531 hw_counter = i8254_read(dev->iobase + DAS16M1_8254_FIRST, 0, 1);
532 /* make sure hardware counter reading is not bogus due to initial value
533 * not having been loaded yet */
534 if (devpriv->adc_count == 0 && hw_counter == devpriv->initial_hw_count) {
535 num_samples = 0;
536 } else {
537 /* The calculation of num_samples looks odd, but it uses the following facts.
538 * 16 bit hardware counter is initialized with value of zero (which really
539 * means 0x1000). The counter decrements by one on each conversion
540 * (when the counter decrements from zero it goes to 0xffff). num_samples
541 * is a 16 bit variable, so it will roll over in a similar fashion to the
542 * hardware counter. Work it out, and this is what you get. */
543 num_samples = -hw_counter - devpriv->adc_count;
544 }
545 // check if we only need some of the points
546 if (cmd->stop_src == TRIG_COUNT) {
547 if (num_samples > cmd->stop_arg * cmd->chanlist_len)
548 num_samples = cmd->stop_arg * cmd->chanlist_len;
549 }
550 // make sure we dont try to get too many points if fifo has overrun
551 if (num_samples > FIFO_SIZE)
552 num_samples = FIFO_SIZE;
553 insw(dev->iobase, devpriv->ai_buffer, num_samples);
554 munge_sample_array(devpriv->ai_buffer, num_samples);
555 cfc_write_array_to_buffer(s, devpriv->ai_buffer,
556 num_samples * sizeof(short));
557 devpriv->adc_count += num_samples;
558
559 if (cmd->stop_src == TRIG_COUNT) {
560 if (devpriv->adc_count >= cmd->stop_arg * cmd->chanlist_len) { /* end of acquisition */
561 das16m1_cancel(dev, s);
562 async->events |= COMEDI_CB_EOA;
563 }
564 }
565
566 /* this probably won't catch overruns since the card doesn't generate
567 * overrun interrupts, but we might as well try */
568 if (status & OVRUN) {
569 das16m1_cancel(dev, s);
570 async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
571 comedi_error(dev, "fifo overflow");
572 }
573
574 comedi_event(dev, s);
575
576 }
577
578 /* This function takes a time in nanoseconds and sets the *
579 * 2 pacer clocks to the closest frequency possible. It also *
580 * returns the actual sampling period. */
581 static unsigned int das16m1_set_pacer(struct comedi_device * dev, unsigned int ns,
582 int rounding_flags)
583 {
584 i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL, &(devpriv->divisor1),
585 &(devpriv->divisor2), &ns, rounding_flags & TRIG_ROUND_MASK);
586
587 /* Write the values of ctr1 and ctr2 into counters 1 and 2 */
588 i8254_load(dev->iobase + DAS16M1_8254_SECOND, 0, 1, devpriv->divisor1,
589 2);
590 i8254_load(dev->iobase + DAS16M1_8254_SECOND, 0, 2, devpriv->divisor2,
591 2);
592
593 return ns;
594 }
595
596 static int das16m1_irq_bits(unsigned int irq)
597 {
598 int ret;
599
600 switch (irq) {
601 case 10:
602 ret = 0x0;
603 break;
604 case 11:
605 ret = 0x1;
606 break;
607 case 12:
608 ret = 0x2;
609 break;
610 case 15:
611 ret = 0x3;
612 break;
613 case 2:
614 ret = 0x4;
615 break;
616 case 3:
617 ret = 0x5;
618 break;
619 case 5:
620 ret = 0x6;
621 break;
622 case 7:
623 ret = 0x7;
624 break;
625 default:
626 return -1;
627 break;
628 }
629 return (ret << 4);
630 }
631
632 /*
633 * Options list:
634 * 0 I/O base
635 * 1 IRQ
636 */
637
638 static int das16m1_attach(struct comedi_device * dev, comedi_devconfig * it)
639 {
640 comedi_subdevice *s;
641 int ret;
642 unsigned int irq;
643 unsigned long iobase;
644
645 iobase = it->options[0];
646
647 printk("comedi%d: das16m1:", dev->minor);
648
649 if ((ret = alloc_private(dev,
650 sizeof(struct das16m1_private_struct))) < 0)
651 return ret;
652
653 dev->board_name = thisboard->name;
654
655 printk(" io 0x%lx-0x%lx 0x%lx-0x%lx",
656 iobase, iobase + DAS16M1_SIZE,
657 iobase + DAS16M1_82C55, iobase + DAS16M1_82C55 + DAS16M1_SIZE2);
658 if (!request_region(iobase, DAS16M1_SIZE, driver_das16m1.driver_name)) {
659 printk(" I/O port conflict\n");
660 return -EIO;
661 }
662 if (!request_region(iobase + DAS16M1_82C55, DAS16M1_SIZE2,
663 driver_das16m1.driver_name)) {
664 release_region(iobase, DAS16M1_SIZE);
665 printk(" I/O port conflict\n");
666 return -EIO;
667 }
668 dev->iobase = iobase;
669
670 /* now for the irq */
671 irq = it->options[1];
672 // make sure it is valid
673 if (das16m1_irq_bits(irq) >= 0) {
674 ret = comedi_request_irq(irq, das16m1_interrupt, 0,
675 driver_das16m1.driver_name, dev);
676 if (ret < 0) {
677 printk(", irq unavailable\n");
678 return ret;
679 }
680 dev->irq = irq;
681 printk(", irq %u\n", irq);
682 } else if (irq == 0) {
683 printk(", no irq\n");
684 } else {
685 printk(", invalid irq\n"
686 " valid irqs are 2, 3, 5, 7, 10, 11, 12, or 15\n");
687 return -EINVAL;
688 }
689
690 if ((ret = alloc_subdevices(dev, 4)) < 0)
691 return ret;
692
693 s = dev->subdevices + 0;
694 dev->read_subdev = s;
695 /* ai */
696 s->type = COMEDI_SUBD_AI;
697 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
698 s->n_chan = 8;
699 s->subdev_flags = SDF_DIFF;
700 s->len_chanlist = 256;
701 s->maxdata = (1 << 12) - 1;
702 s->range_table = &range_das16m1;
703 s->insn_read = das16m1_ai_rinsn;
704 s->do_cmdtest = das16m1_cmd_test;
705 s->do_cmd = das16m1_cmd_exec;
706 s->cancel = das16m1_cancel;
707 s->poll = das16m1_poll;
708
709 s = dev->subdevices + 1;
710 /* di */
711 s->type = COMEDI_SUBD_DI;
712 s->subdev_flags = SDF_READABLE;
713 s->n_chan = 4;
714 s->maxdata = 1;
715 s->range_table = &range_digital;
716 s->insn_bits = das16m1_di_rbits;
717
718 s = dev->subdevices + 2;
719 /* do */
720 s->type = COMEDI_SUBD_DO;
721 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
722 s->n_chan = 4;
723 s->maxdata = 1;
724 s->range_table = &range_digital;
725 s->insn_bits = das16m1_do_wbits;
726
727 s = dev->subdevices + 3;
728 /* 8255 */
729 subdev_8255_init(dev, s, NULL, dev->iobase + DAS16M1_82C55);
730
731 // disable upper half of hardware conversion counter so it doesn't mess with us
732 outb(TOTAL_CLEAR, dev->iobase + DAS16M1_8254_FIRST_CNTRL);
733
734 // initialize digital output lines
735 outb(devpriv->do_bits, dev->iobase + DAS16M1_DIO);
736
737 /* set the interrupt level */
738 if (dev->irq)
739 devpriv->control_state = das16m1_irq_bits(dev->irq);
740 else
741 devpriv->control_state = 0;
742 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
743
744 return 0;
745 }
746
747 static int das16m1_detach(struct comedi_device * dev)
748 {
749 printk("comedi%d: das16m1: remove\n", dev->minor);
750
751 // das16m1_reset(dev);
752
753 if (dev->subdevices)
754 subdev_8255_cleanup(dev, dev->subdevices + 3);
755
756 if (dev->irq)
757 comedi_free_irq(dev->irq, dev);
758
759 if (dev->iobase) {
760 release_region(dev->iobase, DAS16M1_SIZE);
761 release_region(dev->iobase + DAS16M1_82C55, DAS16M1_SIZE2);
762 }
763
764 return 0;
765 }