]> Pileus Git - ~andy/linux/blob - drivers/staging/comedi/drivers/icp_multi.c
staging: comedi: conditionally build in PCI driver support
[~andy/linux] / drivers / staging / comedi / drivers / icp_multi.c
1 /*
2     comedi/drivers/icp_multi.c
3
4     COMEDI - Linux Control and Measurement Device Interface
5     Copyright (C) 1997-2002 David A. Schleef <ds@schleef.org>
6
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21 */
22
23 /*
24 Driver: icp_multi
25 Description: Inova ICP_MULTI
26 Author: Anne Smorthit <anne.smorthit@sfwte.ch>
27 Devices: [Inova] ICP_MULTI (icp_multi)
28 Status: works
29
30 The driver works for analog input and output and digital input and output.
31 It does not work with interrupts or with the counters.  Currently no support
32 for DMA.
33
34 It has 16 single-ended or 8 differential Analogue Input channels with 12-bit
35 resolution.  Ranges : 5V, 10V, +/-5V, +/-10V, 0..20mA and 4..20mA.  Input
36 ranges can be individually programmed for each channel.  Voltage or current
37 measurement is selected by jumper.
38
39 There are 4 x 12-bit Analogue Outputs.  Ranges : 5V, 10V, +/-5V, +/-10V
40
41 16 x Digital Inputs, 24V
42
43 8 x Digital Outputs, 24V, 1A
44
45 4 x 16-bit counters
46
47 Configuration options: not applicable, uses PCI auto config
48 */
49
50 #include <linux/pci.h>
51 #include <linux/delay.h>
52 #include <linux/interrupt.h>
53
54 #include "../comedidev.h"
55
56 #define PCI_DEVICE_ID_ICP_MULTI 0x8000
57
58 #define ICP_MULTI_ADC_CSR       0       /* R/W: ADC command/status register */
59 #define ICP_MULTI_AI            2       /* R:   Analogue input data */
60 #define ICP_MULTI_DAC_CSR       4       /* R/W: DAC command/status register */
61 #define ICP_MULTI_AO            6       /* R/W: Analogue output data */
62 #define ICP_MULTI_DI            8       /* R/W: Digital inouts */
63 #define ICP_MULTI_DO            0x0A    /* R/W: Digital outputs */
64 #define ICP_MULTI_INT_EN        0x0C    /* R/W: Interrupt enable register */
65 #define ICP_MULTI_INT_STAT      0x0E    /* R/W: Interrupt status register */
66 #define ICP_MULTI_CNTR0         0x10    /* R/W: Counter 0 */
67 #define ICP_MULTI_CNTR1         0x12    /* R/W: counter 1 */
68 #define ICP_MULTI_CNTR2         0x14    /* R/W: Counter 2 */
69 #define ICP_MULTI_CNTR3         0x16    /* R/W: Counter 3 */
70
71 #define ICP_MULTI_SIZE          0x20    /* 32 bytes */
72
73 /*  Define bits from ADC command/status register */
74 #define ADC_ST          0x0001  /* Start ADC */
75 #define ADC_BSY         0x0001  /* ADC busy */
76 #define ADC_BI          0x0010  /* Bipolar input range 1 = bipolar */
77 #define ADC_RA          0x0020  /* Input range 0 = 5V, 1 = 10V */
78 #define ADC_DI          0x0040  /* Differential input mode 1 = differential */
79
80 /*  Define bits from DAC command/status register */
81 #define DAC_ST          0x0001  /* Start DAC */
82 #define DAC_BSY         0x0001  /* DAC busy */
83 #define DAC_BI          0x0010  /* Bipolar input range 1 = bipolar */
84 #define DAC_RA          0x0020  /* Input range 0 = 5V, 1 = 10V */
85
86 /*  Define bits from interrupt enable/status registers */
87 #define ADC_READY       0x0001  /* A/d conversion ready interrupt */
88 #define DAC_READY       0x0002  /* D/a conversion ready interrupt */
89 #define DOUT_ERROR      0x0004  /* Digital output error interrupt */
90 #define DIN_STATUS      0x0008  /* Digital input status change interrupt */
91 #define CIE0            0x0010  /* Counter 0 overrun interrupt */
92 #define CIE1            0x0020  /* Counter 1 overrun interrupt */
93 #define CIE2            0x0040  /* Counter 2 overrun interrupt */
94 #define CIE3            0x0080  /* Counter 3 overrun interrupt */
95
96 /*  Useful definitions */
97 #define Status_IRQ      0x00ff  /*  All interrupts */
98
99 /*  Define analogue range */
100 static const struct comedi_lrange range_analog = { 4, {
101                                                        UNI_RANGE(5),
102                                                        UNI_RANGE(10),
103                                                        BIP_RANGE(5),
104                                                        BIP_RANGE(10)
105                                                        }
106 };
107
108 static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
109
110 /*
111 ==============================================================================
112         Data & Structure declarations
113 ==============================================================================
114 */
115
116 struct icp_multi_private {
117         char valid;             /*  card is usable */
118         void __iomem *io_addr;          /*  Pointer to mapped io address */
119         unsigned int AdcCmdStatus;      /*  ADC Command/Status register */
120         unsigned int DacCmdStatus;      /*  DAC Command/Status register */
121         unsigned int IntEnable; /*  Interrupt Enable register */
122         unsigned int IntStatus; /*  Interrupt Status register */
123         unsigned int act_chanlist[32];  /*  list of scaned channel */
124         unsigned char act_chanlist_len; /*  len of scanlist */
125         unsigned char act_chanlist_pos; /*  actual position in MUX list */
126         unsigned int *ai_chanlist;      /*  actaul chanlist */
127         short *ai_data;         /*  data buffer */
128         short ao_data[4];       /*  data output buffer */
129         short di_data;          /*  Digital input data */
130         unsigned int do_data;   /*  Remember digital output data */
131 };
132
133 static void setup_channel_list(struct comedi_device *dev,
134                                struct comedi_subdevice *s,
135                                unsigned int *chanlist, unsigned int n_chan)
136 {
137         struct icp_multi_private *devpriv = dev->private;
138         unsigned int i, range, chanprog;
139         unsigned int diff;
140
141         devpriv->act_chanlist_len = n_chan;
142         devpriv->act_chanlist_pos = 0;
143
144         for (i = 0; i < n_chan; i++) {
145                 /*  Get channel */
146                 chanprog = CR_CHAN(chanlist[i]);
147
148                 /*  Determine if it is a differential channel (Bit 15  = 1) */
149                 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
150                         diff = 1;
151                         chanprog &= 0x0007;
152                 } else {
153                         diff = 0;
154                         chanprog &= 0x000f;
155                 }
156
157                 /*  Clear channel, range and input mode bits
158                  *  in A/D command/status register */
159                 devpriv->AdcCmdStatus &= 0xf00f;
160
161                 /*  Set channel number and differential mode status bit */
162                 if (diff) {
163                         /*  Set channel number, bits 9-11 & mode, bit 6 */
164                         devpriv->AdcCmdStatus |= (chanprog << 9);
165                         devpriv->AdcCmdStatus |= ADC_DI;
166                 } else
167                         /*  Set channel number, bits 8-11 */
168                         devpriv->AdcCmdStatus |= (chanprog << 8);
169
170                 /*  Get range for current channel */
171                 range = range_codes_analog[CR_RANGE(chanlist[i])];
172                 /*  Set range. bits 4-5 */
173                 devpriv->AdcCmdStatus |= range;
174
175                 /* Output channel, range, mode to ICP Multi */
176                 writew(devpriv->AdcCmdStatus,
177                        devpriv->io_addr + ICP_MULTI_ADC_CSR);
178         }
179 }
180
181 static int icp_multi_insn_read_ai(struct comedi_device *dev,
182                                   struct comedi_subdevice *s,
183                                   struct comedi_insn *insn, unsigned int *data)
184 {
185         struct icp_multi_private *devpriv = dev->private;
186         int n, timeout;
187
188         /*  Disable A/D conversion ready interrupt */
189         devpriv->IntEnable &= ~ADC_READY;
190         writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
191
192         /*  Clear interrupt status */
193         devpriv->IntStatus |= ADC_READY;
194         writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
195
196         /*  Set up appropriate channel, mode and range data, for specified ch */
197         setup_channel_list(dev, s, &insn->chanspec, 1);
198
199         for (n = 0; n < insn->n; n++) {
200                 /*  Set start ADC bit */
201                 devpriv->AdcCmdStatus |= ADC_ST;
202                 writew(devpriv->AdcCmdStatus,
203                        devpriv->io_addr + ICP_MULTI_ADC_CSR);
204                 devpriv->AdcCmdStatus &= ~ADC_ST;
205
206                 udelay(1);
207
208                 /*  Wait for conversion to complete, or get fed up waiting */
209                 timeout = 100;
210                 while (timeout--) {
211                         if (!(readw(devpriv->io_addr +
212                                     ICP_MULTI_ADC_CSR) & ADC_BSY))
213                                 goto conv_finish;
214
215                         udelay(1);
216                 }
217
218                 /*  If we reach here, a timeout has occurred */
219                 comedi_error(dev, "A/D insn timeout");
220
221                 /*  Disable interrupt */
222                 devpriv->IntEnable &= ~ADC_READY;
223                 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
224
225                 /*  Clear interrupt status */
226                 devpriv->IntStatus |= ADC_READY;
227                 writew(devpriv->IntStatus,
228                        devpriv->io_addr + ICP_MULTI_INT_STAT);
229
230                 /*  Clear data received */
231                 data[n] = 0;
232
233                 return -ETIME;
234
235 conv_finish:
236                 data[n] =
237                     (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff;
238         }
239
240         /*  Disable interrupt */
241         devpriv->IntEnable &= ~ADC_READY;
242         writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
243
244         /*  Clear interrupt status */
245         devpriv->IntStatus |= ADC_READY;
246         writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
247
248         return n;
249 }
250
251 static int icp_multi_insn_write_ao(struct comedi_device *dev,
252                                    struct comedi_subdevice *s,
253                                    struct comedi_insn *insn, unsigned int *data)
254 {
255         struct icp_multi_private *devpriv = dev->private;
256         int n, chan, range, timeout;
257
258         /*  Disable D/A conversion ready interrupt */
259         devpriv->IntEnable &= ~DAC_READY;
260         writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
261
262         /*  Clear interrupt status */
263         devpriv->IntStatus |= DAC_READY;
264         writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
265
266         /*  Get channel number and range */
267         chan = CR_CHAN(insn->chanspec);
268         range = CR_RANGE(insn->chanspec);
269
270         /*  Set up range and channel data */
271         /*  Bit 4 = 1 : Bipolar */
272         /*  Bit 5 = 0 : 5V */
273         /*  Bit 5 = 1 : 10V */
274         /*  Bits 8-9 : Channel number */
275         devpriv->DacCmdStatus &= 0xfccf;
276         devpriv->DacCmdStatus |= range_codes_analog[range];
277         devpriv->DacCmdStatus |= (chan << 8);
278
279         writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR);
280
281         for (n = 0; n < insn->n; n++) {
282                 /*  Wait for analogue output data register to be
283                  *  ready for new data, or get fed up waiting */
284                 timeout = 100;
285                 while (timeout--) {
286                         if (!(readw(devpriv->io_addr +
287                                     ICP_MULTI_DAC_CSR) & DAC_BSY))
288                                 goto dac_ready;
289
290                         udelay(1);
291                 }
292
293                 /*  If we reach here, a timeout has occurred */
294                 comedi_error(dev, "D/A insn timeout");
295
296                 /*  Disable interrupt */
297                 devpriv->IntEnable &= ~DAC_READY;
298                 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
299
300                 /*  Clear interrupt status */
301                 devpriv->IntStatus |= DAC_READY;
302                 writew(devpriv->IntStatus,
303                        devpriv->io_addr + ICP_MULTI_INT_STAT);
304
305                 /*  Clear data received */
306                 devpriv->ao_data[chan] = 0;
307
308                 return -ETIME;
309
310 dac_ready:
311                 /*  Write data to analogue output data register */
312                 writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
313
314                 /*  Set DAC_ST bit to write the data to selected channel */
315                 devpriv->DacCmdStatus |= DAC_ST;
316                 writew(devpriv->DacCmdStatus,
317                        devpriv->io_addr + ICP_MULTI_DAC_CSR);
318                 devpriv->DacCmdStatus &= ~DAC_ST;
319
320                 /*  Save analogue output data */
321                 devpriv->ao_data[chan] = data[n];
322         }
323
324         return n;
325 }
326
327 static int icp_multi_insn_read_ao(struct comedi_device *dev,
328                                   struct comedi_subdevice *s,
329                                   struct comedi_insn *insn, unsigned int *data)
330 {
331         struct icp_multi_private *devpriv = dev->private;
332         int n, chan;
333
334         /*  Get channel number */
335         chan = CR_CHAN(insn->chanspec);
336
337         /*  Read analogue outputs */
338         for (n = 0; n < insn->n; n++)
339                 data[n] = devpriv->ao_data[chan];
340
341         return n;
342 }
343
344 static int icp_multi_insn_bits_di(struct comedi_device *dev,
345                                   struct comedi_subdevice *s,
346                                   struct comedi_insn *insn, unsigned int *data)
347 {
348         struct icp_multi_private *devpriv = dev->private;
349
350         data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
351
352         return insn->n;
353 }
354
355 static int icp_multi_insn_bits_do(struct comedi_device *dev,
356                                   struct comedi_subdevice *s,
357                                   struct comedi_insn *insn, unsigned int *data)
358 {
359         struct icp_multi_private *devpriv = dev->private;
360
361         if (data[0]) {
362                 s->state &= ~data[0];
363                 s->state |= (data[0] & data[1]);
364
365                 printk(KERN_DEBUG "Digital outputs = %4x \n", s->state);
366
367                 writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
368         }
369
370         data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
371
372         return insn->n;
373 }
374
375 static int icp_multi_insn_read_ctr(struct comedi_device *dev,
376                                    struct comedi_subdevice *s,
377                                    struct comedi_insn *insn, unsigned int *data)
378 {
379         return 0;
380 }
381
382 static int icp_multi_insn_write_ctr(struct comedi_device *dev,
383                                     struct comedi_subdevice *s,
384                                     struct comedi_insn *insn,
385                                     unsigned int *data)
386 {
387         return 0;
388 }
389
390 static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
391 {
392         struct comedi_device *dev = d;
393         struct icp_multi_private *devpriv = dev->private;
394         int int_no;
395
396         /*  Is this interrupt from our board? */
397         int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
398         if (!int_no)
399                 /*  No, exit */
400                 return IRQ_NONE;
401
402         /*  Determine which interrupt is active & handle it */
403         switch (int_no) {
404         case ADC_READY:
405                 break;
406         case DAC_READY:
407                 break;
408         case DOUT_ERROR:
409                 break;
410         case DIN_STATUS:
411                 break;
412         case CIE0:
413                 break;
414         case CIE1:
415                 break;
416         case CIE2:
417                 break;
418         case CIE3:
419                 break;
420         default:
421                 break;
422
423         }
424
425         return IRQ_HANDLED;
426 }
427
428 #if 0
429 static int check_channel_list(struct comedi_device *dev,
430                               struct comedi_subdevice *s,
431                               unsigned int *chanlist, unsigned int n_chan)
432 {
433         unsigned int i;
434
435         /*  Check that we at least have one channel to check */
436         if (n_chan < 1) {
437                 comedi_error(dev, "range/channel list is empty!");
438                 return 0;
439         }
440         /*  Check all channels */
441         for (i = 0; i < n_chan; i++) {
442                 /*  Check that channel number is < maximum */
443                 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
444                         if (CR_CHAN(chanlist[i]) > (s->nchan / 2)) {
445                                 comedi_error(dev,
446                                              "Incorrect differential ai ch-nr");
447                                 return 0;
448                         }
449                 } else {
450                         if (CR_CHAN(chanlist[i]) > s->n_chan) {
451                                 comedi_error(dev,
452                                              "Incorrect ai channel number");
453                                 return 0;
454                         }
455                 }
456         }
457         return 1;
458 }
459 #endif
460
461 static int icp_multi_reset(struct comedi_device *dev)
462 {
463         struct icp_multi_private *devpriv = dev->private;
464         unsigned int i;
465
466         /*  Clear INT enables and requests */
467         writew(0, devpriv->io_addr + ICP_MULTI_INT_EN);
468         writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT);
469
470         /* Set DACs to 0..5V range and 0V output */
471         for (i = 0; i < 4; i++) {
472                 devpriv->DacCmdStatus &= 0xfcce;
473
474                 /*  Set channel number */
475                 devpriv->DacCmdStatus |= (i << 8);
476
477                 /*  Output 0V */
478                 writew(0, devpriv->io_addr + ICP_MULTI_AO);
479
480                 /*  Set start conversion bit */
481                 devpriv->DacCmdStatus |= DAC_ST;
482
483                 /*  Output to command / status register */
484                 writew(devpriv->DacCmdStatus,
485                         devpriv->io_addr + ICP_MULTI_DAC_CSR);
486
487                 /*  Delay to allow DAC time to recover */
488                 udelay(1);
489         }
490
491         /* Digital outputs to 0 */
492         writew(0, devpriv->io_addr + ICP_MULTI_DO);
493
494         return 0;
495 }
496
497 static int icp_multi_auto_attach(struct comedi_device *dev,
498                                            unsigned long context_unused)
499 {
500         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
501         struct icp_multi_private *devpriv;
502         struct comedi_subdevice *s;
503         resource_size_t iobase;
504         int ret;
505
506         dev->board_name = dev->driver->driver_name;
507
508         devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
509         if (!devpriv)
510                 return -ENOMEM;
511         dev->private = devpriv;
512
513         ret = comedi_pci_enable(pcidev, dev->board_name);
514         if (ret)
515                 return ret;
516         iobase = pci_resource_start(pcidev, 2);
517         dev->iobase = iobase;
518
519         devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
520         if (!devpriv->io_addr)
521                 return -ENOMEM;
522
523         ret = comedi_alloc_subdevices(dev, 5);
524         if (ret)
525                 return ret;
526
527         icp_multi_reset(dev);
528
529         if (pcidev->irq) {
530                 ret = request_irq(pcidev->irq, interrupt_service_icp_multi,
531                                   IRQF_SHARED, dev->board_name, dev);
532                 if (ret == 0)
533                         dev->irq = pcidev->irq;
534         }
535
536         s = &dev->subdevices[0];
537         dev->read_subdev = s;
538         s->type = COMEDI_SUBD_AI;
539         s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
540         s->n_chan = 16;
541         s->maxdata = 0x0fff;
542         s->len_chanlist = 16;
543         s->range_table = &range_analog;
544         s->insn_read = icp_multi_insn_read_ai;
545
546         s = &dev->subdevices[1];
547         s->type = COMEDI_SUBD_AO;
548         s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
549         s->n_chan = 4;
550         s->maxdata = 0x0fff;
551         s->len_chanlist = 4;
552         s->range_table = &range_analog;
553         s->insn_write = icp_multi_insn_write_ao;
554         s->insn_read = icp_multi_insn_read_ao;
555
556         s = &dev->subdevices[2];
557         s->type = COMEDI_SUBD_DI;
558         s->subdev_flags = SDF_READABLE;
559         s->n_chan = 16;
560         s->maxdata = 1;
561         s->len_chanlist = 16;
562         s->range_table = &range_digital;
563         s->io_bits = 0;
564         s->insn_bits = icp_multi_insn_bits_di;
565
566         s = &dev->subdevices[3];
567         s->type = COMEDI_SUBD_DO;
568         s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
569         s->n_chan = 8;
570         s->maxdata = 1;
571         s->len_chanlist = 8;
572         s->range_table = &range_digital;
573         s->io_bits = 0xff;
574         s->state = 0;
575         s->insn_bits = icp_multi_insn_bits_do;
576
577         s = &dev->subdevices[4];
578         s->type = COMEDI_SUBD_COUNTER;
579         s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
580         s->n_chan = 4;
581         s->maxdata = 0xffff;
582         s->len_chanlist = 4;
583         s->state = 0;
584         s->insn_read = icp_multi_insn_read_ctr;
585         s->insn_write = icp_multi_insn_write_ctr;
586
587         devpriv->valid = 1;
588
589         dev_info(dev->class_dev, "%s attached, irq %sabled\n",
590                 dev->board_name, dev->irq ? "en" : "dis");
591
592         return 0;
593 }
594
595 static void icp_multi_detach(struct comedi_device *dev)
596 {
597         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
598         struct icp_multi_private *devpriv = dev->private;
599
600         if (devpriv)
601                 if (devpriv->valid)
602                         icp_multi_reset(dev);
603         if (dev->irq)
604                 free_irq(dev->irq, dev);
605         if (devpriv && devpriv->io_addr)
606                 iounmap(devpriv->io_addr);
607         if (pcidev) {
608                 if (dev->iobase)
609                         comedi_pci_disable(pcidev);
610         }
611 }
612
613 static struct comedi_driver icp_multi_driver = {
614         .driver_name    = "icp_multi",
615         .module         = THIS_MODULE,
616         .auto_attach    = icp_multi_auto_attach,
617         .detach         = icp_multi_detach,
618 };
619
620 static int icp_multi_pci_probe(struct pci_dev *dev,
621                                            const struct pci_device_id *ent)
622 {
623         return comedi_pci_auto_config(dev, &icp_multi_driver);
624 }
625
626 static DEFINE_PCI_DEVICE_TABLE(icp_multi_pci_table) = {
627         { PCI_DEVICE(PCI_VENDOR_ID_ICP, PCI_DEVICE_ID_ICP_MULTI) },
628         { 0 }
629 };
630 MODULE_DEVICE_TABLE(pci, icp_multi_pci_table);
631
632 static struct pci_driver icp_multi_pci_driver = {
633         .name           = "icp_multi",
634         .id_table       = icp_multi_pci_table,
635         .probe          = icp_multi_pci_probe,
636         .remove         = comedi_pci_auto_unconfig,
637 };
638 module_comedi_pci_driver(icp_multi_driver, icp_multi_pci_driver);
639
640 MODULE_AUTHOR("Comedi http://www.comedi.org");
641 MODULE_DESCRIPTION("Comedi low-level driver");
642 MODULE_LICENSE("GPL");