Staging: comedi: Remove COMEDI_PCI_INITCLEANUP macro
[deliverable/linux.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
727b286b
AT
185static int __devinit driver_amplc_pc236_pci_probe(struct pci_dev *dev,
186 const struct pci_device_id
187 *ent)
188{
189 return comedi_pci_auto_config(dev, driver_amplc_pc236.driver_name);
190}
191
192static void __devexit driver_amplc_pc236_pci_remove(struct pci_dev *dev)
193{
194 comedi_pci_auto_unconfig(dev);
195}
196
197static struct pci_driver driver_amplc_pc236_pci_driver = {
198 .id_table = pc236_pci_table,
199 .probe = &driver_amplc_pc236_pci_probe,
200 .remove = __devexit_p(&driver_amplc_pc236_pci_remove)
201};
202
203static int __init driver_amplc_pc236_init_module(void)
204{
205 int retval;
206
207 retval = comedi_driver_register(&driver_amplc_pc236);
208 if (retval < 0)
209 return retval;
210
211 driver_amplc_pc236_pci_driver.name =
212 (char *)driver_amplc_pc236.driver_name;
213 return pci_register_driver(&driver_amplc_pc236_pci_driver);
214}
215
216static void __exit driver_amplc_pc236_cleanup_module(void)
217{
218 pci_unregister_driver(&driver_amplc_pc236_pci_driver);
219 comedi_driver_unregister(&driver_amplc_pc236);
220}
221
222module_init(driver_amplc_pc236_init_module);
223module_exit(driver_amplc_pc236_cleanup_module);
6a5c8664 224#else
7114a280
AT
225static int __init driver_amplc_pc236_init_module(void)
226{
227 return comedi_driver_register(&driver_amplc_pc236);
228}
229
230static void __exit driver_amplc_pc236_cleanup_module(void)
231{
232 comedi_driver_unregister(&driver_amplc_pc236);
233}
234
235module_init(driver_amplc_pc236_init_module);
236module_exit(driver_amplc_pc236_cleanup_module);
6a5c8664
IA
237#endif
238
239static int pc236_request_region(unsigned minor, unsigned long from,
0a85b6f0 240 unsigned long extent);
814900c9
BP
241static void pc236_intr_disable(struct comedi_device *dev);
242static void pc236_intr_enable(struct comedi_device *dev);
243static int pc236_intr_check(struct comedi_device *dev);
0a85b6f0
MT
244static int pc236_intr_insn(struct comedi_device *dev,
245 struct comedi_subdevice *s, struct comedi_insn *insn,
246 unsigned int *data);
247static int pc236_intr_cmdtest(struct comedi_device *dev,
248 struct comedi_subdevice *s,
249 struct comedi_cmd *cmd);
250static int pc236_intr_cmd(struct comedi_device *dev,
251 struct comedi_subdevice *s);
252static int pc236_intr_cancel(struct comedi_device *dev,
253 struct comedi_subdevice *s);
70265d24 254static irqreturn_t pc236_interrupt(int irq, void *d);
6a5c8664
IA
255
256/*
257 * This function looks for a PCI device matching the requested board name,
258 * bus and slot.
259 */
260#ifdef CONFIG_COMEDI_PCI
261static int
da91b269 262pc236_find_pci(struct comedi_device *dev, int bus, int slot,
0a85b6f0 263 struct pci_dev **pci_dev_p)
6a5c8664
IA
264{
265 struct pci_dev *pci_dev = NULL;
266
267 *pci_dev_p = NULL;
268
269 /* Look for matching PCI device. */
270 for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
0a85b6f0
MT
271 pci_dev != NULL;
272 pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
273 PCI_ANY_ID, pci_dev)) {
6a5c8664
IA
274 /* If bus/slot specified, check them. */
275 if (bus || slot) {
276 if (bus != pci_dev->bus->number
0a85b6f0 277 || slot != PCI_SLOT(pci_dev->devfn))
6a5c8664
IA
278 continue;
279 }
280 if (thisboard->model == anypci_model) {
281 /* Match any supported model. */
282 int i;
283
284 for (i = 0; i < ARRAY_SIZE(pc236_boards); i++) {
285 if (pc236_boards[i].bustype != pci_bustype)
286 continue;
287 if (pci_dev->device == pc236_boards[i].devid) {
288 /* Change board_ptr to matched board. */
289 dev->board_ptr = &pc236_boards[i];
290 break;
291 }
292 }
293 if (i == ARRAY_SIZE(pc236_boards))
294 continue;
295 } else {
296 /* Match specific model name. */
297 if (pci_dev->device != thisboard->devid)
298 continue;
299 }
300
301 /* Found a match. */
302 *pci_dev_p = pci_dev;
303 return 0;
304 }
305 /* No match found. */
306 if (bus || slot) {
307 printk(KERN_ERR
0a85b6f0
MT
308 "comedi%d: error! no %s found at pci %02x:%02x!\n",
309 dev->minor, thisboard->name, bus, slot);
6a5c8664
IA
310 } else {
311 printk(KERN_ERR "comedi%d: error! no %s found!\n",
0a85b6f0 312 dev->minor, thisboard->name);
6a5c8664
IA
313 }
314 return -EIO;
315}
316#endif
317
318/*
319 * Attach is called by the Comedi core to configure the driver
320 * for a particular board. If you specified a board_name array
321 * in the driver structure, dev->board_ptr contains that
322 * address.
323 */
da91b269 324static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it)
6a5c8664 325{
34c43922 326 struct comedi_subdevice *s;
6a5c8664
IA
327 unsigned long iobase = 0;
328 unsigned int irq = 0;
329#ifdef CONFIG_COMEDI_PCI
330 struct pci_dev *pci_dev = NULL;
331 int bus = 0, slot = 0;
332#endif
333 int share_irq = 0;
334 int ret;
335
336 printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor,
0a85b6f0 337 PC236_DRIVER_NAME);
6a5c8664
IA
338/*
339 * Allocate the private structure area. alloc_private() is a
340 * convenient macro defined in comedidev.h.
341 */
c3744138
BP
342 ret = alloc_private(dev, sizeof(struct pc236_private));
343 if (ret < 0) {
6a5c8664 344 printk(KERN_ERR "comedi%d: error! out of memory!\n",
0a85b6f0 345 dev->minor);
6a5c8664
IA
346 return ret;
347 }
348 /* Process options. */
349 switch (thisboard->bustype) {
350 case isa_bustype:
351 iobase = it->options[0];
352 irq = it->options[1];
353 share_irq = 0;
354 break;
355#ifdef CONFIG_COMEDI_PCI
356 case pci_bustype:
357 bus = it->options[0];
358 slot = it->options[1];
359 share_irq = 1;
360
c3744138
BP
361 ret = pc236_find_pci(dev, bus, slot, &pci_dev);
362 if (ret < 0)
6a5c8664
IA
363 return ret;
364 devpriv->pci_dev = pci_dev;
365 break;
366#endif /* CONFIG_COMEDI_PCI */
367 default:
368 printk(KERN_ERR
0a85b6f0
MT
369 "comedi%d: %s: BUG! cannot determine board type!\n",
370 dev->minor, PC236_DRIVER_NAME);
6a5c8664
IA
371 return -EINVAL;
372 break;
373 }
374
375/*
376 * Initialize dev->board_name.
377 */
378 dev->board_name = thisboard->name;
379
380 /* Enable device and reserve I/O spaces. */
381#ifdef CONFIG_COMEDI_PCI
382 if (pci_dev) {
c3744138
BP
383
384 ret = comedi_pci_enable(pci_dev, PC236_DRIVER_NAME);
385 if (ret < 0) {
6a5c8664 386 printk(KERN_ERR
0a85b6f0
MT
387 "comedi%d: error! cannot enable PCI device and request regions!\n",
388 dev->minor);
6a5c8664
IA
389 return ret;
390 }
391 devpriv->lcr_iobase = pci_resource_start(pci_dev, 1);
392 iobase = pci_resource_start(pci_dev, 2);
393 irq = pci_dev->irq;
394 } else
395#endif
396 {
397 ret = pc236_request_region(dev->minor, iobase, PC236_IO_SIZE);
9f7a344b 398 if (ret < 0)
6a5c8664 399 return ret;
6a5c8664
IA
400 }
401 dev->iobase = iobase;
402
403/*
404 * Allocate the subdevice structures. alloc_subdevice() is a
405 * convenient macro defined in comedidev.h.
406 */
c3744138
BP
407 ret = alloc_subdevices(dev, 2);
408 if (ret < 0) {
6a5c8664 409 printk(KERN_ERR "comedi%d: error! out of memory!\n",
0a85b6f0 410 dev->minor);
6a5c8664
IA
411 return ret;
412 }
413
414 s = dev->subdevices + 0;
415 /* digital i/o subdevice (8255) */
c3744138
BP
416 ret = subdev_8255_init(dev, s, NULL, iobase);
417 if (ret < 0) {
6a5c8664 418 printk(KERN_ERR "comedi%d: error! out of memory!\n",
0a85b6f0 419 dev->minor);
6a5c8664
IA
420 return ret;
421 }
422 s = dev->subdevices + 1;
423 dev->read_subdev = s;
424 s->type = COMEDI_SUBD_UNUSED;
425 pc236_intr_disable(dev);
426 if (irq) {
427 unsigned long flags = share_irq ? IRQF_SHARED : 0;
428
5f74ea14 429 if (request_irq(irq, pc236_interrupt, flags,
6a5c8664
IA
430 PC236_DRIVER_NAME, dev) >= 0) {
431 dev->irq = irq;
432 s->type = COMEDI_SUBD_DI;
433 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
434 s->n_chan = 1;
435 s->maxdata = 1;
436 s->range_table = &range_digital;
437 s->insn_bits = pc236_intr_insn;
438 s->do_cmdtest = pc236_intr_cmdtest;
439 s->do_cmd = pc236_intr_cmd;
440 s->cancel = pc236_intr_cancel;
441 }
442 }
443 printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name);
444 if (thisboard->bustype == isa_bustype) {
445 printk("(base %#lx) ", iobase);
446 } else {
447#ifdef CONFIG_COMEDI_PCI
448 printk("(pci %s) ", pci_name(pci_dev));
449#endif
450 }
9f7a344b 451 if (irq)
6a5c8664 452 printk("(irq %u%s) ", irq, (dev->irq ? "" : " UNAVAILABLE"));
9f7a344b 453 else
6a5c8664 454 printk("(no irq) ");
6a5c8664
IA
455
456 printk("attached\n");
457
458 return 1;
459}
460
461/*
462 * _detach is called to deconfigure a device. It should deallocate
463 * resources.
464 * This function is also called when _attach() fails, so it should be
465 * careful not to release resources that were not necessarily
466 * allocated by _attach(). dev->private and dev->subdevices are
467 * deallocated automatically by the core.
468 */
da91b269 469static int pc236_detach(struct comedi_device *dev)
6a5c8664
IA
470{
471 printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor,
0a85b6f0 472 PC236_DRIVER_NAME);
9f7a344b 473 if (devpriv)
6a5c8664 474 pc236_intr_disable(dev);
9f7a344b 475
6a5c8664 476 if (dev->irq)
5f74ea14 477 free_irq(dev->irq, dev);
9f7a344b 478 if (dev->subdevices)
6a5c8664 479 subdev_8255_cleanup(dev, dev->subdevices + 0);
6a5c8664
IA
480 if (devpriv) {
481#ifdef CONFIG_COMEDI_PCI
482 if (devpriv->pci_dev) {
9f7a344b 483 if (dev->iobase)
6a5c8664 484 comedi_pci_disable(devpriv->pci_dev);
6a5c8664
IA
485 pci_dev_put(devpriv->pci_dev);
486 } else
487#endif
488 {
9f7a344b 489 if (dev->iobase)
6a5c8664 490 release_region(dev->iobase, PC236_IO_SIZE);
6a5c8664
IA
491 }
492 }
493 if (dev->board_name) {
494 printk(KERN_INFO "comedi%d: %s removed\n",
0a85b6f0 495 dev->minor, dev->board_name);
6a5c8664
IA
496 }
497 return 0;
498}
499
500/*
501 * This function checks and requests an I/O region, reporting an error
502 * if there is a conflict.
503 */
504static int pc236_request_region(unsigned minor, unsigned long from,
0a85b6f0 505 unsigned long extent)
6a5c8664
IA
506{
507 if (!from || !request_region(from, extent, PC236_DRIVER_NAME)) {
508 printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n",
0a85b6f0 509 minor, from, extent);
6a5c8664
IA
510 return -EIO;
511 }
512 return 0;
513}
514
515/*
516 * This function is called to mark the interrupt as disabled (no command
517 * configured on subdevice 1) and to physically disable the interrupt
518 * (not possible on the PC36AT, except by removing the IRQ jumper!).
519 */
da91b269 520static void pc236_intr_disable(struct comedi_device *dev)
6a5c8664
IA
521{
522 unsigned long flags;
523
5f74ea14 524 spin_lock_irqsave(&dev->spinlock, flags);
6a5c8664
IA
525 devpriv->enable_irq = 0;
526#ifdef CONFIG_COMEDI_PCI
527 if (devpriv->lcr_iobase)
528 outl(PCI236_INTR_DISABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
529#endif
5f74ea14 530 spin_unlock_irqrestore(&dev->spinlock, flags);
6a5c8664
IA
531}
532
533/*
534 * This function is called to mark the interrupt as enabled (a command
535 * configured on subdevice 1) and to physically enable the interrupt
536 * (not possible on the PC36AT, except by (re)connecting the IRQ jumper!).
537 */
da91b269 538static void pc236_intr_enable(struct comedi_device *dev)
6a5c8664
IA
539{
540 unsigned long flags;
541
5f74ea14 542 spin_lock_irqsave(&dev->spinlock, flags);
6a5c8664
IA
543 devpriv->enable_irq = 1;
544#ifdef CONFIG_COMEDI_PCI
545 if (devpriv->lcr_iobase)
546 outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
547#endif
5f74ea14 548 spin_unlock_irqrestore(&dev->spinlock, flags);
6a5c8664
IA
549}
550
551/*
552 * This function is called when an interrupt occurs to check whether
553 * the interrupt has been marked as enabled and was generated by the
554 * board. If so, the function prepares the hardware for the next
555 * interrupt.
556 * Returns 0 if the interrupt should be ignored.
557 */
da91b269 558static int pc236_intr_check(struct comedi_device *dev)
6a5c8664
IA
559{
560 int retval = 0;
561 unsigned long flags;
562
5f74ea14 563 spin_lock_irqsave(&dev->spinlock, flags);
6a5c8664
IA
564 if (devpriv->enable_irq) {
565 retval = 1;
566#ifdef CONFIG_COMEDI_PCI
567 if (devpriv->lcr_iobase) {
568 if ((inl(devpriv->lcr_iobase + PLX9052_INTCSR)
0a85b6f0
MT
569 & PLX9052_INTCSR_LI1STAT_MASK)
570 == PLX9052_INTCSR_LI1STAT_INACTIVE) {
6a5c8664
IA
571 retval = 0;
572 } else {
573 /* Clear interrupt and keep it enabled. */
574 outl(PCI236_INTR_ENABLE,
0a85b6f0 575 devpriv->lcr_iobase + PLX9052_INTCSR);
6a5c8664
IA
576 }
577 }
578#endif
579 }
5f74ea14 580 spin_unlock_irqrestore(&dev->spinlock, flags);
6a5c8664
IA
581
582 return retval;
583}
584
585/*
586 * Input from subdevice 1.
587 * Copied from the comedi_parport driver.
588 */
0a85b6f0
MT
589static int pc236_intr_insn(struct comedi_device *dev,
590 struct comedi_subdevice *s, struct comedi_insn *insn,
591 unsigned int *data)
6a5c8664
IA
592{
593 data[1] = 0;
594 return 2;
595}
596
597/*
598 * Subdevice 1 command test.
599 * Copied from the comedi_parport driver.
600 */
0a85b6f0
MT
601static int pc236_intr_cmdtest(struct comedi_device *dev,
602 struct comedi_subdevice *s,
603 struct comedi_cmd *cmd)
6a5c8664
IA
604{
605 int err = 0;
606 int tmp;
607
608 /* step 1 */
609
610 tmp = cmd->start_src;
611 cmd->start_src &= TRIG_NOW;
612 if (!cmd->start_src || tmp != cmd->start_src)
613 err++;
614
615 tmp = cmd->scan_begin_src;
616 cmd->scan_begin_src &= TRIG_EXT;
617 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
618 err++;
619
620 tmp = cmd->convert_src;
621 cmd->convert_src &= TRIG_FOLLOW;
622 if (!cmd->convert_src || tmp != cmd->convert_src)
623 err++;
624
625 tmp = cmd->scan_end_src;
626 cmd->scan_end_src &= TRIG_COUNT;
627 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
628 err++;
629
630 tmp = cmd->stop_src;
631 cmd->stop_src &= TRIG_NONE;
632 if (!cmd->stop_src || tmp != cmd->stop_src)
633 err++;
634
635 if (err)
636 return 1;
637
638 /* step 2: ignored */
639
640 if (err)
641 return 2;
642
643 /* step 3: */
644
645 if (cmd->start_arg != 0) {
646 cmd->start_arg = 0;
647 err++;
648 }
649 if (cmd->scan_begin_arg != 0) {
650 cmd->scan_begin_arg = 0;
651 err++;
652 }
653 if (cmd->convert_arg != 0) {
654 cmd->convert_arg = 0;
655 err++;
656 }
657 if (cmd->scan_end_arg != 1) {
658 cmd->scan_end_arg = 1;
659 err++;
660 }
661 if (cmd->stop_arg != 0) {
662 cmd->stop_arg = 0;
663 err++;
664 }
665
666 if (err)
667 return 3;
668
669 /* step 4: ignored */
670
671 if (err)
672 return 4;
673
674 return 0;
675}
676
677/*
678 * Subdevice 1 command.
679 */
da91b269 680static int pc236_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
6a5c8664
IA
681{
682 pc236_intr_enable(dev);
683
684 return 0;
685}
686
687/*
688 * Subdevice 1 cancel command.
689 */
0a85b6f0
MT
690static int pc236_intr_cancel(struct comedi_device *dev,
691 struct comedi_subdevice *s)
6a5c8664
IA
692{
693 pc236_intr_disable(dev);
694
695 return 0;
696}
697
698/*
699 * Interrupt service routine.
700 * Based on the comedi_parport driver.
701 */
70265d24 702static irqreturn_t pc236_interrupt(int irq, void *d)
6a5c8664 703{
71b5f4f1 704 struct comedi_device *dev = d;
34c43922 705 struct comedi_subdevice *s = dev->subdevices + 1;
6a5c8664
IA
706 int handled;
707
708 handled = pc236_intr_check(dev);
709 if (dev->attached && handled) {
710 comedi_buf_put(s->async, 0);
711 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
712 comedi_event(dev, s);
713 }
714 return IRQ_RETVAL(handled);
715}
90f703d3
AT
716
717MODULE_AUTHOR("Comedi http://www.comedi.org");
718MODULE_DESCRIPTION("Comedi low-level driver");
719MODULE_LICENSE("GPL");
This page took 0.212342 seconds and 5 git commands to generate.