Version:  2.0.40 2.2.26 2.4.37 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15 3.16 3.17 3.18 3.19 4.0

Linux/drivers/staging/comedi/drivers/addi_apci_1564.c

  1 /*
  2  * addi_apci_1564.c
  3  * Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code of this module.
  4  *
  5  *      ADDI-DATA GmbH
  6  *      Dieselstrasse 3
  7  *      D-77833 Ottersweier
  8  *      Tel: +19(0)7223/9493-0
  9  *      Fax: +49(0)7223/9493-92
 10  *      http://www.addi-data.com
 11  *      info@addi-data.com
 12  *
 13  * This program is free software; you can redistribute it and/or modify it under
 14  * the terms of the GNU General Public License as published by the Free Software
 15  * Foundation; either version 2 of the License, or (at your option) any later
 16  * version.
 17  *
 18  * This program is distributed in the hope that it will be useful, but WITHOUT
 19  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 20  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 21  * details.
 22  */
 23 
 24 #include <linux/module.h>
 25 #include <linux/pci.h>
 26 #include <linux/interrupt.h>
 27 #include <linux/sched.h>
 28 
 29 #include "../comedidev.h"
 30 #include "comedi_fc.h"
 31 #include "addi_tcw.h"
 32 #include "addi_watchdog.h"
 33 
 34 /*
 35  * PCI BAR 0
 36  *
 37  * PLD Revision 1.0 I/O Mapping
 38  *   0x00         93C76 EEPROM
 39  *   0x04 - 0x18  Timer 12-Bit
 40  *
 41  * PLD Revision 2.x I/O Mapping
 42  *   0x00         93C76 EEPROM
 43  *   0x04 - 0x14  Digital Input
 44  *   0x18 - 0x25  Digital Output
 45  *   0x28 - 0x44  Watchdog 8-Bit
 46  *   0x48 - 0x64  Timer 12-Bit
 47  */
 48 #define APCI1564_EEPROM_REG                     0x00
 49 #define APCI1564_EEPROM_VCC_STATUS              (1 << 8)
 50 #define APCI1564_EEPROM_TO_REV(x)               (((x) >> 4) & 0xf)
 51 #define APCI1564_EEPROM_DI                      (1 << 3)
 52 #define APCI1564_EEPROM_DO                      (1 << 2)
 53 #define APCI1564_EEPROM_CS                      (1 << 1)
 54 #define APCI1564_EEPROM_CLK                     (1 << 0)
 55 #define APCI1564_REV1_TIMER_IOBASE              0x04
 56 #define APCI1564_REV2_MAIN_IOBASE               0x04
 57 #define APCI1564_REV2_TIMER_IOBASE              0x48
 58 
 59 /*
 60  * PCI BAR 1
 61  *
 62  * PLD Revision 1.0 I/O Mapping
 63  *   0x00 - 0x10  Digital Input
 64  *   0x14 - 0x20  Digital Output
 65  *   0x24 - 0x3c  Watchdog 8-Bit
 66  *
 67  * PLD Revision 2.x I/O Mapping
 68  *   0x00         Counter_0
 69  *   0x20         Counter_1
 70  *   0x30         Counter_3
 71  */
 72 #define APCI1564_REV1_MAIN_IOBASE               0x00
 73 
 74 /*
 75  * dev->iobase Register Map
 76  *   PLD Revision 1.0 - PCI BAR 1 + 0x00
 77  *   PLD Revision 2.x - PCI BAR 0 + 0x04
 78  */
 79 #define APCI1564_DI_REG                         0x00
 80 #define APCI1564_DI_INT_MODE1_REG               0x04
 81 #define APCI1564_DI_INT_MODE2_REG               0x08
 82 #define APCI1564_DI_INT_STATUS_REG              0x0c
 83 #define APCI1564_DI_IRQ_REG                     0x10
 84 #define APCI1564_DO_REG                         0x14
 85 #define APCI1564_DO_INT_CTRL_REG                0x18
 86 #define APCI1564_DO_INT_STATUS_REG              0x1c
 87 #define APCI1564_DO_IRQ_REG                     0x20
 88 #define APCI1564_WDOG_REG                       0x24
 89 #define APCI1564_WDOG_RELOAD_REG                0x28
 90 #define APCI1564_WDOG_TIMEBASE_REG              0x2c
 91 #define APCI1564_WDOG_CTRL_REG                  0x30
 92 #define APCI1564_WDOG_STATUS_REG                0x34
 93 #define APCI1564_WDOG_IRQ_REG                   0x38
 94 #define APCI1564_WDOG_WARN_TIMEVAL_REG          0x3c
 95 #define APCI1564_WDOG_WARN_TIMEBASE_REG         0x40
 96 
 97 /*
 98  * devpriv->timer Register Map (see addi_tcw.h for register/bit defines)
 99  *   PLD Revision 1.0 - PCI BAR 0 + 0x04
100  *   PLD Revision 2.x - PCI BAR 0 + 0x48
101  */
102 
103 /*
104  * devpriv->counters Register Map (see addi_tcw.h for register/bit defines)
105  *   PLD Revision 2.x - PCI BAR 1 + 0x00
106  */
107 #define APCI1564_COUNTER(x)                     ((x) * 0x20)
108 
109 struct apci1564_private {
110         unsigned long eeprom;           /* base address of EEPROM register */
111         unsigned long timer;            /* base address of 12-bit timer */
112         unsigned long counters;         /* base address of 32-bit counters */
113         unsigned int mode1;             /* riding-edge/high level channels */
114         unsigned int mode2;             /* falling-edge/low level channels */
115         unsigned int ctrl;              /* interrupt mode OR (edge) . AND (level) */
116         struct task_struct *tsk_current;
117 };
118 
119 #include "addi-data/hwdrv_apci1564.c"
120 
121 static int apci1564_reset(struct comedi_device *dev)
122 {
123         struct apci1564_private *devpriv = dev->private;
124 
125         /* Disable the input interrupts and reset status register */
126         outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
127         inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
128         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG);
129         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG);
130 
131         /* Reset the output channels and disable interrupts */
132         outl(0x0, dev->iobase + APCI1564_DO_REG);
133         outl(0x0, dev->iobase + APCI1564_DO_INT_CTRL_REG);
134 
135         /* Reset the watchdog registers */
136         addi_watchdog_reset(dev->iobase + APCI1564_WDOG_REG);
137 
138         /* Reset the timer registers */
139         outl(0x0, devpriv->timer + ADDI_TCW_CTRL_REG);
140         outl(0x0, devpriv->timer + ADDI_TCW_RELOAD_REG);
141 
142         if (devpriv->counters) {
143                 unsigned long iobase = devpriv->counters + ADDI_TCW_CTRL_REG;
144 
145                 /* Reset the counter registers */
146                 outl(0x0, iobase + APCI1564_COUNTER(0));
147                 outl(0x0, iobase + APCI1564_COUNTER(1));
148                 outl(0x0, iobase + APCI1564_COUNTER(2));
149         }
150 
151         return 0;
152 }
153 
154 static irqreturn_t apci1564_interrupt(int irq, void *d)
155 {
156         struct comedi_device *dev = d;
157         struct apci1564_private *devpriv = dev->private;
158         struct comedi_subdevice *s = dev->read_subdev;
159         unsigned int status;
160         unsigned int ctrl;
161         unsigned int chan;
162 
163         status = inl(dev->iobase + APCI1564_DI_IRQ_REG);
164         if (status & APCI1564_DI_INT_ENABLE) {
165                 /* disable the interrupt */
166                 outl(status & APCI1564_DI_INT_DISABLE,
167                      dev->iobase + APCI1564_DI_IRQ_REG);
168 
169                 s->state = inl(dev->iobase + APCI1564_DI_INT_STATUS_REG) &
170                            0xffff;
171                 comedi_buf_write_samples(s, &s->state, 1);
172                 comedi_handle_events(dev, s);
173 
174                 /* enable the interrupt */
175                 outl(status, dev->iobase + APCI1564_DI_IRQ_REG);
176         }
177 
178         status = inl(devpriv->timer + ADDI_TCW_IRQ_REG);
179         if (status & 0x01) {
180                 /*  Disable Timer Interrupt */
181                 ctrl = inl(devpriv->timer + ADDI_TCW_CTRL_REG);
182                 outl(0x0, devpriv->timer + ADDI_TCW_CTRL_REG);
183 
184                 /* Send a signal to from kernel to user space */
185                 send_sig(SIGIO, devpriv->tsk_current, 0);
186 
187                 /*  Enable Timer Interrupt */
188                 outl(ctrl, devpriv->timer + ADDI_TCW_CTRL_REG);
189         }
190 
191         if (devpriv->counters) {
192                 for (chan = 0; chan < 4; chan++) {
193                         unsigned long iobase;
194 
195                         iobase = devpriv->counters + APCI1564_COUNTER(chan);
196 
197                         status = inl(iobase + ADDI_TCW_IRQ_REG);
198                         if (status & 0x01) {
199                                 /*  Disable Counter Interrupt */
200                                 ctrl = inl(iobase + ADDI_TCW_CTRL_REG);
201                                 outl(0x0, iobase + ADDI_TCW_CTRL_REG);
202 
203                                 /* Send a signal to from kernel to user space */
204                                 send_sig(SIGIO, devpriv->tsk_current, 0);
205 
206                                 /*  Enable Counter Interrupt */
207                                 outl(ctrl, iobase + ADDI_TCW_CTRL_REG);
208                         }
209                 }
210         }
211 
212         return IRQ_HANDLED;
213 }
214 
215 static int apci1564_di_insn_bits(struct comedi_device *dev,
216                                  struct comedi_subdevice *s,
217                                  struct comedi_insn *insn,
218                                  unsigned int *data)
219 {
220         data[1] = inl(dev->iobase + APCI1564_DI_REG);
221 
222         return insn->n;
223 }
224 
225 static int apci1564_do_insn_bits(struct comedi_device *dev,
226                                  struct comedi_subdevice *s,
227                                  struct comedi_insn *insn,
228                                  unsigned int *data)
229 {
230         s->state = inl(dev->iobase + APCI1564_DO_REG);
231 
232         if (comedi_dio_update_state(s, data))
233                 outl(s->state, dev->iobase + APCI1564_DO_REG);
234 
235         data[1] = s->state;
236 
237         return insn->n;
238 }
239 
240 static int apci1564_diag_insn_bits(struct comedi_device *dev,
241                                    struct comedi_subdevice *s,
242                                    struct comedi_insn *insn,
243                                    unsigned int *data)
244 {
245         data[1] = inl(dev->iobase + APCI1564_DO_INT_STATUS_REG) & 3;
246 
247         return insn->n;
248 }
249 
250 /*
251  * Change-Of-State (COS) interrupt configuration
252  *
253  * Channels 0 to 15 are interruptible. These channels can be configured
254  * to generate interrupts based on AND/OR logic for the desired channels.
255  *
256  *      OR logic
257  *              - reacts to rising or falling edges
258  *              - interrupt is generated when any enabled channel
259  *                meet the desired interrupt condition
260  *
261  *      AND logic
262  *              - reacts to changes in level of the selected inputs
263  *              - interrupt is generated when all enabled channels
264  *                meet the desired interrupt condition
265  *              - after an interrupt, a change in level must occur on
266  *                the selected inputs to release the IRQ logic
267  *
268  * The COS interrupt must be configured before it can be enabled.
269  *
270  *      data[0] : INSN_CONFIG_DIGITAL_TRIG
271  *      data[1] : trigger number (= 0)
272  *      data[2] : configuration operation:
273  *                COMEDI_DIGITAL_TRIG_DISABLE = no interrupts
274  *                COMEDI_DIGITAL_TRIG_ENABLE_EDGES = OR (edge) interrupts
275  *                COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = AND (level) interrupts
276  *      data[3] : left-shift for data[4] and data[5]
277  *      data[4] : rising-edge/high level channels
278  *      data[5] : falling-edge/low level channels
279  */
280 static int apci1564_cos_insn_config(struct comedi_device *dev,
281                                     struct comedi_subdevice *s,
282                                     struct comedi_insn *insn,
283                                     unsigned int *data)
284 {
285         struct apci1564_private *devpriv = dev->private;
286         unsigned int shift, oldmask;
287 
288         switch (data[0]) {
289         case INSN_CONFIG_DIGITAL_TRIG:
290                 if (data[1] != 0)
291                         return -EINVAL;
292                 shift = data[3];
293                 oldmask = (1U << shift) - 1;
294                 switch (data[2]) {
295                 case COMEDI_DIGITAL_TRIG_DISABLE:
296                         devpriv->ctrl = 0;
297                         devpriv->mode1 = 0;
298                         devpriv->mode2 = 0;
299                         outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
300                         inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
301                         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG);
302                         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG);
303                         break;
304                 case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
305                         if (devpriv->ctrl != (APCI1564_DI_INT_ENABLE |
306                                               APCI1564_DI_INT_OR)) {
307                                 /* switching to 'OR' mode */
308                                 devpriv->ctrl = APCI1564_DI_INT_ENABLE |
309                                                 APCI1564_DI_INT_OR;
310                                 /* wipe old channels */
311                                 devpriv->mode1 = 0;
312                                 devpriv->mode2 = 0;
313                         } else {
314                                 /* preserve unspecified channels */
315                                 devpriv->mode1 &= oldmask;
316                                 devpriv->mode2 &= oldmask;
317                         }
318                         /* configure specified channels */
319                         devpriv->mode1 |= data[4] << shift;
320                         devpriv->mode2 |= data[5] << shift;
321                         break;
322                 case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS:
323                         if (devpriv->ctrl != (APCI1564_DI_INT_ENABLE |
324                                               APCI1564_DI_INT_AND)) {
325                                 /* switching to 'AND' mode */
326                                 devpriv->ctrl = APCI1564_DI_INT_ENABLE |
327                                                 APCI1564_DI_INT_AND;
328                                 /* wipe old channels */
329                                 devpriv->mode1 = 0;
330                                 devpriv->mode2 = 0;
331                         } else {
332                                 /* preserve unspecified channels */
333                                 devpriv->mode1 &= oldmask;
334                                 devpriv->mode2 &= oldmask;
335                         }
336                         /* configure specified channels */
337                         devpriv->mode1 |= data[4] << shift;
338                         devpriv->mode2 |= data[5] << shift;
339                         break;
340                 default:
341                         return -EINVAL;
342                 }
343                 break;
344         default:
345                 return -EINVAL;
346         }
347         return insn->n;
348 }
349 
350 static int apci1564_cos_insn_bits(struct comedi_device *dev,
351                                   struct comedi_subdevice *s,
352                                   struct comedi_insn *insn,
353                                   unsigned int *data)
354 {
355         data[1] = s->state;
356 
357         return 0;
358 }
359 
360 static int apci1564_cos_cmdtest(struct comedi_device *dev,
361                                 struct comedi_subdevice *s,
362                                 struct comedi_cmd *cmd)
363 {
364         int err = 0;
365 
366         /* Step 1 : check if triggers are trivially valid */
367 
368         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
369         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
370         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
371         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
372         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
373 
374         if (err)
375                 return 1;
376 
377         /* Step 2a : make sure trigger sources are unique */
378         /* Step 2b : and mutually compatible */
379 
380         /* Step 3: check if arguments are trivially valid */
381 
382         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
383         err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
384         err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
385         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
386         err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
387 
388         if (err)
389                 return 3;
390 
391         /* Step 4: fix up any arguments */
392 
393         /* Step 5: check channel list if it exists */
394 
395         return 0;
396 }
397 
398 /*
399  * Change-Of-State (COS) 'do_cmd' operation
400  *
401  * Enable the COS interrupt as configured by apci1564_cos_insn_config().
402  */
403 static int apci1564_cos_cmd(struct comedi_device *dev,
404                             struct comedi_subdevice *s)
405 {
406         struct apci1564_private *devpriv = dev->private;
407 
408         if (!devpriv->ctrl) {
409                 dev_warn(dev->class_dev,
410                         "Interrupts disabled due to mode configuration!\n");
411                 return -EINVAL;
412         }
413 
414         outl(devpriv->mode1, dev->iobase + APCI1564_DI_INT_MODE1_REG);
415         outl(devpriv->mode2, dev->iobase + APCI1564_DI_INT_MODE2_REG);
416         outl(devpriv->ctrl, dev->iobase + APCI1564_DI_IRQ_REG);
417 
418         return 0;
419 }
420 
421 static int apci1564_cos_cancel(struct comedi_device *dev,
422                                struct comedi_subdevice *s)
423 {
424         outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
425         inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
426         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG);
427         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG);
428 
429         return 0;
430 }
431 
432 static int apci1564_auto_attach(struct comedi_device *dev,
433                                       unsigned long context_unused)
434 {
435         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
436         struct apci1564_private *devpriv;
437         struct comedi_subdevice *s;
438         unsigned int val;
439         int ret;
440 
441         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
442         if (!devpriv)
443                 return -ENOMEM;
444 
445         ret = comedi_pci_enable(dev);
446         if (ret)
447                 return ret;
448 
449         /* read the EEPROM register and check the I/O map revision */
450         devpriv->eeprom = pci_resource_start(pcidev, 0);
451         val = inl(devpriv->eeprom + APCI1564_EEPROM_REG);
452         if (APCI1564_EEPROM_TO_REV(val) == 0) {
453                 /* PLD Revision 1.0 I/O Mapping */
454                 dev->iobase = pci_resource_start(pcidev, 1) +
455                               APCI1564_REV1_MAIN_IOBASE;
456                 devpriv->timer = devpriv->eeprom + APCI1564_REV1_TIMER_IOBASE;
457         } else {
458                 /* PLD Revision 2.x I/O Mapping */
459                 dev->iobase = devpriv->eeprom + APCI1564_REV2_MAIN_IOBASE;
460                 devpriv->timer = devpriv->eeprom + APCI1564_REV2_TIMER_IOBASE;
461                 devpriv->counters = pci_resource_start(pcidev, 1);
462         }
463 
464         apci1564_reset(dev);
465 
466         if (pcidev->irq > 0) {
467                 ret = request_irq(pcidev->irq, apci1564_interrupt, IRQF_SHARED,
468                                   dev->board_name, dev);
469                 if (ret == 0)
470                         dev->irq = pcidev->irq;
471         }
472 
473         ret = comedi_alloc_subdevices(dev, 7);
474         if (ret)
475                 return ret;
476 
477         /*  Allocate and Initialise DI Subdevice Structures */
478         s = &dev->subdevices[0];
479         s->type         = COMEDI_SUBD_DI;
480         s->subdev_flags = SDF_READABLE;
481         s->n_chan       = 32;
482         s->maxdata      = 1;
483         s->range_table  = &range_digital;
484         s->insn_bits    = apci1564_di_insn_bits;
485 
486         /*  Allocate and Initialise DO Subdevice Structures */
487         s = &dev->subdevices[1];
488         s->type         = COMEDI_SUBD_DO;
489         s->subdev_flags = SDF_WRITABLE;
490         s->n_chan       = 32;
491         s->maxdata      = 1;
492         s->range_table  = &range_digital;
493         s->insn_bits    = apci1564_do_insn_bits;
494 
495         /* Change-Of-State (COS) interrupt subdevice */
496         s = &dev->subdevices[2];
497         if (dev->irq) {
498                 dev->read_subdev = s;
499                 s->type         = COMEDI_SUBD_DI;
500                 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
501                 s->n_chan       = 1;
502                 s->maxdata      = 1;
503                 s->range_table  = &range_digital;
504                 s->len_chanlist = 1;
505                 s->insn_config  = apci1564_cos_insn_config;
506                 s->insn_bits    = apci1564_cos_insn_bits;
507                 s->do_cmdtest   = apci1564_cos_cmdtest;
508                 s->do_cmd       = apci1564_cos_cmd;
509                 s->cancel       = apci1564_cos_cancel;
510         } else {
511                 s->type         = COMEDI_SUBD_UNUSED;
512         }
513 
514         /* Timer subdevice */
515         s = &dev->subdevices[3];
516         s->type         = COMEDI_SUBD_TIMER;
517         s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
518         s->n_chan       = 1;
519         s->maxdata      = 0x0fff;
520         s->range_table  = &range_digital;
521         s->insn_config  = apci1564_timer_insn_config;
522         s->insn_write   = apci1564_timer_insn_write;
523         s->insn_read    = apci1564_timer_insn_read;
524 
525         /* Counter subdevice */
526         s = &dev->subdevices[4];
527         if (devpriv->counters) {
528                 s->type         = COMEDI_SUBD_COUNTER;
529                 s->subdev_flags = SDF_WRITABLE | SDF_READABLE | SDF_LSAMPL;
530                 s->n_chan       = 3;
531                 s->maxdata      = 0xffffffff;
532                 s->range_table  = &range_digital;
533                 s->insn_config  = apci1564_counter_insn_config;
534                 s->insn_write   = apci1564_counter_insn_write;
535                 s->insn_read    = apci1564_counter_insn_read;
536         } else {
537                 s->type         = COMEDI_SUBD_UNUSED;
538         }
539 
540         /* Initialize the watchdog subdevice */
541         s = &dev->subdevices[5];
542         ret = addi_watchdog_init(s, dev->iobase + APCI1564_WDOG_REG);
543         if (ret)
544                 return ret;
545 
546         /* Initialize the diagnostic status subdevice */
547         s = &dev->subdevices[6];
548         s->type         = COMEDI_SUBD_DI;
549         s->subdev_flags = SDF_READABLE;
550         s->n_chan       = 2;
551         s->maxdata      = 1;
552         s->range_table  = &range_digital;
553         s->insn_bits    = apci1564_diag_insn_bits;
554 
555         return 0;
556 }
557 
558 static void apci1564_detach(struct comedi_device *dev)
559 {
560         if (dev->iobase)
561                 apci1564_reset(dev);
562         comedi_pci_detach(dev);
563 }
564 
565 static struct comedi_driver apci1564_driver = {
566         .driver_name    = "addi_apci_1564",
567         .module         = THIS_MODULE,
568         .auto_attach    = apci1564_auto_attach,
569         .detach         = apci1564_detach,
570 };
571 
572 static int apci1564_pci_probe(struct pci_dev *dev,
573                               const struct pci_device_id *id)
574 {
575         return comedi_pci_auto_config(dev, &apci1564_driver, id->driver_data);
576 }
577 
578 static const struct pci_device_id apci1564_pci_table[] = {
579         { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1006) },
580         { 0 }
581 };
582 MODULE_DEVICE_TABLE(pci, apci1564_pci_table);
583 
584 static struct pci_driver apci1564_pci_driver = {
585         .name           = "addi_apci_1564",
586         .id_table       = apci1564_pci_table,
587         .probe          = apci1564_pci_probe,
588         .remove         = comedi_pci_auto_unconfig,
589 };
590 module_comedi_pci_driver(apci1564_driver, apci1564_pci_driver);
591 
592 MODULE_AUTHOR("Comedi http://www.comedi.org");
593 MODULE_DESCRIPTION("ADDI-DATA APCI-1564, 32 channel DI / 32 channel DO boards");
594 MODULE_LICENSE("GPL");
595 

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us