]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - drivers/staging/comedi/drivers/amplc_pc236.c
Staging: comedi: fix printk() coding style issue in ni_labpc.c
[mirror_ubuntu-hirsute-kernel.git] / drivers / staging / comedi / drivers / amplc_pc236.c
CommitLineData
6a5c8664
IA
1/*
2 comedi/drivers/amplc_pc236.c
3 Driver for Amplicon PC36AT and PCI236 DIO boards.
4
5 Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/>
6
7 COMEDI - Linux Control and Measurement Device Interface
8 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23
24*/
25/*
26Driver: amplc_pc236
27Description: Amplicon PC36AT, PCI236
28Author: Ian Abbott <abbotti@mev.co.uk>
29Devices: [Amplicon] PC36AT (pc36at), PCI236 (pci236 or amplc_pc236)
0d6e5dad 30Updated: Wed, 01 Apr 2009 15:41:25 +0100
6a5c8664
IA
31Status: works
32
33Configuration options - PC36AT:
34 [0] - I/O port base address
35 [1] - IRQ (optional)
36
37Configuration options - PCI236:
38 [0] - PCI bus of device (optional)
39 [1] - PCI slot of device (optional)
40 If bus/slot is not specified, the first available PCI device will be
41 used.
42
43The PC36AT ISA board and PCI236 PCI board have a single 8255 appearing
44as subdevice 0.
45
46Subdevice 1 pretends to be a digital input device, but it always returns
470 when read. However, if you run a command with scan_begin_src=TRIG_EXT,
0d6e5dad 48a rising edge on port C bit 3 acts as an external trigger, which can be
6a5c8664
IA
49used to wake up tasks. This is like the comedi_parport device, but the
50only way to physically disable the interrupt on the PC36AT is to remove
51the IRQ jumper. If no interrupt is connected, then subdevice 1 is
52unused.
53*/
54
70265d24
JS
55#include <linux/interrupt.h>
56
6a5c8664
IA
57#include "../comedidev.h"
58
59#include "comedi_pci.h"
60
61#include "8255.h"
62#include "plx9052.h"
63
64#define PC236_DRIVER_NAME "amplc_pc236"
65
66/* PCI236 PCI configuration register information */
67#define PCI_VENDOR_ID_AMPLICON 0x14dc
68#define PCI_DEVICE_ID_AMPLICON_PCI236 0x0009
69#define PCI_DEVICE_ID_INVALID 0xffff
70
71/* PC36AT / PCI236 registers */
72
73#define PC236_IO_SIZE 4
74#define PC236_LCR_IO_SIZE 128
75
76/*
77 * INTCSR values for PCI236.
78 */
79/* Disable interrupt, also clear any interrupt there */
53106ae6 80#define PCI236_INTR_DISABLE (PLX9052_INTCSR_LI1ENAB_DISABLED \
9f7a344b
BA
81 | PLX9052_INTCSR_LI1POL_HIGH \
82 | PLX9052_INTCSR_LI2POL_HIGH \
83 | PLX9052_INTCSR_PCIENAB_DISABLED \
84 | PLX9052_INTCSR_LI1SEL_EDGE \
85 | PLX9052_INTCSR_LI1CLRINT_ASSERTED)
6a5c8664 86/* Enable interrupt, also clear any interrupt there. */
53106ae6 87#define PCI236_INTR_ENABLE (PLX9052_INTCSR_LI1ENAB_ENABLED \
9f7a344b
BA
88 | PLX9052_INTCSR_LI1POL_HIGH \
89 | PLX9052_INTCSR_LI2POL_HIGH \
90 | PLX9052_INTCSR_PCIENAB_ENABLED \
91 | PLX9052_INTCSR_LI1SEL_EDGE \
92 | PLX9052_INTCSR_LI1CLRINT_ASSERTED)
6a5c8664
IA
93
94/*
95 * Board descriptions for Amplicon PC36AT and PCI236.
96 */
97
98enum pc236_bustype { isa_bustype, pci_bustype };
99enum pc236_model { pc36at_model, pci236_model, anypci_model };
100
57ad8696 101struct pc236_board {
6a5c8664
IA
102 const char *name;
103 const char *fancy_name;
104 unsigned short devid;
105 enum pc236_bustype bustype;
106 enum pc236_model model;
57ad8696
BP
107};
108static const struct pc236_board pc236_boards[] = {
6a5c8664 109 {
0a85b6f0
MT
110 .name = "pc36at",
111 .fancy_name = "PC36AT",
112 .bustype = isa_bustype,
113 .model = pc36at_model,
114 },
6a5c8664
IA
115#ifdef CONFIG_COMEDI_PCI
116 {
0a85b6f0
MT
117 .name = "pci236",
118 .fancy_name = "PCI236",
119 .devid = PCI_DEVICE_ID_AMPLICON_PCI236,
120 .bustype = pci_bustype,
121 .model = pci236_model,
122 },
6a5c8664
IA
123#endif
124#ifdef CONFIG_COMEDI_PCI
125 {
0a85b6f0
MT
126 .name = PC236_DRIVER_NAME,
127 .fancy_name = PC236_DRIVER_NAME,
128 .devid = PCI_DEVICE_ID_INVALID,
129 .bustype = pci_bustype,
130 .model = anypci_model, /* wildcard */
131 },
6a5c8664
IA
132#endif
133};
134
135#ifdef CONFIG_COMEDI_PCI
136static DEFINE_PCI_DEVICE_TABLE(pc236_pci_table) = {
0a85b6f0
MT
137 {
138 PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI236,
139 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
140 0}
6a5c8664
IA
141};
142
143MODULE_DEVICE_TABLE(pci, pc236_pci_table);
144#endif /* CONFIG_COMEDI_PCI */
145
146/*
147 * Useful for shorthand access to the particular board structure
148 */
57ad8696 149#define thisboard ((const struct pc236_board *)dev->board_ptr)
6a5c8664
IA
150
151/* this structure is for data unique to this hardware driver. If
152 several hardware drivers keep similar information in this structure,
9f7a344b
BA
153 feel free to suggest moving the variable to the struct comedi_device struct.
154 */
f1ee810a 155struct pc236_private {
6a5c8664
IA
156#ifdef CONFIG_COMEDI_PCI
157 /* PCI device */
158 struct pci_dev *pci_dev;
9f7a344b 159 unsigned long lcr_iobase; /* PLX PCI9052 config registers in PCIBAR1 */
6a5c8664
IA
160#endif
161 int enable_irq;
f1ee810a 162};
6a5c8664 163
f1ee810a 164#define devpriv ((struct pc236_private *)dev->private)
6a5c8664
IA
165
166/*
139dfbdf 167 * The struct comedi_driver structure tells the Comedi core module
6a5c8664
IA
168 * which functions to call to configure/deconfigure (attach/detach)
169 * the board, and also about the kernel module that contains
170 * the device code.
171 */
da91b269
BP
172static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it);
173static int pc236_detach(struct comedi_device *dev);
139dfbdf 174static struct comedi_driver driver_amplc_pc236 = {
68c3dbff
BP
175 .driver_name = PC236_DRIVER_NAME,
176 .module = THIS_MODULE,
177 .attach = pc236_attach,
178 .detach = pc236_detach,
179 .board_name = &pc236_boards[0].name,
180 .offset = sizeof(struct pc236_board),
8629efa4 181 .num_names = ARRAY_SIZE(pc236_boards),
6a5c8664
IA
182};
183
184#ifdef CONFIG_COMEDI_PCI
185COMEDI_PCI_INITCLEANUP(driver_amplc_pc236, pc236_pci_table);
186#else
187COMEDI_INITCLEANUP(driver_amplc_pc236);
188#endif
189
190static int pc236_request_region(unsigned minor, unsigned long from,
0a85b6f0 191 unsigned long extent);
814900c9
BP
192static void pc236_intr_disable(struct comedi_device *dev);
193static void pc236_intr_enable(struct comedi_device *dev);
194static int pc236_intr_check(struct comedi_device *dev);
0a85b6f0
MT
195static int pc236_intr_insn(struct comedi_device *dev,
196 struct comedi_subdevice *s, struct comedi_insn *insn,
197 unsigned int *data);
198static int pc236_intr_cmdtest(struct comedi_device *dev,
199 struct comedi_subdevice *s,
200 struct comedi_cmd *cmd);
201static int pc236_intr_cmd(struct comedi_device *dev,
202 struct comedi_subdevice *s);
203static int pc236_intr_cancel(struct comedi_device *dev,
204 struct comedi_subdevice *s);
70265d24 205static irqreturn_t pc236_interrupt(int irq, void *d);
6a5c8664
IA
206
207/*
208 * This function looks for a PCI device matching the requested board name,
209 * bus and slot.
210 */
211#ifdef CONFIG_COMEDI_PCI
212static int
da91b269 213pc236_find_pci(struct comedi_device *dev, int bus, int slot,
0a85b6f0 214 struct pci_dev **pci_dev_p)
6a5c8664
IA
215{
216 struct pci_dev *pci_dev = NULL;
217
218 *pci_dev_p = NULL;
219
220 /* Look for matching PCI device. */
221 for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
0a85b6f0
MT
222 pci_dev != NULL;
223 pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
224 PCI_ANY_ID, pci_dev)) {
6a5c8664
IA
225 /* If bus/slot specified, check them. */
226 if (bus || slot) {
227 if (bus != pci_dev->bus->number
0a85b6f0 228 || slot != PCI_SLOT(pci_dev->devfn))
6a5c8664
IA
229 continue;
230 }
231 if (thisboard->model == anypci_model) {
232 /* Match any supported model. */
233 int i;
234
235 for (i = 0; i < ARRAY_SIZE(pc236_boards); i++) {
236 if (pc236_boards[i].bustype != pci_bustype)
237 continue;
238 if (pci_dev->device == pc236_boards[i].devid) {
239 /* Change board_ptr to matched board. */
240 dev->board_ptr = &pc236_boards[i];
241 break;
242 }
243 }
244 if (i == ARRAY_SIZE(pc236_boards))
245 continue;
246 } else {
247 /* Match specific model name. */
248 if (pci_dev->device != thisboard->devid)
249 continue;
250 }
251
252 /* Found a match. */
253 *pci_dev_p = pci_dev;
254 return 0;
255 }
256 /* No match found. */
257 if (bus || slot) {
258 printk(KERN_ERR
0a85b6f0
MT
259 "comedi%d: error! no %s found at pci %02x:%02x!\n",
260 dev->minor, thisboard->name, bus, slot);
6a5c8664
IA
261 } else {
262 printk(KERN_ERR "comedi%d: error! no %s found!\n",
0a85b6f0 263 dev->minor, thisboard->name);
6a5c8664
IA
264 }
265 return -EIO;
266}
267#endif
268
269/*
270 * Attach is called by the Comedi core to configure the driver
271 * for a particular board. If you specified a board_name array
272 * in the driver structure, dev->board_ptr contains that
273 * address.
274 */
da91b269 275static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it)
6a5c8664 276{
34c43922 277 struct comedi_subdevice *s;
6a5c8664
IA
278 unsigned long iobase = 0;
279 unsigned int irq = 0;
280#ifdef CONFIG_COMEDI_PCI
281 struct pci_dev *pci_dev = NULL;
282 int bus = 0, slot = 0;
283#endif
284 int share_irq = 0;
285 int ret;
286
287 printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor,
0a85b6f0 288 PC236_DRIVER_NAME);
6a5c8664
IA
289/*
290 * Allocate the private structure area. alloc_private() is a
291 * convenient macro defined in comedidev.h.
292 */
c3744138
BP
293 ret = alloc_private(dev, sizeof(struct pc236_private));
294 if (ret < 0) {
6a5c8664 295 printk(KERN_ERR "comedi%d: error! out of memory!\n",
0a85b6f0 296 dev->minor);
6a5c8664
IA
297 return ret;
298 }
299 /* Process options. */
300 switch (thisboard->bustype) {
301 case isa_bustype:
302 iobase = it->options[0];
303 irq = it->options[1];
304 share_irq = 0;
305 break;
306#ifdef CONFIG_COMEDI_PCI
307 case pci_bustype:
308 bus = it->options[0];
309 slot = it->options[1];
310 share_irq = 1;
311
c3744138
BP
312 ret = pc236_find_pci(dev, bus, slot, &pci_dev);
313 if (ret < 0)
6a5c8664
IA
314 return ret;
315 devpriv->pci_dev = pci_dev;
316 break;
317#endif /* CONFIG_COMEDI_PCI */
318 default:
319 printk(KERN_ERR
0a85b6f0
MT
320 "comedi%d: %s: BUG! cannot determine board type!\n",
321 dev->minor, PC236_DRIVER_NAME);
6a5c8664
IA
322 return -EINVAL;
323 break;
324 }
325
326/*
327 * Initialize dev->board_name.
328 */
329 dev->board_name = thisboard->name;
330
331 /* Enable device and reserve I/O spaces. */
332#ifdef CONFIG_COMEDI_PCI
333 if (pci_dev) {
c3744138
BP
334
335 ret = comedi_pci_enable(pci_dev, PC236_DRIVER_NAME);
336 if (ret < 0) {
6a5c8664 337 printk(KERN_ERR
0a85b6f0
MT
338 "comedi%d: error! cannot enable PCI device and request regions!\n",
339 dev->minor);
6a5c8664
IA
340 return ret;
341 }
342 devpriv->lcr_iobase = pci_resource_start(pci_dev, 1);
343 iobase = pci_resource_start(pci_dev, 2);
344 irq = pci_dev->irq;
345 } else
346#endif
347 {
348 ret = pc236_request_region(dev->minor, iobase, PC236_IO_SIZE);
9f7a344b 349 if (ret < 0)
6a5c8664 350 return ret;
6a5c8664
IA
351 }
352 dev->iobase = iobase;
353
354/*
355 * Allocate the subdevice structures. alloc_subdevice() is a
356 * convenient macro defined in comedidev.h.
357 */
c3744138
BP
358 ret = alloc_subdevices(dev, 2);
359 if (ret < 0) {
6a5c8664 360 printk(KERN_ERR "comedi%d: error! out of memory!\n",
0a85b6f0 361 dev->minor);
6a5c8664
IA
362 return ret;
363 }
364
365 s = dev->subdevices + 0;
366 /* digital i/o subdevice (8255) */
c3744138
BP
367 ret = subdev_8255_init(dev, s, NULL, iobase);
368 if (ret < 0) {
6a5c8664 369 printk(KERN_ERR "comedi%d: error! out of memory!\n",
0a85b6f0 370 dev->minor);
6a5c8664
IA
371 return ret;
372 }
373 s = dev->subdevices + 1;
374 dev->read_subdev = s;
375 s->type = COMEDI_SUBD_UNUSED;
376 pc236_intr_disable(dev);
377 if (irq) {
378 unsigned long flags = share_irq ? IRQF_SHARED : 0;
379
5f74ea14 380 if (request_irq(irq, pc236_interrupt, flags,
6a5c8664
IA
381 PC236_DRIVER_NAME, dev) >= 0) {
382 dev->irq = irq;
383 s->type = COMEDI_SUBD_DI;
384 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
385 s->n_chan = 1;
386 s->maxdata = 1;
387 s->range_table = &range_digital;
388 s->insn_bits = pc236_intr_insn;
389 s->do_cmdtest = pc236_intr_cmdtest;
390 s->do_cmd = pc236_intr_cmd;
391 s->cancel = pc236_intr_cancel;
392 }
393 }
394 printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name);
395 if (thisboard->bustype == isa_bustype) {
396 printk("(base %#lx) ", iobase);
397 } else {
398#ifdef CONFIG_COMEDI_PCI
399 printk("(pci %s) ", pci_name(pci_dev));
400#endif
401 }
9f7a344b 402 if (irq)
6a5c8664 403 printk("(irq %u%s) ", irq, (dev->irq ? "" : " UNAVAILABLE"));
9f7a344b 404 else
6a5c8664 405 printk("(no irq) ");
6a5c8664
IA
406
407 printk("attached\n");
408
409 return 1;
410}
411
412/*
413 * _detach is called to deconfigure a device. It should deallocate
414 * resources.
415 * This function is also called when _attach() fails, so it should be
416 * careful not to release resources that were not necessarily
417 * allocated by _attach(). dev->private and dev->subdevices are
418 * deallocated automatically by the core.
419 */
da91b269 420static int pc236_detach(struct comedi_device *dev)
6a5c8664
IA
421{
422 printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor,
0a85b6f0 423 PC236_DRIVER_NAME);
9f7a344b 424 if (devpriv)
6a5c8664 425 pc236_intr_disable(dev);
9f7a344b 426
6a5c8664 427 if (dev->irq)
5f74ea14 428 free_irq(dev->irq, dev);
9f7a344b 429 if (dev->subdevices)
6a5c8664 430 subdev_8255_cleanup(dev, dev->subdevices + 0);
6a5c8664
IA
431 if (devpriv) {
432#ifdef CONFIG_COMEDI_PCI
433 if (devpriv->pci_dev) {
9f7a344b 434 if (dev->iobase)
6a5c8664 435 comedi_pci_disable(devpriv->pci_dev);
6a5c8664
IA
436 pci_dev_put(devpriv->pci_dev);
437 } else
438#endif
439 {
9f7a344b 440 if (dev->iobase)
6a5c8664 441 release_region(dev->iobase, PC236_IO_SIZE);
6a5c8664
IA
442 }
443 }
444 if (dev->board_name) {
445 printk(KERN_INFO "comedi%d: %s removed\n",
0a85b6f0 446 dev->minor, dev->board_name);
6a5c8664
IA
447 }
448 return 0;
449}
450
451/*
452 * This function checks and requests an I/O region, reporting an error
453 * if there is a conflict.
454 */
455static int pc236_request_region(unsigned minor, unsigned long from,
0a85b6f0 456 unsigned long extent)
6a5c8664
IA
457{
458 if (!from || !request_region(from, extent, PC236_DRIVER_NAME)) {
459 printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n",
0a85b6f0 460 minor, from, extent);
6a5c8664
IA
461 return -EIO;
462 }
463 return 0;
464}
465
466/*
467 * This function is called to mark the interrupt as disabled (no command
468 * configured on subdevice 1) and to physically disable the interrupt
469 * (not possible on the PC36AT, except by removing the IRQ jumper!).
470 */
da91b269 471static void pc236_intr_disable(struct comedi_device *dev)
6a5c8664
IA
472{
473 unsigned long flags;
474
5f74ea14 475 spin_lock_irqsave(&dev->spinlock, flags);
6a5c8664
IA
476 devpriv->enable_irq = 0;
477#ifdef CONFIG_COMEDI_PCI
478 if (devpriv->lcr_iobase)
479 outl(PCI236_INTR_DISABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
480#endif
5f74ea14 481 spin_unlock_irqrestore(&dev->spinlock, flags);
6a5c8664
IA
482}
483
484/*
485 * This function is called to mark the interrupt as enabled (a command
486 * configured on subdevice 1) and to physically enable the interrupt
487 * (not possible on the PC36AT, except by (re)connecting the IRQ jumper!).
488 */
da91b269 489static void pc236_intr_enable(struct comedi_device *dev)
6a5c8664
IA
490{
491 unsigned long flags;
492
5f74ea14 493 spin_lock_irqsave(&dev->spinlock, flags);
6a5c8664
IA
494 devpriv->enable_irq = 1;
495#ifdef CONFIG_COMEDI_PCI
496 if (devpriv->lcr_iobase)
497 outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
498#endif
5f74ea14 499 spin_unlock_irqrestore(&dev->spinlock, flags);
6a5c8664
IA
500}
501
502/*
503 * This function is called when an interrupt occurs to check whether
504 * the interrupt has been marked as enabled and was generated by the
505 * board. If so, the function prepares the hardware for the next
506 * interrupt.
507 * Returns 0 if the interrupt should be ignored.
508 */
da91b269 509static int pc236_intr_check(struct comedi_device *dev)
6a5c8664
IA
510{
511 int retval = 0;
512 unsigned long flags;
513
5f74ea14 514 spin_lock_irqsave(&dev->spinlock, flags);
6a5c8664
IA
515 if (devpriv->enable_irq) {
516 retval = 1;
517#ifdef CONFIG_COMEDI_PCI
518 if (devpriv->lcr_iobase) {
519 if ((inl(devpriv->lcr_iobase + PLX9052_INTCSR)
0a85b6f0
MT
520 & PLX9052_INTCSR_LI1STAT_MASK)
521 == PLX9052_INTCSR_LI1STAT_INACTIVE) {
6a5c8664
IA
522 retval = 0;
523 } else {
524 /* Clear interrupt and keep it enabled. */
525 outl(PCI236_INTR_ENABLE,
0a85b6f0 526 devpriv->lcr_iobase + PLX9052_INTCSR);
6a5c8664
IA
527 }
528 }
529#endif
530 }
5f74ea14 531 spin_unlock_irqrestore(&dev->spinlock, flags);
6a5c8664
IA
532
533 return retval;
534}
535
536/*
537 * Input from subdevice 1.
538 * Copied from the comedi_parport driver.
539 */
0a85b6f0
MT
540static int pc236_intr_insn(struct comedi_device *dev,
541 struct comedi_subdevice *s, struct comedi_insn *insn,
542 unsigned int *data)
6a5c8664
IA
543{
544 data[1] = 0;
545 return 2;
546}
547
548/*
549 * Subdevice 1 command test.
550 * Copied from the comedi_parport driver.
551 */
0a85b6f0
MT
552static int pc236_intr_cmdtest(struct comedi_device *dev,
553 struct comedi_subdevice *s,
554 struct comedi_cmd *cmd)
6a5c8664
IA
555{
556 int err = 0;
557 int tmp;
558
559 /* step 1 */
560
561 tmp = cmd->start_src;
562 cmd->start_src &= TRIG_NOW;
563 if (!cmd->start_src || tmp != cmd->start_src)
564 err++;
565
566 tmp = cmd->scan_begin_src;
567 cmd->scan_begin_src &= TRIG_EXT;
568 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
569 err++;
570
571 tmp = cmd->convert_src;
572 cmd->convert_src &= TRIG_FOLLOW;
573 if (!cmd->convert_src || tmp != cmd->convert_src)
574 err++;
575
576 tmp = cmd->scan_end_src;
577 cmd->scan_end_src &= TRIG_COUNT;
578 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
579 err++;
580
581 tmp = cmd->stop_src;
582 cmd->stop_src &= TRIG_NONE;
583 if (!cmd->stop_src || tmp != cmd->stop_src)
584 err++;
585
586 if (err)
587 return 1;
588
589 /* step 2: ignored */
590
591 if (err)
592 return 2;
593
594 /* step 3: */
595
596 if (cmd->start_arg != 0) {
597 cmd->start_arg = 0;
598 err++;
599 }
600 if (cmd->scan_begin_arg != 0) {
601 cmd->scan_begin_arg = 0;
602 err++;
603 }
604 if (cmd->convert_arg != 0) {
605 cmd->convert_arg = 0;
606 err++;
607 }
608 if (cmd->scan_end_arg != 1) {
609 cmd->scan_end_arg = 1;
610 err++;
611 }
612 if (cmd->stop_arg != 0) {
613 cmd->stop_arg = 0;
614 err++;
615 }
616
617 if (err)
618 return 3;
619
620 /* step 4: ignored */
621
622 if (err)
623 return 4;
624
625 return 0;
626}
627
628/*
629 * Subdevice 1 command.
630 */
da91b269 631static int pc236_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
6a5c8664
IA
632{
633 pc236_intr_enable(dev);
634
635 return 0;
636}
637
638/*
639 * Subdevice 1 cancel command.
640 */
0a85b6f0
MT
641static int pc236_intr_cancel(struct comedi_device *dev,
642 struct comedi_subdevice *s)
6a5c8664
IA
643{
644 pc236_intr_disable(dev);
645
646 return 0;
647}
648
649/*
650 * Interrupt service routine.
651 * Based on the comedi_parport driver.
652 */
70265d24 653static irqreturn_t pc236_interrupt(int irq, void *d)
6a5c8664 654{
71b5f4f1 655 struct comedi_device *dev = d;
34c43922 656 struct comedi_subdevice *s = dev->subdevices + 1;
6a5c8664
IA
657 int handled;
658
659 handled = pc236_intr_check(dev);
660 if (dev->attached && handled) {
661 comedi_buf_put(s->async, 0);
662 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
663 comedi_event(dev, s);
664 }
665 return IRQ_RETVAL(handled);
666}