2 comedi/drivers/das16cs.c
3 Driver for Computer Boards PC-CARD DAS16/16.
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 2000, 2001, 2002 David A. Schleef <ds@schleef.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 Description: Computer Boards PC-CARD DAS16/16
26 Devices: [ComputerBoards] PC-CARD DAS16/16 (cb_das16_cs), PC-CARD DAS16/16-AO
28 Updated: Mon, 04 Nov 2002 20:04:21 -0800
34 #include <linux/interrupt.h>
35 #include "../comedidev.h"
36 #include <linux/delay.h>
37 #include <linux/pci.h>
39 #include <pcmcia/cs_types.h>
40 #include <pcmcia/cs.h>
41 #include <pcmcia/cistpl.h>
42 #include <pcmcia/ds.h>
46 #define DAS16CS_SIZE 18
48 #define DAS16CS_ADC_DATA 0
49 #define DAS16CS_DIO_MUX 2
50 #define DAS16CS_MISC1 4
51 #define DAS16CS_MISC2 6
52 #define DAS16CS_CTR0 8
53 #define DAS16CS_CTR1 10
54 #define DAS16CS_CTR2 12
55 #define DAS16CS_CTR_CONTROL 14
56 #define DAS16CS_DIO 16
58 struct das16cs_board {
63 static const struct das16cs_board das16cs_boards[] = {
65 .device_id = 0x0000, /* unknown */
66 .name = "PC-CARD DAS16/16",
71 .name = "PC-CARD DAS16/16-AO",
76 .name = "PCM-DAS16s/16",
81 #define n_boards ARRAY_SIZE(das16cs_boards)
82 #define thisboard ((const struct das16cs_board *)dev->board_ptr)
84 struct das16cs_private {
85 struct pcmcia_device *link;
87 unsigned int ao_readback[2];
88 unsigned short status1;
89 unsigned short status2;
91 #define devpriv ((struct das16cs_private *)dev->private)
93 static int das16cs_attach(struct comedi_device *dev,
94 struct comedi_devconfig *it);
95 static int das16cs_detach(struct comedi_device *dev);
96 static struct comedi_driver driver_das16cs = {
97 .driver_name = "cb_das16_cs",
98 .module = THIS_MODULE,
99 .attach = das16cs_attach,
100 .detach = das16cs_detach,
103 static struct pcmcia_device *cur_dev = NULL;
105 static const struct comedi_lrange das16cs_ai_range = { 4, {
113 static irqreturn_t das16cs_interrupt(int irq, void *d);
114 static int das16cs_ai_rinsn(struct comedi_device *dev,
115 struct comedi_subdevice *s,
116 struct comedi_insn *insn, unsigned int *data);
117 static int das16cs_ai_cmd(struct comedi_device *dev,
118 struct comedi_subdevice *s);
119 static int das16cs_ai_cmdtest(struct comedi_device *dev,
120 struct comedi_subdevice *s,
121 struct comedi_cmd *cmd);
122 static int das16cs_ao_winsn(struct comedi_device *dev,
123 struct comedi_subdevice *s,
124 struct comedi_insn *insn, unsigned int *data);
125 static int das16cs_ao_rinsn(struct comedi_device *dev,
126 struct comedi_subdevice *s,
127 struct comedi_insn *insn, unsigned int *data);
128 static int das16cs_dio_insn_bits(struct comedi_device *dev,
129 struct comedi_subdevice *s,
130 struct comedi_insn *insn, unsigned int *data);
131 static int das16cs_dio_insn_config(struct comedi_device *dev,
132 struct comedi_subdevice *s,
133 struct comedi_insn *insn,
135 static int das16cs_timer_insn_read(struct comedi_device *dev,
136 struct comedi_subdevice *s,
137 struct comedi_insn *insn,
139 static int das16cs_timer_insn_config(struct comedi_device *dev,
140 struct comedi_subdevice *s,
141 struct comedi_insn *insn,
144 static const struct das16cs_board *das16cs_probe(struct comedi_device *dev,
145 struct pcmcia_device *link)
149 for (i = 0; i < n_boards; i++) {
150 if (das16cs_boards[i].device_id == link->card_id)
151 return das16cs_boards + i;
154 printk("unknown board!\n");
159 static int das16cs_attach(struct comedi_device *dev,
160 struct comedi_devconfig *it)
162 struct pcmcia_device *link;
163 struct comedi_subdevice *s;
167 printk("comedi%d: cb_das16_cs: ", dev->minor);
169 link = cur_dev; /* XXX hack */
173 dev->iobase = link->io.BasePort1;
174 printk("I/O base=0x%04lx ", dev->iobase);
176 printk("fingerprint:\n");
177 for (i = 0; i < 48; i += 2) {
178 printk("%04x ", inw(dev->iobase + i));
182 ret = request_irq(link->irq.AssignedIRQ, das16cs_interrupt,
183 IRQF_SHARED, "cb_das16_cs", dev);
187 dev->irq = link->irq.AssignedIRQ;
188 printk("irq=%u ", dev->irq);
190 dev->board_ptr = das16cs_probe(dev, link);
194 dev->board_name = thisboard->name;
196 if (alloc_private(dev, sizeof(struct das16cs_private)) < 0)
199 if (alloc_subdevices(dev, 4) < 0)
202 s = dev->subdevices + 0;
203 dev->read_subdev = s;
204 /* analog input subdevice */
205 s->type = COMEDI_SUBD_AI;
206 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
209 s->range_table = &das16cs_ai_range;
210 s->len_chanlist = 16;
211 s->insn_read = das16cs_ai_rinsn;
212 s->do_cmd = das16cs_ai_cmd;
213 s->do_cmdtest = das16cs_ai_cmdtest;
215 s = dev->subdevices + 1;
216 /* analog output subdevice */
217 if (thisboard->n_ao_chans) {
218 s->type = COMEDI_SUBD_AO;
219 s->subdev_flags = SDF_WRITABLE;
220 s->n_chan = thisboard->n_ao_chans;
222 s->range_table = &range_bipolar10;
223 s->insn_write = &das16cs_ao_winsn;
224 s->insn_read = &das16cs_ao_rinsn;
227 s = dev->subdevices + 2;
228 /* digital i/o subdevice */
230 s->type = COMEDI_SUBD_DIO;
231 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
234 s->range_table = &range_digital;
235 s->insn_bits = das16cs_dio_insn_bits;
236 s->insn_config = das16cs_dio_insn_config;
238 s->type = COMEDI_SUBD_UNUSED;
241 s = dev->subdevices + 3;
242 /* timer subdevice */
244 s->type = COMEDI_SUBD_TIMER;
245 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
248 s->range_table = &range_unknown;
249 s->insn_read = das16cs_timer_insn_read;
250 s->insn_config = das16cs_timer_insn_config;
252 s->type = COMEDI_SUBD_UNUSED;
255 printk("attached\n");
260 static int das16cs_detach(struct comedi_device *dev)
262 printk("comedi%d: das16cs: remove\n", dev->minor);
265 free_irq(dev->irq, dev);
271 static irqreturn_t das16cs_interrupt(int irq, void *d)
273 /* struct comedi_device *dev = d; */
278 * "instructions" read/write data in "one-shot" or "software-triggered"
281 static int das16cs_ai_rinsn(struct comedi_device *dev,
282 struct comedi_subdevice *s,
283 struct comedi_insn *insn, unsigned int *data)
290 static int range_bits[] = { 0x800, 0x000, 0x100, 0x200 };
292 chan = CR_CHAN(insn->chanspec);
293 aref = CR_AREF(insn->chanspec);
294 range = CR_RANGE(insn->chanspec);
296 outw(chan, dev->iobase + 2);
298 devpriv->status1 &= ~0xf320;
299 devpriv->status1 |= (aref == AREF_DIFF) ? 0 : 0x0020;
300 outw(devpriv->status1, dev->iobase + 4);
302 devpriv->status2 &= ~0xff00;
303 devpriv->status2 |= range_bits[range];
304 outw(devpriv->status2, dev->iobase + 6);
306 for (i = 0; i < insn->n; i++) {
307 outw(0, dev->iobase);
310 for (to = 0; to < TIMEOUT; to++) {
311 if (inw(dev->iobase + 4) & 0x0080)
315 printk("cb_das16_cs: ai timeout\n");
318 data[i] = (unsigned short)inw(dev->iobase + 0);
324 static int das16cs_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
329 static int das16cs_ai_cmdtest(struct comedi_device *dev,
330 struct comedi_subdevice *s,
331 struct comedi_cmd *cmd)
336 /* cmdtest tests a particular command to see if it is valid.
337 * Using the cmdtest ioctl, a user can create a valid cmd
338 * and then have it executes by the cmd ioctl.
340 * cmdtest returns 1,2,3,4 or 0, depending on which tests
341 * the command passes. */
343 /* step 1: make sure trigger sources are trivially valid */
345 tmp = cmd->start_src;
346 cmd->start_src &= TRIG_NOW;
347 if (!cmd->start_src || tmp != cmd->start_src)
350 tmp = cmd->scan_begin_src;
351 cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
352 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
355 tmp = cmd->convert_src;
356 cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
357 if (!cmd->convert_src || tmp != cmd->convert_src)
360 tmp = cmd->scan_end_src;
361 cmd->scan_end_src &= TRIG_COUNT;
362 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
366 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
367 if (!cmd->stop_src || tmp != cmd->stop_src)
373 /* step 2: make sure trigger sources are unique and mutually compatible */
375 /* note that mutual compatibility is not an issue here */
376 if (cmd->scan_begin_src != TRIG_TIMER &&
377 cmd->scan_begin_src != TRIG_EXT)
379 if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
381 if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
387 /* step 3: make sure arguments are trivially compatible */
389 if (cmd->start_arg != 0) {
393 #define MAX_SPEED 10000 /* in nanoseconds */
394 #define MIN_SPEED 1000000000 /* in nanoseconds */
396 if (cmd->scan_begin_src == TRIG_TIMER) {
397 if (cmd->scan_begin_arg < MAX_SPEED) {
398 cmd->scan_begin_arg = MAX_SPEED;
401 if (cmd->scan_begin_arg > MIN_SPEED) {
402 cmd->scan_begin_arg = MIN_SPEED;
406 /* external trigger */
407 /* should be level/edge, hi/lo specification here */
408 /* should specify multiple external triggers */
409 if (cmd->scan_begin_arg > 9) {
410 cmd->scan_begin_arg = 9;
414 if (cmd->convert_src == TRIG_TIMER) {
415 if (cmd->convert_arg < MAX_SPEED) {
416 cmd->convert_arg = MAX_SPEED;
419 if (cmd->convert_arg > MIN_SPEED) {
420 cmd->convert_arg = MIN_SPEED;
424 /* external trigger */
426 if (cmd->convert_arg > 9) {
427 cmd->convert_arg = 9;
432 if (cmd->scan_end_arg != cmd->chanlist_len) {
433 cmd->scan_end_arg = cmd->chanlist_len;
436 if (cmd->stop_src == TRIG_COUNT) {
437 if (cmd->stop_arg > 0x00ffffff) {
438 cmd->stop_arg = 0x00ffffff;
443 if (cmd->stop_arg != 0) {
452 /* step 4: fix up any arguments */
454 if (cmd->scan_begin_src == TRIG_TIMER) {
455 unsigned int div1 = 0, div2 = 0;
457 tmp = cmd->scan_begin_arg;
458 i8253_cascade_ns_to_timer(100, &div1, &div2,
459 &cmd->scan_begin_arg,
460 cmd->flags & TRIG_ROUND_MASK);
461 if (tmp != cmd->scan_begin_arg)
464 if (cmd->convert_src == TRIG_TIMER) {
465 unsigned int div1 = 0, div2 = 0;
467 tmp = cmd->convert_arg;
468 i8253_cascade_ns_to_timer(100, &div1, &div2,
469 &cmd->scan_begin_arg,
470 cmd->flags & TRIG_ROUND_MASK);
471 if (tmp != cmd->convert_arg)
473 if (cmd->scan_begin_src == TRIG_TIMER &&
474 cmd->scan_begin_arg <
475 cmd->convert_arg * cmd->scan_end_arg) {
476 cmd->scan_begin_arg =
477 cmd->convert_arg * cmd->scan_end_arg;
488 static int das16cs_ao_winsn(struct comedi_device *dev,
489 struct comedi_subdevice *s,
490 struct comedi_insn *insn, unsigned int *data)
493 int chan = CR_CHAN(insn->chanspec);
494 unsigned short status1;
498 for (i = 0; i < insn->n; i++) {
499 devpriv->ao_readback[chan] = data[i];
502 outw(devpriv->status1, dev->iobase + 4);
505 status1 = devpriv->status1 & ~0xf;
511 /* printk("0x%04x\n",status1);*/
512 outw(status1, dev->iobase + 4);
515 for (bit = 15; bit >= 0; bit--) {
516 int b = (d >> bit) & 0x1;
518 /* printk("0x%04x\n",status1 | b | 0x0000);*/
519 outw(status1 | b | 0x0000, dev->iobase + 4);
521 /* printk("0x%04x\n",status1 | b | 0x0004);*/
522 outw(status1 | b | 0x0004, dev->iobase + 4);
525 /* make high both DAC0CS and DAC1CS to load
526 new data and update analog output*/
527 outw(status1 | 0x9, dev->iobase + 4);
533 /* AO subdevices should have a read insn as well as a write insn.
534 * Usually this means copying a value stored in devpriv. */
535 static int das16cs_ao_rinsn(struct comedi_device *dev,
536 struct comedi_subdevice *s,
537 struct comedi_insn *insn, unsigned int *data)
540 int chan = CR_CHAN(insn->chanspec);
542 for (i = 0; i < insn->n; i++)
543 data[i] = devpriv->ao_readback[chan];
548 /* DIO devices are slightly special. Although it is possible to
549 * implement the insn_read/insn_write interface, it is much more
550 * useful to applications if you implement the insn_bits interface.
551 * This allows packed reading/writing of the DIO channels. The
552 * comedi core can convert between insn_bits and insn_read/write */
553 static int das16cs_dio_insn_bits(struct comedi_device *dev,
554 struct comedi_subdevice *s,
555 struct comedi_insn *insn, unsigned int *data)
561 s->state &= ~data[0];
562 s->state |= data[0] & data[1];
564 outw(s->state, dev->iobase + 16);
567 /* on return, data[1] contains the value of the digital
568 * input and output lines. */
569 data[1] = inw(dev->iobase + 16);
574 static int das16cs_dio_insn_config(struct comedi_device *dev,
575 struct comedi_subdevice *s,
576 struct comedi_insn *insn, unsigned int *data)
578 int chan = CR_CHAN(insn->chanspec);
587 case INSN_CONFIG_DIO_OUTPUT:
590 case INSN_CONFIG_DIO_INPUT:
593 case INSN_CONFIG_DIO_QUERY:
595 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
603 devpriv->status2 &= ~0x00c0;
604 devpriv->status2 |= (s->io_bits & 0xf0) ? 0x0080 : 0;
605 devpriv->status2 |= (s->io_bits & 0x0f) ? 0x0040 : 0;
607 outw(devpriv->status2, dev->iobase + 6);
612 static int das16cs_timer_insn_read(struct comedi_device *dev,
613 struct comedi_subdevice *s,
614 struct comedi_insn *insn, unsigned int *data)
619 static int das16cs_timer_insn_config(struct comedi_device *dev,
620 struct comedi_subdevice *s,
621 struct comedi_insn *insn,
629 /*======================================================================
631 The following pcmcia code for the pcm-das08 is adapted from the
632 dummy_cs.c driver of the Linux PCMCIA Card Services package.
634 The initial developer of the original code is David A. Hinds
635 <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
636 are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
638 ======================================================================*/
640 #if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE)
642 static void das16cs_pcmcia_config(struct pcmcia_device *link);
643 static void das16cs_pcmcia_release(struct pcmcia_device *link);
644 static int das16cs_pcmcia_suspend(struct pcmcia_device *p_dev);
645 static int das16cs_pcmcia_resume(struct pcmcia_device *p_dev);
648 The attach() and detach() entry points are used to create and destroy
649 "instances" of the driver, where each instance represents everything
650 needed to manage one actual PCMCIA card.
653 static int das16cs_pcmcia_attach(struct pcmcia_device *);
654 static void das16cs_pcmcia_detach(struct pcmcia_device *);
657 You'll also need to prototype all the functions that will actually
658 be used to talk to your device. See 'memory_cs' for a good example
659 of a fully self-sufficient driver; the other drivers rely more or
660 less on other parts of the kernel.
664 The dev_info variable is the "key" that is used to match up this
665 device driver with appropriate cards, through the card configuration
669 static dev_info_t dev_info = "cb_das16_cs";
671 struct local_info_t {
672 struct pcmcia_device *link;
675 struct bus_operations *bus;
678 /*======================================================================
680 das16cs_pcmcia_attach() creates an "instance" of the driver, allocating
681 local data structures for one device. The device is registered
684 The dev_link structure is initialized, but we don't actually
685 configure the card at this point -- we wait until we receive a
686 card insertion event.
688 ======================================================================*/
690 static int das16cs_pcmcia_attach(struct pcmcia_device *link)
692 struct local_info_t *local;
694 dev_dbg(&link->dev, "das16cs_pcmcia_attach()\n");
696 /* Allocate space for private device-specific data */
697 local = kzalloc(sizeof(struct local_info_t), GFP_KERNEL);
703 /* Initialize the pcmcia_device structure */
704 /* Interrupt setup */
705 link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
706 link->irq.Handler = NULL;
708 link->conf.Attributes = 0;
709 link->conf.IntType = INT_MEMORY_AND_IO;
713 das16cs_pcmcia_config(link);
716 } /* das16cs_pcmcia_attach */
718 static void das16cs_pcmcia_detach(struct pcmcia_device *link)
720 dev_dbg(&link->dev, "das16cs_pcmcia_detach\n");
722 if (link->dev_node) {
723 ((struct local_info_t *)link->priv)->stop = 1;
724 das16cs_pcmcia_release(link);
726 /* This points to the parent struct local_info_t struct */
729 } /* das16cs_pcmcia_detach */
732 static int das16cs_pcmcia_config_loop(struct pcmcia_device *p_dev,
733 cistpl_cftable_entry_t *cfg,
734 cistpl_cftable_entry_t *dflt,
741 /* Do we need to allocate an interrupt? */
742 if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1)
743 p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
745 /* IO window settings */
746 p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
747 if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
748 cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
749 p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
750 if (!(io->flags & CISTPL_IO_8BIT))
751 p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
752 if (!(io->flags & CISTPL_IO_16BIT))
753 p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
754 p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
755 p_dev->io.BasePort1 = io->win[0].base;
756 p_dev->io.NumPorts1 = io->win[0].len;
758 p_dev->io.Attributes2 = p_dev->io.Attributes1;
759 p_dev->io.BasePort2 = io->win[1].base;
760 p_dev->io.NumPorts2 = io->win[1].len;
762 /* This reserves IO space but doesn't actually enable it */
763 return pcmcia_request_io(p_dev, &p_dev->io);
769 static void das16cs_pcmcia_config(struct pcmcia_device *link)
771 struct local_info_t *dev = link->priv;
774 dev_dbg(&link->dev, "das16cs_pcmcia_config\n");
776 ret = pcmcia_loop_config(link, das16cs_pcmcia_config_loop, NULL);
778 dev_warn(&link->dev, "no configuration found\n");
783 Allocate an interrupt line. Note that this does not assign a
784 handler to the interrupt, unless the 'Handler' member of the
785 irq structure is initialized.
787 if (link->conf.Attributes & CONF_ENABLE_IRQ) {
788 ret = pcmcia_request_irq(link, &link->irq);
793 This actually configures the PCMCIA socket -- setting up
794 the I/O windows and the interrupt mapping, and putting the
795 card and host interface into "Memory and IO" mode.
797 ret = pcmcia_request_configuration(link, &link->conf);
802 At this point, the dev_node_t structure(s) need to be
803 initialized and arranged in a linked list at link->dev.
805 sprintf(dev->node.dev_name, "cb_das16_cs");
806 dev->node.major = dev->node.minor = 0;
807 link->dev_node = &dev->node;
809 /* Finally, report what we've done */
810 printk(KERN_INFO "%s: index 0x%02x",
811 dev->node.dev_name, link->conf.ConfigIndex);
812 if (link->conf.Attributes & CONF_ENABLE_IRQ)
813 printk(", irq %u", link->irq.AssignedIRQ);
814 if (link->io.NumPorts1)
815 printk(", io 0x%04x-0x%04x", link->io.BasePort1,
816 link->io.BasePort1 + link->io.NumPorts1 - 1);
817 if (link->io.NumPorts2)
818 printk(" & 0x%04x-0x%04x", link->io.BasePort2,
819 link->io.BasePort2 + link->io.NumPorts2 - 1);
825 das16cs_pcmcia_release(link);
826 } /* das16cs_pcmcia_config */
828 static void das16cs_pcmcia_release(struct pcmcia_device *link)
830 dev_dbg(&link->dev, "das16cs_pcmcia_release\n");
831 pcmcia_disable_device(link);
832 } /* das16cs_pcmcia_release */
834 static int das16cs_pcmcia_suspend(struct pcmcia_device *link)
836 struct local_info_t *local = link->priv;
838 /* Mark the device as stopped, to block IO until later */
842 } /* das16cs_pcmcia_suspend */
844 static int das16cs_pcmcia_resume(struct pcmcia_device *link)
846 struct local_info_t *local = link->priv;
850 } /* das16cs_pcmcia_resume */
852 /*====================================================================*/
854 static struct pcmcia_device_id das16cs_id_table[] = {
855 PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0039),
856 PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4009),
860 MODULE_DEVICE_TABLE(pcmcia, das16cs_id_table);
862 struct pcmcia_driver das16cs_driver = {
863 .probe = das16cs_pcmcia_attach,
864 .remove = das16cs_pcmcia_detach,
865 .suspend = das16cs_pcmcia_suspend,
866 .resume = das16cs_pcmcia_resume,
867 .id_table = das16cs_id_table,
868 .owner = THIS_MODULE,
874 static int __init init_das16cs_pcmcia_cs(void)
876 pcmcia_register_driver(&das16cs_driver);
880 static void __exit exit_das16cs_pcmcia_cs(void)
882 pr_debug("das16cs_pcmcia_cs: unloading\n");
883 pcmcia_unregister_driver(&das16cs_driver);
886 int __init init_module(void)
890 ret = init_das16cs_pcmcia_cs();
894 return comedi_driver_register(&driver_das16cs);
897 void __exit cleanup_module(void)
899 exit_das16cs_pcmcia_cs();
900 comedi_driver_unregister(&driver_das16cs);
904 COMEDI_INITCLEANUP(driver_das16cs);
905 #endif /* CONFIG_PCMCIA */