Staging: comedi: fix "foo * bar" should be "foo *bar"
[cascardo/linux.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 struct 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 struct 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, struct comedi_subdevice * s,
135         struct comedi_insn * insn, unsigned int * data);
136 static int das16m1_di_rbits(struct comedi_device * dev, struct comedi_subdevice * s,
137         struct comedi_insn * insn, unsigned int * data);
138 static int das16m1_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
139         struct comedi_insn * insn, unsigned int * data);
140
141 static int das16m1_cmd_test(struct comedi_device * dev, struct comedi_subdevice * s,
142         struct comedi_cmd * cmd);
143 static int das16m1_cmd_exec(struct comedi_device * dev, struct comedi_subdevice * s);
144 static int das16m1_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
145
146 static int das16m1_poll(struct comedi_device * dev, struct comedi_subdevice * s);
147 static irqreturn_t das16m1_interrupt(int irq, void *d);
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 struct das16m1_board {
156         const char *name;
157         unsigned int ai_speed;
158 };
159
160 static const struct 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, struct comedi_devconfig *it);
170 static int das16m1_detach(struct comedi_device *dev);
171 static struct 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 struct 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, struct comedi_subdevice *s,
204         struct 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, struct comedi_subdevice *s)
326 {
327         struct comedi_async *async = s->async;
328         struct 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, struct 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, struct comedi_subdevice *s,
397         struct 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, struct comedi_subdevice *s,
434         struct 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, struct comedi_subdevice *s,
446         struct 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, struct 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)
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         struct comedi_subdevice *s;
520         struct comedi_async *async;
521         struct 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, struct comedi_devconfig *it)
639 {
640         struct 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 }