staging: comedi: pcmuio: fix possible NULL deref on detach
[pandora-kernel.git] / drivers / staging / comedi / drivers / pcmuio.c
1 /*
2  * pcmuio.c
3  * Comedi driver for Winsystems PC-104 based 48/96-channel DIO boards.
4  *
5  * COMEDI - Linux Control and Measurement Device Interface
6  * Copyright (C) 2006 Calin A. Culianu <calin@ajvar.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  */
18
19 /*
20  * Driver: pcmuio
21  * Description: Winsystems PC-104 based 48/96-channel DIO boards.
22  * Devices: (Winsystems) PCM-UIO48A [pcmuio48]
23  *          (Winsystems) PCM-UIO96A [pcmuio96]
24  * Author: Calin Culianu <calin@ajvar.org>
25  * Updated: Fri, 13 Jan 2006 12:01:01 -0500
26  * Status: works
27  *
28  * A driver for the relatively straightforward-to-program PCM-UIO48A and
29  * PCM-UIO96A boards from Winsystems. These boards use either one or two
30  * (in the 96-DIO version) WS16C48 ASIC HighDensity I/O Chips (HDIO). This
31  * chip is interesting in that each I/O line is individually programmable
32  * for INPUT or OUTPUT (thus comedi_dio_config can be done on a per-channel
33  * basis). Also, each chip supports edge-triggered interrupts for the first
34  * 24 I/O lines. Of course, since the 96-channel version of the board has
35  * two ASICs, it can detect polarity changes on up to 48 I/O lines. Since
36  * this is essentially an (non-PnP) ISA board, I/O Address and IRQ selection
37  * are done through jumpers on the board. You need to pass that information
38  * to this driver as the first and second comedi_config option, respectively.
39  * Note that the 48-channel version uses 16 bytes of IO memory and the 96-
40  * channel version uses 32-bytes (in case you are worried about conflicts).
41  * The 48-channel board is split into two 24-channel comedi subdevices. The
42  * 96-channel board is split into 4 24-channel DIO subdevices.
43  *
44  * Note that IRQ support has been added, but it is untested.
45  *
46  * To use edge-detection IRQ support, pass the IRQs of both ASICS (for the
47  * 96 channel version) or just 1 ASIC (for 48-channel version). Then, use
48  * comedi_commands with TRIG_NOW. Your callback will be called each time an
49  * edge is triggered, and the data values will be two sample_t's, which
50  * should be concatenated to form one 32-bit unsigned int.  This value is
51  * the mask of channels that had edges detected from your channel list. Note
52  * that the bits positions in the mask correspond to positions in your
53  * chanlist when you specified the command and *not* channel id's!
54  *
55  * To set the polarity of the edge-detection interrupts pass a nonzero value
56  * for either CR_RANGE or CR_AREF for edge-up polarity, or a zero value for
57  * both CR_RANGE and CR_AREF if you want edge-down polarity.
58  *
59  * In the 48-channel version:
60  *
61  * On subdev 0, the first 24 channels channels are edge-detect channels.
62  *
63  * In the 96-channel board you have the following channels that can do edge
64  * detection:
65  *
66  * subdev 0, channels 0-24  (first 24 channels of 1st ASIC)
67  * subdev 2, channels 0-24  (first 24 channels of 2nd ASIC)
68  *
69  * Configuration Options:
70  *  [0] - I/O port base address
71  *  [1] - IRQ (for first ASIC, or first 24 channels)
72  *  [2] - IRQ (for second ASIC, pcmuio96 only - IRQ for chans 48-72
73  *             can be the same as first irq!)
74  */
75
76 #include <linux/module.h>
77 #include <linux/interrupt.h>
78 #include <linux/slab.h>
79
80 #include "../comedidev.h"
81
82 #include "comedi_fc.h"
83
84 /*
85  * Register I/O map
86  *
87  * Offset    Page 0       Page 1       Page 2       Page 3
88  * ------  -----------  -----------  -----------  -----------
89  *  0x00   Port 0 I/O   Port 0 I/O   Port 0 I/O   Port 0 I/O
90  *  0x01   Port 1 I/O   Port 1 I/O   Port 1 I/O   Port 1 I/O
91  *  0x02   Port 2 I/O   Port 2 I/O   Port 2 I/O   Port 2 I/O
92  *  0x03   Port 3 I/O   Port 3 I/O   Port 3 I/O   Port 3 I/O
93  *  0x04   Port 4 I/O   Port 4 I/O   Port 4 I/O   Port 4 I/O
94  *  0x05   Port 5 I/O   Port 5 I/O   Port 5 I/O   Port 5 I/O
95  *  0x06   INT_PENDING  INT_PENDING  INT_PENDING  INT_PENDING
96  *  0x07    Page/Lock    Page/Lock    Page/Lock    Page/Lock
97  *  0x08       N/A         POL_0       ENAB_0       INT_ID0
98  *  0x09       N/A         POL_1       ENAB_1       INT_ID1
99  *  0x0a       N/A         POL_2       ENAB_2       INT_ID2
100  */
101 #define PCMUIO_PORT_REG(x)              (0x00 + (x))
102 #define PCMUIO_INT_PENDING_REG          0x06
103 #define PCMUIO_PAGE_LOCK_REG            0x07
104 #define PCMUIO_LOCK_PORT(x)             ((1 << (x)) & 0x3f)
105 #define PCMUIO_PAGE(x)                  (((x) & 0x3) << 6)
106 #define PCMUIO_PAGE_MASK                PCMUIO_PAGE(3)
107 #define PCMUIO_PAGE_POL                 1
108 #define PCMUIO_PAGE_ENAB                2
109 #define PCMUIO_PAGE_INT_ID              3
110 #define PCMUIO_PAGE_REG(x)              (0x08 + (x))
111
112 #define PCMUIO_ASIC_IOSIZE              0x10
113 #define PCMUIO_MAX_ASICS                2
114
115 struct pcmuio_board {
116         const char *name;
117         const int num_asics;
118 };
119
120 static const struct pcmuio_board pcmuio_boards[] = {
121         {
122                 .name           = "pcmuio48",
123                 .num_asics      = 1,
124         }, {
125                 .name           = "pcmuio96",
126                 .num_asics      = 2,
127         },
128 };
129
130 struct pcmuio_subdev_private {
131         /* The below is only used for intr subdevices */
132         struct {
133                 /* if non-negative, this subdev has an interrupt asic */
134                 int asic;
135                 /*
136                  * subdev-relative channel mask for channels
137                  * we are interested in
138                  */
139                 int enabled_mask;
140                 int active;
141                 int stop_count;
142                 int continuous;
143                 spinlock_t spinlock;
144         } intr;
145 };
146
147 struct pcmuio_private {
148         struct {
149                 unsigned int irq;
150                 spinlock_t spinlock;
151         } asics[PCMUIO_MAX_ASICS];
152         struct pcmuio_subdev_private *sprivs;
153 };
154
155 static void pcmuio_write(struct comedi_device *dev, unsigned int val,
156                          int asic, int page, int port)
157 {
158         unsigned long iobase = dev->iobase + (asic * PCMUIO_ASIC_IOSIZE);
159
160         if (page == 0) {
161                 /* Port registers are valid for any page */
162                 outb(val & 0xff, iobase + PCMUIO_PORT_REG(port + 0));
163                 outb((val >> 8) & 0xff, iobase + PCMUIO_PORT_REG(port + 1));
164                 outb((val >> 16) & 0xff, iobase + PCMUIO_PORT_REG(port + 2));
165         } else {
166                 outb(PCMUIO_PAGE(page), iobase + PCMUIO_PAGE_LOCK_REG);
167                 outb(val & 0xff, iobase + PCMUIO_PAGE_REG(0));
168                 outb((val >> 8) & 0xff, iobase + PCMUIO_PAGE_REG(1));
169                 outb((val >> 16) & 0xff, iobase + PCMUIO_PAGE_REG(2));
170         }
171 }
172
173 static unsigned int pcmuio_read(struct comedi_device *dev,
174                                 int asic, int page, int port)
175 {
176         unsigned long iobase = dev->iobase + (asic * PCMUIO_ASIC_IOSIZE);
177         unsigned int val;
178
179         if (page == 0) {
180                 /* Port registers are valid for any page */
181                 val = inb(iobase + PCMUIO_PORT_REG(port + 0));
182                 val |= (inb(iobase + PCMUIO_PORT_REG(port + 1)) << 8);
183                 val |= (inb(iobase + PCMUIO_PORT_REG(port + 2)) << 16);
184         } else {
185                 outb(PCMUIO_PAGE(page), iobase + PCMUIO_PAGE_LOCK_REG);
186                 val = inb(iobase + PCMUIO_PAGE_REG(0));
187                 val |= (inb(iobase + PCMUIO_PAGE_REG(1)) << 8);
188                 val |= (inb(iobase + PCMUIO_PAGE_REG(2)) << 16);
189         }
190
191         return val;
192 }
193
194 /*
195  * Each channel can be individually programmed for input or output.
196  * Writing a '0' to a channel causes the corresponding output pin
197  * to go to a high-z state (pulled high by an external 10K resistor).
198  * This allows it to be used as an input. When used in the input mode,
199  * a read reflects the inverted state of the I/O pin, such that a
200  * high on the pin will read as a '0' in the register. Writing a '1'
201  * to a bit position causes the pin to sink current (up to 12mA),
202  * effectively pulling it low.
203  */
204 static int pcmuio_dio_insn_bits(struct comedi_device *dev,
205                                 struct comedi_subdevice *s,
206                                 struct comedi_insn *insn, unsigned int *data)
207 {
208         unsigned int mask = data[0] & s->io_bits;       /* outputs only */
209         unsigned int bits = data[1];
210         int asic = s->index / 2;
211         int port = (s->index % 2) ? 3 : 0;
212         unsigned int val;
213
214         /* get inverted state of the channels from the port */
215         val = pcmuio_read(dev, asic, 0, port);
216
217         /* get the true state of the channels */
218         s->state = val ^ ((0x1 << s->n_chan) - 1);
219
220         if (mask) {
221                 s->state &= ~mask;
222                 s->state |= (mask & bits);
223
224                 /* invert the state and update the channels */
225                 val = s->state ^ ((0x1 << s->n_chan) - 1);
226                 pcmuio_write(dev, val, asic, 0, port);
227         }
228
229         data[1] = s->state;
230
231         return insn->n;
232 }
233
234 static int pcmuio_dio_insn_config(struct comedi_device *dev,
235                                   struct comedi_subdevice *s,
236                                   struct comedi_insn *insn,
237                                   unsigned int *data)
238 {
239         int asic = s->index / 2;
240         int port = (s->index % 2) ? 3 : 0;
241         int ret;
242
243         ret = comedi_dio_insn_config(dev, s, insn, data, 0);
244         if (ret)
245                 return ret;
246
247         if (data[0] == INSN_CONFIG_DIO_INPUT)
248                 pcmuio_write(dev, s->io_bits, asic, 0, port);
249
250         return insn->n;
251 }
252
253 static void pcmuio_reset(struct comedi_device *dev)
254 {
255         const struct pcmuio_board *board = comedi_board(dev);
256         int asic;
257
258         for (asic = 0; asic < board->num_asics; ++asic) {
259                 /* first, clear all the DIO port bits */
260                 pcmuio_write(dev, 0, asic, 0, 0);
261                 pcmuio_write(dev, 0, asic, 0, 3);
262
263                 /* Next, clear all the paged registers for each page */
264                 pcmuio_write(dev, 0, asic, PCMUIO_PAGE_POL, 0);
265                 pcmuio_write(dev, 0, asic, PCMUIO_PAGE_ENAB, 0);
266                 pcmuio_write(dev, 0, asic, PCMUIO_PAGE_INT_ID, 0);
267         }
268 }
269
270 static void pcmuio_stop_intr(struct comedi_device *dev,
271                              struct comedi_subdevice *s)
272 {
273         struct pcmuio_subdev_private *subpriv = s->private;
274         int asic;
275
276         asic = subpriv->intr.asic;
277         if (asic < 0)
278                 return;         /* not an interrupt subdev */
279
280         subpriv->intr.enabled_mask = 0;
281         subpriv->intr.active = 0;
282         s->async->inttrig = NULL;
283
284         /* disable all intrs for this subdev.. */
285         pcmuio_write(dev, 0, asic, PCMUIO_PAGE_ENAB, 0);
286 }
287
288 static void pcmuio_handle_intr_subdev(struct comedi_device *dev,
289                                       struct comedi_subdevice *s,
290                                       unsigned triggered)
291 {
292         struct pcmuio_subdev_private *subpriv = s->private;
293         unsigned int len = s->async->cmd.chanlist_len;
294         unsigned oldevents = s->async->events;
295         unsigned int val = 0;
296         unsigned long flags;
297         unsigned mytrig;
298         unsigned int i;
299
300         spin_lock_irqsave(&subpriv->intr.spinlock, flags);
301
302         if (!subpriv->intr.active)
303                 goto done;
304
305         mytrig = triggered;
306         mytrig &= ((0x1 << s->n_chan) - 1);
307
308         if (!(mytrig & subpriv->intr.enabled_mask))
309                 goto done;
310
311         for (i = 0; i < len; i++) {
312                 unsigned int chan = CR_CHAN(s->async->cmd.chanlist[i]);
313                 if (mytrig & (1U << chan))
314                         val |= (1U << i);
315         }
316
317         /* Write the scan to the buffer. */
318         if (comedi_buf_put(s->async, ((short *)&val)[0]) &&
319             comedi_buf_put(s->async, ((short *)&val)[1])) {
320                 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
321         } else {
322                 /* Overflow! Stop acquisition!! */
323                 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
324                 pcmuio_stop_intr(dev, s);
325         }
326
327         /* Check for end of acquisition. */
328         if (!subpriv->intr.continuous) {
329                 /* stop_src == TRIG_COUNT */
330                 if (subpriv->intr.stop_count > 0) {
331                         subpriv->intr.stop_count--;
332                         if (subpriv->intr.stop_count == 0) {
333                                 s->async->events |= COMEDI_CB_EOA;
334                                 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
335                                 pcmuio_stop_intr(dev, s);
336                         }
337                 }
338         }
339
340 done:
341         spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
342
343         if (oldevents != s->async->events)
344                 comedi_event(dev, s);
345 }
346
347 static int pcmuio_handle_asic_interrupt(struct comedi_device *dev, int asic)
348 {
349         struct pcmuio_private *devpriv = dev->private;
350         struct pcmuio_subdev_private *subpriv;
351         unsigned long iobase = dev->iobase + (asic * PCMUIO_ASIC_IOSIZE);
352         unsigned int triggered = 0;
353         int got1 = 0;
354         unsigned long flags;
355         unsigned char int_pend;
356         int i;
357
358         spin_lock_irqsave(&devpriv->asics[asic].spinlock, flags);
359
360         int_pend = inb(iobase + PCMUIO_INT_PENDING_REG) & 0x07;
361         if (int_pend) {
362                 triggered = pcmuio_read(dev, asic, PCMUIO_PAGE_INT_ID, 0);
363                 pcmuio_write(dev, 0, asic, PCMUIO_PAGE_INT_ID, 0);
364
365                 ++got1;
366         }
367
368         spin_unlock_irqrestore(&devpriv->asics[asic].spinlock, flags);
369
370         if (triggered) {
371                 struct comedi_subdevice *s;
372                 /* TODO here: dispatch io lines to subdevs with commands.. */
373                 for (i = 0; i < dev->n_subdevices; i++) {
374                         s = &dev->subdevices[i];
375                         subpriv = s->private;
376                         if (subpriv->intr.asic == asic) {
377                                 /*
378                                  * This is an interrupt subdev, and it
379                                  * matches this asic!
380                                  */
381                                 pcmuio_handle_intr_subdev(dev, s,
382                                                           triggered);
383                         }
384                 }
385         }
386         return got1;
387 }
388
389 static irqreturn_t pcmuio_interrupt(int irq, void *d)
390 {
391         struct comedi_device *dev = d;
392         struct pcmuio_private *devpriv = dev->private;
393         int got1 = 0;
394         int asic;
395
396         for (asic = 0; asic < PCMUIO_MAX_ASICS; ++asic) {
397                 if (irq == devpriv->asics[asic].irq) {
398                         /* it is an interrupt for ASIC #asic */
399                         if (pcmuio_handle_asic_interrupt(dev, asic))
400                                 got1++;
401                 }
402         }
403         if (!got1)
404                 return IRQ_NONE;        /* interrupt from other source */
405         return IRQ_HANDLED;
406 }
407
408 static int pcmuio_start_intr(struct comedi_device *dev,
409                              struct comedi_subdevice *s)
410 {
411         struct pcmuio_subdev_private *subpriv = s->private;
412
413         if (!subpriv->intr.continuous && subpriv->intr.stop_count == 0) {
414                 /* An empty acquisition! */
415                 s->async->events |= COMEDI_CB_EOA;
416                 subpriv->intr.active = 0;
417                 return 1;
418         } else {
419                 unsigned bits = 0, pol_bits = 0, n;
420                 int asic;
421                 struct comedi_cmd *cmd = &s->async->cmd;
422
423                 asic = subpriv->intr.asic;
424                 if (asic < 0)
425                         return 1;       /* not an interrupt
426                                            subdev */
427                 subpriv->intr.enabled_mask = 0;
428                 subpriv->intr.active = 1;
429                 if (cmd->chanlist) {
430                         for (n = 0; n < cmd->chanlist_len; n++) {
431                                 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
432                                 pol_bits |= (CR_AREF(cmd->chanlist[n])
433                                              || CR_RANGE(cmd->
434                                                          chanlist[n]) ? 1U : 0U)
435                                     << CR_CHAN(cmd->chanlist[n]);
436                         }
437                 }
438                 bits &= ((0x1 << s->n_chan) - 1);
439                 subpriv->intr.enabled_mask = bits;
440
441                 /* set pol and enab intrs for this subdev.. */
442                 pcmuio_write(dev, pol_bits, asic, PCMUIO_PAGE_POL, 0);
443                 pcmuio_write(dev, bits, asic, PCMUIO_PAGE_ENAB, 0);
444         }
445         return 0;
446 }
447
448 static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
449 {
450         struct pcmuio_subdev_private *subpriv = s->private;
451         unsigned long flags;
452
453         spin_lock_irqsave(&subpriv->intr.spinlock, flags);
454         if (subpriv->intr.active)
455                 pcmuio_stop_intr(dev, s);
456         spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
457
458         return 0;
459 }
460
461 /*
462  * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
463  */
464 static int
465 pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
466                           unsigned int trignum)
467 {
468         struct pcmuio_subdev_private *subpriv = s->private;
469         unsigned long flags;
470         int event = 0;
471
472         if (trignum != 0)
473                 return -EINVAL;
474
475         spin_lock_irqsave(&subpriv->intr.spinlock, flags);
476         s->async->inttrig = NULL;
477         if (subpriv->intr.active)
478                 event = pcmuio_start_intr(dev, s);
479
480         spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
481
482         if (event)
483                 comedi_event(dev, s);
484
485         return 1;
486 }
487
488 /*
489  * 'do_cmd' function for an 'INTERRUPT' subdevice.
490  */
491 static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
492 {
493         struct pcmuio_subdev_private *subpriv = s->private;
494         struct comedi_cmd *cmd = &s->async->cmd;
495         unsigned long flags;
496         int event = 0;
497
498         spin_lock_irqsave(&subpriv->intr.spinlock, flags);
499         subpriv->intr.active = 1;
500
501         /* Set up end of acquisition. */
502         switch (cmd->stop_src) {
503         case TRIG_COUNT:
504                 subpriv->intr.continuous = 0;
505                 subpriv->intr.stop_count = cmd->stop_arg;
506                 break;
507         default:
508                 /* TRIG_NONE */
509                 subpriv->intr.continuous = 1;
510                 subpriv->intr.stop_count = 0;
511                 break;
512         }
513
514         /* Set up start of acquisition. */
515         switch (cmd->start_src) {
516         case TRIG_INT:
517                 s->async->inttrig = pcmuio_inttrig_start_intr;
518                 break;
519         default:
520                 /* TRIG_NOW */
521                 event = pcmuio_start_intr(dev, s);
522                 break;
523         }
524         spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
525
526         if (event)
527                 comedi_event(dev, s);
528
529         return 0;
530 }
531
532 static int pcmuio_cmdtest(struct comedi_device *dev,
533                           struct comedi_subdevice *s,
534                           struct comedi_cmd *cmd)
535 {
536         int err = 0;
537
538         /* Step 1 : check if triggers are trivially valid */
539
540         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
541         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
542         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
543         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
544         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
545
546         if (err)
547                 return 1;
548
549         /* Step 2a : make sure trigger sources are unique */
550
551         err |= cfc_check_trigger_is_unique(cmd->start_src);
552         err |= cfc_check_trigger_is_unique(cmd->stop_src);
553
554         /* Step 2b : and mutually compatible */
555
556         if (err)
557                 return 2;
558
559         /* Step 3: check if arguments are trivially valid */
560
561         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
562         err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
563         err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
564         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
565
566         switch (cmd->stop_src) {
567         case TRIG_COUNT:
568                 /* any count allowed */
569                 break;
570         case TRIG_NONE:
571                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
572                 break;
573         default:
574                 break;
575         }
576
577         if (err)
578                 return 3;
579
580         /* step 4: fix up any arguments */
581
582         /* if (err) return 4; */
583
584         return 0;
585 }
586
587 static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
588 {
589         const struct pcmuio_board *board = comedi_board(dev);
590         struct comedi_subdevice *s;
591         struct pcmuio_private *devpriv;
592         struct pcmuio_subdev_private *subpriv;
593         int sdev_no, n_subdevs, asic;
594         unsigned int irq[PCMUIO_MAX_ASICS];
595         int ret;
596
597         irq[0] = it->options[1];
598         irq[1] = it->options[2];
599
600         ret = comedi_request_region(dev, it->options[0],
601                                     board->num_asics * PCMUIO_ASIC_IOSIZE);
602         if (ret)
603                 return ret;
604
605         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
606         if (!devpriv)
607                 return -ENOMEM;
608
609         for (asic = 0; asic < PCMUIO_MAX_ASICS; ++asic)
610                 spin_lock_init(&devpriv->asics[asic].spinlock);
611
612         n_subdevs = board->num_asics * 2;
613         devpriv->sprivs = kcalloc(n_subdevs, sizeof(*subpriv), GFP_KERNEL);
614         if (!devpriv->sprivs)
615                 return -ENOMEM;
616
617         ret = comedi_alloc_subdevices(dev, n_subdevs);
618         if (ret)
619                 return ret;
620
621         for (sdev_no = 0; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
622                 s = &dev->subdevices[sdev_no];
623                 subpriv = &devpriv->sprivs[sdev_no];
624                 s->private = subpriv;
625                 s->maxdata = 1;
626                 s->range_table = &range_digital;
627                 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
628                 s->type = COMEDI_SUBD_DIO;
629                 s->insn_bits = pcmuio_dio_insn_bits;
630                 s->insn_config = pcmuio_dio_insn_config;
631                 s->n_chan = 24;
632
633                 /* subdevices 0 and 2 suppport interrupts */
634                 if ((sdev_no % 2) == 0) {
635                         /* setup the interrupt subdevice */
636                         subpriv->intr.asic = sdev_no / 2;
637                         dev->read_subdev = s;
638                         s->subdev_flags |= SDF_CMD_READ;
639                         s->cancel = pcmuio_cancel;
640                         s->do_cmd = pcmuio_cmd;
641                         s->do_cmdtest = pcmuio_cmdtest;
642                         s->len_chanlist = s->n_chan;
643                 } else {
644                         subpriv->intr.asic = -1;
645                         s->len_chanlist = 1;
646                 }
647                 spin_lock_init(&subpriv->intr.spinlock);
648         }
649
650         pcmuio_reset(dev);
651
652         for (asic = 0; irq[0] && asic < PCMUIO_MAX_ASICS; ++asic) {
653                 if (irq[asic]
654                     && request_irq(irq[asic], pcmuio_interrupt,
655                                    IRQF_SHARED, board->name, dev)) {
656                         int i;
657                         /* unroll the allocated irqs.. */
658                         for (i = asic - 1; i >= 0; --i) {
659                                 free_irq(irq[i], dev);
660                                 devpriv->asics[i].irq = irq[i] = 0;
661                         }
662                         irq[asic] = 0;
663                 }
664                 devpriv->asics[asic].irq = irq[asic];
665         }
666
667         return 0;
668 }
669
670 static void pcmuio_detach(struct comedi_device *dev)
671 {
672         struct pcmuio_private *devpriv = dev->private;
673         int i;
674
675         if (devpriv) {
676                 for (i = 0; i < PCMUIO_MAX_ASICS; ++i) {
677                         if (devpriv->asics[i].irq)
678                                 free_irq(devpriv->asics[i].irq, dev);
679                 }
680                 kfree(devpriv->sprivs);
681         }
682         comedi_legacy_detach(dev);
683 }
684
685 static struct comedi_driver pcmuio_driver = {
686         .driver_name    = "pcmuio",
687         .module         = THIS_MODULE,
688         .attach         = pcmuio_attach,
689         .detach         = pcmuio_detach,
690         .board_name     = &pcmuio_boards[0].name,
691         .offset         = sizeof(struct pcmuio_board),
692         .num_names      = ARRAY_SIZE(pcmuio_boards),
693 };
694 module_comedi_driver(pcmuio_driver);
695
696 MODULE_AUTHOR("Comedi http://www.comedi.org");
697 MODULE_DESCRIPTION("Comedi low-level driver");
698 MODULE_LICENSE("GPL");