staging: comedi: amplc_pc236: Remove forward function declarations
[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
6a5c8664
IA
59#include "8255.h"
60#include "plx9052.h"
61
62#define PC236_DRIVER_NAME "amplc_pc236"
63
64/* PCI236 PCI configuration register information */
65#define PCI_VENDOR_ID_AMPLICON 0x14dc
66#define PCI_DEVICE_ID_AMPLICON_PCI236 0x0009
67#define PCI_DEVICE_ID_INVALID 0xffff
68
69/* PC36AT / PCI236 registers */
70
71#define PC236_IO_SIZE 4
72#define PC236_LCR_IO_SIZE 128
73
74/*
75 * INTCSR values for PCI236.
76 */
77/* Disable interrupt, also clear any interrupt there */
53106ae6 78#define PCI236_INTR_DISABLE (PLX9052_INTCSR_LI1ENAB_DISABLED \
9f7a344b
BA
79 | PLX9052_INTCSR_LI1POL_HIGH \
80 | PLX9052_INTCSR_LI2POL_HIGH \
81 | PLX9052_INTCSR_PCIENAB_DISABLED \
82 | PLX9052_INTCSR_LI1SEL_EDGE \
83 | PLX9052_INTCSR_LI1CLRINT_ASSERTED)
6a5c8664 84/* Enable interrupt, also clear any interrupt there. */
53106ae6 85#define PCI236_INTR_ENABLE (PLX9052_INTCSR_LI1ENAB_ENABLED \
9f7a344b
BA
86 | PLX9052_INTCSR_LI1POL_HIGH \
87 | PLX9052_INTCSR_LI2POL_HIGH \
88 | PLX9052_INTCSR_PCIENAB_ENABLED \
89 | PLX9052_INTCSR_LI1SEL_EDGE \
90 | PLX9052_INTCSR_LI1CLRINT_ASSERTED)
6a5c8664
IA
91
92/*
93 * Board descriptions for Amplicon PC36AT and PCI236.
94 */
95
96enum pc236_bustype { isa_bustype, pci_bustype };
97enum pc236_model { pc36at_model, pci236_model, anypci_model };
98
57ad8696 99struct pc236_board {
6a5c8664
IA
100 const char *name;
101 const char *fancy_name;
102 unsigned short devid;
103 enum pc236_bustype bustype;
104 enum pc236_model model;
57ad8696
BP
105};
106static const struct pc236_board pc236_boards[] = {
1bb6dfc4 107#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA)
6a5c8664 108 {
0a85b6f0
MT
109 .name = "pc36at",
110 .fancy_name = "PC36AT",
111 .bustype = isa_bustype,
112 .model = pc36at_model,
113 },
717ab674 114#endif
1bb6dfc4 115#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
6a5c8664 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 123 {
0a85b6f0
MT
124 .name = PC236_DRIVER_NAME,
125 .fancy_name = PC236_DRIVER_NAME,
126 .devid = PCI_DEVICE_ID_INVALID,
127 .bustype = pci_bustype,
128 .model = anypci_model, /* wildcard */
129 },
6a5c8664
IA
130#endif
131};
132
6a5c8664
IA
133/*
134 * Useful for shorthand access to the particular board structure
135 */
57ad8696 136#define thisboard ((const struct pc236_board *)dev->board_ptr)
6a5c8664
IA
137
138/* this structure is for data unique to this hardware driver. If
139 several hardware drivers keep similar information in this structure,
9f7a344b
BA
140 feel free to suggest moving the variable to the struct comedi_device struct.
141 */
f1ee810a 142struct pc236_private {
1bb6dfc4 143#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
6a5c8664
IA
144 /* PCI device */
145 struct pci_dev *pci_dev;
9f7a344b 146 unsigned long lcr_iobase; /* PLX PCI9052 config registers in PCIBAR1 */
6a5c8664
IA
147#endif
148 int enable_irq;
f1ee810a 149};
6a5c8664 150
f1ee810a 151#define devpriv ((struct pc236_private *)dev->private)
6a5c8664 152
6a5c8664
IA
153/*
154 * This function looks for a PCI device matching the requested board name,
155 * bus and slot.
156 */
1bb6dfc4 157#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
6a5c8664 158static int
da91b269 159pc236_find_pci(struct comedi_device *dev, int bus, int slot,
0a85b6f0 160 struct pci_dev **pci_dev_p)
6a5c8664
IA
161{
162 struct pci_dev *pci_dev = NULL;
163
164 *pci_dev_p = NULL;
165
166 /* Look for matching PCI device. */
167 for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
0a85b6f0
MT
168 pci_dev != NULL;
169 pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
170 PCI_ANY_ID, pci_dev)) {
6a5c8664
IA
171 /* If bus/slot specified, check them. */
172 if (bus || slot) {
173 if (bus != pci_dev->bus->number
0a85b6f0 174 || slot != PCI_SLOT(pci_dev->devfn))
6a5c8664
IA
175 continue;
176 }
177 if (thisboard->model == anypci_model) {
178 /* Match any supported model. */
179 int i;
180
181 for (i = 0; i < ARRAY_SIZE(pc236_boards); i++) {
182 if (pc236_boards[i].bustype != pci_bustype)
183 continue;
184 if (pci_dev->device == pc236_boards[i].devid) {
185 /* Change board_ptr to matched board. */
186 dev->board_ptr = &pc236_boards[i];
187 break;
188 }
189 }
190 if (i == ARRAY_SIZE(pc236_boards))
191 continue;
192 } else {
193 /* Match specific model name. */
194 if (pci_dev->device != thisboard->devid)
195 continue;
196 }
197
198 /* Found a match. */
199 *pci_dev_p = pci_dev;
200 return 0;
201 }
202 /* No match found. */
203 if (bus || slot) {
204 printk(KERN_ERR
0a85b6f0
MT
205 "comedi%d: error! no %s found at pci %02x:%02x!\n",
206 dev->minor, thisboard->name, bus, slot);
6a5c8664
IA
207 } else {
208 printk(KERN_ERR "comedi%d: error! no %s found!\n",
0a85b6f0 209 dev->minor, thisboard->name);
6a5c8664
IA
210 }
211 return -EIO;
212}
213#endif
214
6a5c8664
IA
215/*
216 * This function checks and requests an I/O region, reporting an error
217 * if there is a conflict.
218 */
1bb6dfc4 219#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA)
6a5c8664 220static int pc236_request_region(unsigned minor, unsigned long from,
0a85b6f0 221 unsigned long extent)
6a5c8664
IA
222{
223 if (!from || !request_region(from, extent, PC236_DRIVER_NAME)) {
224 printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n",
0a85b6f0 225 minor, from, extent);
6a5c8664
IA
226 return -EIO;
227 }
228 return 0;
229}
717ab674 230#endif
6a5c8664
IA
231
232/*
233 * This function is called to mark the interrupt as disabled (no command
234 * configured on subdevice 1) and to physically disable the interrupt
235 * (not possible on the PC36AT, except by removing the IRQ jumper!).
236 */
da91b269 237static void pc236_intr_disable(struct comedi_device *dev)
6a5c8664
IA
238{
239 unsigned long flags;
240
5f74ea14 241 spin_lock_irqsave(&dev->spinlock, flags);
6a5c8664 242 devpriv->enable_irq = 0;
1bb6dfc4 243#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
6a5c8664
IA
244 if (devpriv->lcr_iobase)
245 outl(PCI236_INTR_DISABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
246#endif
5f74ea14 247 spin_unlock_irqrestore(&dev->spinlock, flags);
6a5c8664
IA
248}
249
250/*
251 * This function is called to mark the interrupt as enabled (a command
252 * configured on subdevice 1) and to physically enable the interrupt
253 * (not possible on the PC36AT, except by (re)connecting the IRQ jumper!).
254 */
da91b269 255static void pc236_intr_enable(struct comedi_device *dev)
6a5c8664
IA
256{
257 unsigned long flags;
258
5f74ea14 259 spin_lock_irqsave(&dev->spinlock, flags);
6a5c8664 260 devpriv->enable_irq = 1;
1bb6dfc4 261#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
6a5c8664
IA
262 if (devpriv->lcr_iobase)
263 outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
264#endif
5f74ea14 265 spin_unlock_irqrestore(&dev->spinlock, flags);
6a5c8664
IA
266}
267
268/*
269 * This function is called when an interrupt occurs to check whether
270 * the interrupt has been marked as enabled and was generated by the
271 * board. If so, the function prepares the hardware for the next
272 * interrupt.
273 * Returns 0 if the interrupt should be ignored.
274 */
da91b269 275static int pc236_intr_check(struct comedi_device *dev)
6a5c8664
IA
276{
277 int retval = 0;
278 unsigned long flags;
279
5f74ea14 280 spin_lock_irqsave(&dev->spinlock, flags);
6a5c8664
IA
281 if (devpriv->enable_irq) {
282 retval = 1;
1bb6dfc4 283#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
6a5c8664
IA
284 if (devpriv->lcr_iobase) {
285 if ((inl(devpriv->lcr_iobase + PLX9052_INTCSR)
0a85b6f0
MT
286 & PLX9052_INTCSR_LI1STAT_MASK)
287 == PLX9052_INTCSR_LI1STAT_INACTIVE) {
6a5c8664
IA
288 retval = 0;
289 } else {
290 /* Clear interrupt and keep it enabled. */
291 outl(PCI236_INTR_ENABLE,
0a85b6f0 292 devpriv->lcr_iobase + PLX9052_INTCSR);
6a5c8664
IA
293 }
294 }
295#endif
296 }
5f74ea14 297 spin_unlock_irqrestore(&dev->spinlock, flags);
6a5c8664
IA
298
299 return retval;
300}
301
302/*
303 * Input from subdevice 1.
304 * Copied from the comedi_parport driver.
305 */
0a85b6f0
MT
306static int pc236_intr_insn(struct comedi_device *dev,
307 struct comedi_subdevice *s, struct comedi_insn *insn,
308 unsigned int *data)
6a5c8664
IA
309{
310 data[1] = 0;
311 return 2;
312}
313
314/*
315 * Subdevice 1 command test.
316 * Copied from the comedi_parport driver.
317 */
0a85b6f0
MT
318static int pc236_intr_cmdtest(struct comedi_device *dev,
319 struct comedi_subdevice *s,
320 struct comedi_cmd *cmd)
6a5c8664
IA
321{
322 int err = 0;
323 int tmp;
324
325 /* step 1 */
326
327 tmp = cmd->start_src;
328 cmd->start_src &= TRIG_NOW;
329 if (!cmd->start_src || tmp != cmd->start_src)
330 err++;
331
332 tmp = cmd->scan_begin_src;
333 cmd->scan_begin_src &= TRIG_EXT;
334 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
335 err++;
336
337 tmp = cmd->convert_src;
338 cmd->convert_src &= TRIG_FOLLOW;
339 if (!cmd->convert_src || tmp != cmd->convert_src)
340 err++;
341
342 tmp = cmd->scan_end_src;
343 cmd->scan_end_src &= TRIG_COUNT;
344 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
345 err++;
346
347 tmp = cmd->stop_src;
348 cmd->stop_src &= TRIG_NONE;
349 if (!cmd->stop_src || tmp != cmd->stop_src)
350 err++;
351
352 if (err)
353 return 1;
354
355 /* step 2: ignored */
356
357 if (err)
358 return 2;
359
360 /* step 3: */
361
362 if (cmd->start_arg != 0) {
363 cmd->start_arg = 0;
364 err++;
365 }
366 if (cmd->scan_begin_arg != 0) {
367 cmd->scan_begin_arg = 0;
368 err++;
369 }
370 if (cmd->convert_arg != 0) {
371 cmd->convert_arg = 0;
372 err++;
373 }
374 if (cmd->scan_end_arg != 1) {
375 cmd->scan_end_arg = 1;
376 err++;
377 }
378 if (cmd->stop_arg != 0) {
379 cmd->stop_arg = 0;
380 err++;
381 }
382
383 if (err)
384 return 3;
385
386 /* step 4: ignored */
387
388 if (err)
389 return 4;
390
391 return 0;
392}
393
394/*
395 * Subdevice 1 command.
396 */
da91b269 397static int pc236_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
6a5c8664
IA
398{
399 pc236_intr_enable(dev);
400
401 return 0;
402}
403
404/*
405 * Subdevice 1 cancel command.
406 */
0a85b6f0
MT
407static int pc236_intr_cancel(struct comedi_device *dev,
408 struct comedi_subdevice *s)
6a5c8664
IA
409{
410 pc236_intr_disable(dev);
411
412 return 0;
413}
414
415/*
416 * Interrupt service routine.
417 * Based on the comedi_parport driver.
418 */
70265d24 419static irqreturn_t pc236_interrupt(int irq, void *d)
6a5c8664 420{
71b5f4f1 421 struct comedi_device *dev = d;
34c43922 422 struct comedi_subdevice *s = dev->subdevices + 1;
6a5c8664
IA
423 int handled;
424
425 handled = pc236_intr_check(dev);
426 if (dev->attached && handled) {
427 comedi_buf_put(s->async, 0);
428 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
429 comedi_event(dev, s);
430 }
431 return IRQ_RETVAL(handled);
432}
90f703d3 433
7bd06f69
IA
434/*
435 * Attach is called by the Comedi core to configure the driver
436 * for a particular board. If you specified a board_name array
437 * in the driver structure, dev->board_ptr contains that
438 * address.
439 */
440static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it)
441{
442 struct comedi_subdevice *s;
443 unsigned long iobase = 0;
444 unsigned int irq = 0;
445#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
446 struct pci_dev *pci_dev = NULL;
447 int bus = 0, slot = 0;
448#endif
449 int share_irq = 0;
450 int ret;
451
452 printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor,
453 PC236_DRIVER_NAME);
454/*
455 * Allocate the private structure area. alloc_private() is a
456 * convenient macro defined in comedidev.h.
457 */
458 ret = alloc_private(dev, sizeof(struct pc236_private));
459 if (ret < 0) {
460 printk(KERN_ERR "comedi%d: error! out of memory!\n",
461 dev->minor);
462 return ret;
463 }
464 /* Process options. */
465 switch (thisboard->bustype) {
466#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA)
467 case isa_bustype:
468 iobase = it->options[0];
469 irq = it->options[1];
470 share_irq = 0;
471 break;
472#endif
473#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
474 case pci_bustype:
475 bus = it->options[0];
476 slot = it->options[1];
477 share_irq = 1;
478
479 ret = pc236_find_pci(dev, bus, slot, &pci_dev);
480 if (ret < 0)
481 return ret;
482 devpriv->pci_dev = pci_dev;
483 break;
484#endif
485 default:
486 printk(KERN_ERR
487 "comedi%d: %s: BUG! cannot determine board type!\n",
488 dev->minor, PC236_DRIVER_NAME);
489 return -EINVAL;
490 break;
491 }
492
493/*
494 * Initialize dev->board_name.
495 */
496 dev->board_name = thisboard->name;
497
498 /* Enable device and reserve I/O spaces. */
499#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
500 if (pci_dev) {
501
502 ret = comedi_pci_enable(pci_dev, PC236_DRIVER_NAME);
503 if (ret < 0) {
504 printk(KERN_ERR
505 "comedi%d: error! cannot enable PCI device and request regions!\n",
506 dev->minor);
507 return ret;
508 }
509 devpriv->lcr_iobase = pci_resource_start(pci_dev, 1);
510 iobase = pci_resource_start(pci_dev, 2);
511 irq = pci_dev->irq;
512 } else
513#endif
514 {
515#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA)
516 ret = pc236_request_region(dev->minor, iobase, PC236_IO_SIZE);
517 if (ret < 0)
518 return ret;
519#endif
520 }
521 dev->iobase = iobase;
522
523/*
524 * Allocate the subdevice structures. alloc_subdevice() is a
525 * convenient macro defined in comedidev.h.
526 */
527 ret = alloc_subdevices(dev, 2);
528 if (ret < 0) {
529 printk(KERN_ERR "comedi%d: error! out of memory!\n",
530 dev->minor);
531 return ret;
532 }
533
534 s = dev->subdevices + 0;
535 /* digital i/o subdevice (8255) */
536 ret = subdev_8255_init(dev, s, NULL, iobase);
537 if (ret < 0) {
538 printk(KERN_ERR "comedi%d: error! out of memory!\n",
539 dev->minor);
540 return ret;
541 }
542 s = dev->subdevices + 1;
543 dev->read_subdev = s;
544 s->type = COMEDI_SUBD_UNUSED;
545 pc236_intr_disable(dev);
546 if (irq) {
547 unsigned long flags = share_irq ? IRQF_SHARED : 0;
548
549 if (request_irq(irq, pc236_interrupt, flags,
550 PC236_DRIVER_NAME, dev) >= 0) {
551 dev->irq = irq;
552 s->type = COMEDI_SUBD_DI;
553 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
554 s->n_chan = 1;
555 s->maxdata = 1;
556 s->range_table = &range_digital;
557 s->insn_bits = pc236_intr_insn;
558 s->do_cmdtest = pc236_intr_cmdtest;
559 s->do_cmd = pc236_intr_cmd;
560 s->cancel = pc236_intr_cancel;
561 }
562 }
563 printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name);
564 switch (thisboard->bustype) {
565#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA)
566 case isa_bustype:
567 printk("(base %#lx) ", iobase);
568 break;
569#endif
570#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
571 case pci_bustype:
572 printk("(pci %s) ", pci_name(pci_dev));
573 break;
574#endif
575 default:
576 break;
577 }
578 if (irq)
579 printk("(irq %u%s) ", irq, (dev->irq ? "" : " UNAVAILABLE"));
580 else
581 printk("(no irq) ");
582
583 printk("attached\n");
584
585 return 1;
586}
587
588static void pc236_detach(struct comedi_device *dev)
589{
590 if (devpriv)
591 pc236_intr_disable(dev);
592 if (dev->irq)
593 free_irq(dev->irq, dev);
594 if (dev->subdevices)
595 subdev_8255_cleanup(dev, dev->subdevices + 0);
596 if (devpriv) {
597#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
598 if (devpriv->pci_dev) {
599 if (dev->iobase)
600 comedi_pci_disable(devpriv->pci_dev);
601 pci_dev_put(devpriv->pci_dev);
602 } else
603#endif
604 {
605#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA)
606 if (dev->iobase)
607 release_region(dev->iobase, PC236_IO_SIZE);
608#endif
609 }
610 }
611}
612
613/*
614 * The struct comedi_driver structure tells the Comedi core module
615 * which functions to call to configure/deconfigure (attach/detach)
616 * the board, and also about the kernel module that contains
617 * the device code.
618 */
619static struct comedi_driver amplc_pc236_driver = {
620 .driver_name = PC236_DRIVER_NAME,
621 .module = THIS_MODULE,
622 .attach = pc236_attach,
623 .detach = pc236_detach,
624 .board_name = &pc236_boards[0].name,
625 .offset = sizeof(struct pc236_board),
626 .num_names = ARRAY_SIZE(pc236_boards),
627};
628
629#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
630static DEFINE_PCI_DEVICE_TABLE(pc236_pci_table) = {
631 { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI236) },
632 {0}
633};
634
635MODULE_DEVICE_TABLE(pci, pc236_pci_table);
636
637static int __devinit amplc_pc236_pci_probe(struct pci_dev *dev,
638 const struct pci_device_id *ent)
639{
640 return comedi_pci_auto_config(dev, &amplc_pc236_driver);
641}
642
643static void __devexit amplc_pc236_pci_remove(struct pci_dev *dev)
644{
645 comedi_pci_auto_unconfig(dev);
646}
647
648static struct pci_driver amplc_pc236_pci_driver = {
649 .name = PC236_DRIVER_NAME,
650 .id_table = pc236_pci_table,
651 .probe = &amplc_pc236_pci_probe,
652 .remove = __devexit_p(&amplc_pc236_pci_remove)
653};
654
655module_comedi_pci_driver(amplc_pc236_driver, amplc_pc236_pci_driver);
656#else
657module_comedi_driver(amplc_pc236_driver);
658#endif
659
90f703d3
AT
660MODULE_AUTHOR("Comedi http://www.comedi.org");
661MODULE_DESCRIPTION("Comedi low-level driver");
662MODULE_LICENSE("GPL");
This page took 0.471219 seconds and 5 git commands to generate.