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 <linux/slab.h>
36 #include "../comedidev.h"
37 #include <linux/delay.h>
38 #include <linux/pci.h>
40 #include <pcmcia/cs_types.h>
41 #include <pcmcia/cs.h>
42 #include <pcmcia/cistpl.h>
43 #include <pcmcia/ds.h>
47 #define DAS16CS_SIZE 18
49 #define DAS16CS_ADC_DATA 0
50 #define DAS16CS_DIO_MUX 2
51 #define DAS16CS_MISC1 4
52 #define DAS16CS_MISC2 6
53 #define DAS16CS_CTR0 8
54 #define DAS16CS_CTR1 10
55 #define DAS16CS_CTR2 12
56 #define DAS16CS_CTR_CONTROL 14
57 #define DAS16CS_DIO 16
59 struct das16cs_board {
64 static const struct das16cs_board das16cs_boards[] = {
66 .device_id = 0x0000, /* unknown */
67 .name = "PC-CARD DAS16/16",
72 .name = "PC-CARD DAS16/16-AO",
77 .name = "PCM-DAS16s/16",
82 #define n_boards ARRAY_SIZE(das16cs_boards)
83 #define thisboard ((const struct das16cs_board *)dev->board_ptr)
85 struct das16cs_private {
86 struct pcmcia_device *link;
88 unsigned int ao_readback[2];
89 unsigned short status1;
90 unsigned short status2;
92 #define devpriv ((struct das16cs_private *)dev->private)
94 static int das16cs_attach(struct comedi_device *dev,
95 struct comedi_devconfig *it);
96 static int das16cs_detach(struct comedi_device *dev);
97 static struct comedi_driver driver_das16cs = {
98 .driver_name = "cb_das16_cs",
99 .module = THIS_MODULE,
100 .attach = das16cs_attach,
101 .detach = das16cs_detach,
104 static struct pcmcia_device *cur_dev = NULL;
106 static const struct comedi_lrange das16cs_ai_range = { 4, {
114 static irqreturn_t das16cs_interrupt(int irq, void *d);
115 static int das16cs_ai_rinsn(struct comedi_device *dev,
116 struct comedi_subdevice *s,
117 struct comedi_insn *insn, unsigned int *data);
118 static int das16cs_ai_cmd(struct comedi_device *dev,
119 struct comedi_subdevice *s);
120 static int das16cs_ai_cmdtest(struct comedi_device *dev,
121 struct comedi_subdevice *s,
122 struct comedi_cmd *cmd);
123 static int das16cs_ao_winsn(struct comedi_device *dev,
124 struct comedi_subdevice *s,
125 struct comedi_insn *insn, unsigned int *data);
126 static int das16cs_ao_rinsn(struct comedi_device *dev,
127 struct comedi_subdevice *s,
128 struct comedi_insn *insn, unsigned int *data);
129 static int das16cs_dio_insn_bits(struct comedi_device *dev,
130 struct comedi_subdevice *s,
131 struct comedi_insn *insn, unsigned int *data);
132 static int das16cs_dio_insn_config(struct comedi_device *dev,
133 struct comedi_subdevice *s,
134 struct comedi_insn *insn,
136 static int das16cs_timer_insn_read(struct comedi_device *dev,
137 struct comedi_subdevice *s,
138 struct comedi_insn *insn,
140 static int das16cs_timer_insn_config(struct comedi_device *dev,
141 struct comedi_subdevice *s,
142 struct comedi_insn *insn,
145 static const struct das16cs_board *das16cs_probe(struct comedi_device *dev,
146 struct pcmcia_device *link)
150 for (i = 0; i < n_boards; i++) {
151 if (das16cs_boards[i].device_id == link->card_id)
152 return das16cs_boards + i;
155 printk("unknown board!\n");
160 static int das16cs_attach(struct comedi_device *dev,
161 struct comedi_devconfig *it)
163 struct pcmcia_device *link;
164 struct comedi_subdevice *s;
168 printk("comedi%d: cb_das16_cs: ", dev->minor);
170 link = cur_dev; /* XXX hack */
174 dev->iobase = link->io.BasePort1;
175 printk("I/O base=0x%04lx ", dev->iobase);
177 printk("fingerprint:\n");
178 for (i = 0; i < 48; i += 2)
179 printk("%04x ", inw(dev->iobase + i));
183 ret = request_irq(link->irq, das16cs_interrupt,
184 IRQF_SHARED, "cb_das16_cs", dev);
188 dev->irq = link->irq;
190 printk("irq=%u ", dev->irq);
192 dev->board_ptr = das16cs_probe(dev, link);
196 dev->board_name = thisboard->name;
198 if (alloc_private(dev, sizeof(struct das16cs_private)) < 0)
201 if (alloc_subdevices(dev, 4) < 0)
204 s = dev->subdevices + 0;
205 dev->read_subdev = s;
206 /* analog input subdevice */
207 s->type = COMEDI_SUBD_AI;
208 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
211 s->range_table = &das16cs_ai_range;
212 s->len_chanlist = 16;
213 s->insn_read = das16cs_ai_rinsn;
214 s->do_cmd = das16cs_ai_cmd;
215 s->do_cmdtest = das16cs_ai_cmdtest;
217 s = dev->subdevices + 1;
218 /* analog output subdevice */
219 if (thisboard->n_ao_chans) {
220 s->type = COMEDI_SUBD_AO;
221 s->subdev_flags = SDF_WRITABLE;
222 s->n_chan = thisboard->n_ao_chans;
224 s->range_table = &range_bipolar10;
225 s->insn_write = &das16cs_ao_winsn;
226 s->insn_read = &das16cs_ao_rinsn;
229 s = dev->subdevices + 2;
230 /* digital i/o subdevice */
232 s->type = COMEDI_SUBD_DIO;
233 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
236 s->range_table = &range_digital;
237 s->insn_bits = das16cs_dio_insn_bits;
238 s->insn_config = das16cs_dio_insn_config;
240 s->type = COMEDI_SUBD_UNUSED;
243 s = dev->subdevices + 3;
244 /* timer subdevice */
246 s->type = COMEDI_SUBD_TIMER;
247 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
250 s->range_table = &range_unknown;
251 s->insn_read = das16cs_timer_insn_read;
252 s->insn_config = das16cs_timer_insn_config;
254 s->type = COMEDI_SUBD_UNUSED;
257 printk("attached\n");
262 static int das16cs_detach(struct comedi_device *dev)
264 printk("comedi%d: das16cs: remove\n", dev->minor);
267 free_irq(dev->irq, dev);
273 static irqreturn_t das16cs_interrupt(int irq, void *d)
275 /* struct comedi_device *dev = d; */
280 * "instructions" read/write data in "one-shot" or "software-triggered"
283 static int das16cs_ai_rinsn(struct comedi_device *dev,
284 struct comedi_subdevice *s,
285 struct comedi_insn *insn, unsigned int *data)
292 static int range_bits[] = { 0x800, 0x000, 0x100, 0x200 };
294 chan = CR_CHAN(insn->chanspec);
295 aref = CR_AREF(insn->chanspec);
296 range = CR_RANGE(insn->chanspec);
298 outw(chan, dev->iobase + 2);
300 devpriv->status1 &= ~0xf320;
301 devpriv->status1 |= (aref == AREF_DIFF) ? 0 : 0x0020;
302 outw(devpriv->status1, dev->iobase + 4);
304 devpriv->status2 &= ~0xff00;
305 devpriv->status2 |= range_bits[range];
306 outw(devpriv->status2, dev->iobase + 6);
308 for (i = 0; i < insn->n; i++) {
309 outw(0, dev->iobase);
312 for (to = 0; to < TIMEOUT; to++) {
313 if (inw(dev->iobase + 4) & 0x0080)
317 printk("cb_das16_cs: ai timeout\n");
320 data[i] = (unsigned short)inw(dev->iobase + 0);
326 static int das16cs_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
331 static int das16cs_ai_cmdtest(struct comedi_device *dev,
332 struct comedi_subdevice *s,
333 struct comedi_cmd *cmd)
338 /* cmdtest tests a particular command to see if it is valid.
339 * Using the cmdtest ioctl, a user can create a valid cmd
340 * and then have it executes by the cmd ioctl.
342 * cmdtest returns 1,2,3,4 or 0, depending on which tests
343 * the command passes. */
345 /* step 1: make sure trigger sources are trivially valid */
347 tmp = cmd->start_src;
348 cmd->start_src &= TRIG_NOW;
349 if (!cmd->start_src || tmp != cmd->start_src)
352 tmp = cmd->scan_begin_src;
353 cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
354 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
357 tmp = cmd->convert_src;
358 cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
359 if (!cmd->convert_src || tmp != cmd->convert_src)
362 tmp = cmd->scan_end_src;
363 cmd->scan_end_src &= TRIG_COUNT;
364 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
368 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
369 if (!cmd->stop_src || tmp != cmd->stop_src)
375 /* step 2: make sure trigger sources are unique and mutually compatible */
377 /* note that mutual compatibility is not an issue here */
378 if (cmd->scan_begin_src != TRIG_TIMER &&
379 cmd->scan_begin_src != TRIG_EXT)
381 if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
383 if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
389 /* step 3: make sure arguments are trivially compatible */
391 if (cmd->start_arg != 0) {
395 #define MAX_SPEED 10000 /* in nanoseconds */
396 #define MIN_SPEED 1000000000 /* in nanoseconds */
398 if (cmd->scan_begin_src == TRIG_TIMER) {
399 if (cmd->scan_begin_arg < MAX_SPEED) {
400 cmd->scan_begin_arg = MAX_SPEED;
403 if (cmd->scan_begin_arg > MIN_SPEED) {
404 cmd->scan_begin_arg = MIN_SPEED;
408 /* external trigger */
409 /* should be level/edge, hi/lo specification here */
410 /* should specify multiple external triggers */
411 if (cmd->scan_begin_arg > 9) {
412 cmd->scan_begin_arg = 9;
416 if (cmd->convert_src == TRIG_TIMER) {
417 if (cmd->convert_arg < MAX_SPEED) {
418 cmd->convert_arg = MAX_SPEED;
421 if (cmd->convert_arg > MIN_SPEED) {
422 cmd->convert_arg = MIN_SPEED;
426 /* external trigger */
428 if (cmd->convert_arg > 9) {
429 cmd->convert_arg = 9;
434 if (cmd->scan_end_arg != cmd->chanlist_len) {
435 cmd->scan_end_arg = cmd->chanlist_len;
438 if (cmd->stop_src == TRIG_COUNT) {
439 if (cmd->stop_arg > 0x00ffffff) {
440 cmd->stop_arg = 0x00ffffff;
445 if (cmd->stop_arg != 0) {
454 /* step 4: fix up any arguments */
456 if (cmd->scan_begin_src == TRIG_TIMER) {
457 unsigned int div1 = 0, div2 = 0;
459 tmp = cmd->scan_begin_arg;
460 i8253_cascade_ns_to_timer(100, &div1, &div2,
461 &cmd->scan_begin_arg,
462 cmd->flags & TRIG_ROUND_MASK);
463 if (tmp != cmd->scan_begin_arg)
466 if (cmd->convert_src == TRIG_TIMER) {
467 unsigned int div1 = 0, div2 = 0;
469 tmp = cmd->convert_arg;
470 i8253_cascade_ns_to_timer(100, &div1, &div2,
471 &cmd->scan_begin_arg,
472 cmd->flags & TRIG_ROUND_MASK);
473 if (tmp != cmd->convert_arg)
475 if (cmd->scan_begin_src == TRIG_TIMER &&
476 cmd->scan_begin_arg <
477 cmd->convert_arg * cmd->scan_end_arg) {
478 cmd->scan_begin_arg =
479 cmd->convert_arg * cmd->scan_end_arg;
490 static int das16cs_ao_winsn(struct comedi_device *dev,
491 struct comedi_subdevice *s,
492 struct comedi_insn *insn, unsigned int *data)
495 int chan = CR_CHAN(insn->chanspec);
496 unsigned short status1;
500 for (i = 0; i < insn->n; i++) {
501 devpriv->ao_readback[chan] = data[i];
504 outw(devpriv->status1, dev->iobase + 4);
507 status1 = devpriv->status1 & ~0xf;
513 /* printk("0x%04x\n",status1);*/
514 outw(status1, dev->iobase + 4);
517 for (bit = 15; bit >= 0; bit--) {
518 int b = (d >> bit) & 0x1;
520 /* printk("0x%04x\n",status1 | b | 0x0000);*/
521 outw(status1 | b | 0x0000, dev->iobase + 4);
523 /* printk("0x%04x\n",status1 | b | 0x0004);*/
524 outw(status1 | b | 0x0004, dev->iobase + 4);
527 /* make high both DAC0CS and DAC1CS to load
528 new data and update analog output*/
529 outw(status1 | 0x9, dev->iobase + 4);
535 /* AO subdevices should have a read insn as well as a write insn.
536 * Usually this means copying a value stored in devpriv. */
537 static int das16cs_ao_rinsn(struct comedi_device *dev,
538 struct comedi_subdevice *s,
539 struct comedi_insn *insn, unsigned int *data)
542 int chan = CR_CHAN(insn->chanspec);
544 for (i = 0; i < insn->n; i++)
545 data[i] = devpriv->ao_readback[chan];
550 /* DIO devices are slightly special. Although it is possible to
551 * implement the insn_read/insn_write interface, it is much more
552 * useful to applications if you implement the insn_bits interface.
553 * This allows packed reading/writing of the DIO channels. The
554 * comedi core can convert between insn_bits and insn_read/write */
555 static int das16cs_dio_insn_bits(struct comedi_device *dev,
556 struct comedi_subdevice *s,
557 struct comedi_insn *insn, unsigned int *data)
563 s->state &= ~data[0];
564 s->state |= data[0] & data[1];
566 outw(s->state, dev->iobase + 16);
569 /* on return, data[1] contains the value of the digital
570 * input and output lines. */
571 data[1] = inw(dev->iobase + 16);
576 static int das16cs_dio_insn_config(struct comedi_device *dev,
577 struct comedi_subdevice *s,
578 struct comedi_insn *insn, unsigned int *data)
580 int chan = CR_CHAN(insn->chanspec);
589 case INSN_CONFIG_DIO_OUTPUT:
592 case INSN_CONFIG_DIO_INPUT:
595 case INSN_CONFIG_DIO_QUERY:
597 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
605 devpriv->status2 &= ~0x00c0;
606 devpriv->status2 |= (s->io_bits & 0xf0) ? 0x0080 : 0;
607 devpriv->status2 |= (s->io_bits & 0x0f) ? 0x0040 : 0;
609 outw(devpriv->status2, dev->iobase + 6);
614 static int das16cs_timer_insn_read(struct comedi_device *dev,
615 struct comedi_subdevice *s,
616 struct comedi_insn *insn, unsigned int *data)
621 static int das16cs_timer_insn_config(struct comedi_device *dev,
622 struct comedi_subdevice *s,
623 struct comedi_insn *insn,
631 /*======================================================================
633 The following pcmcia code for the pcm-das08 is adapted from the
634 dummy_cs.c driver of the Linux PCMCIA Card Services package.
636 The initial developer of the original code is David A. Hinds
637 <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
638 are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
640 ======================================================================*/
642 #if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE)
644 static void das16cs_pcmcia_config(struct pcmcia_device *link);
645 static void das16cs_pcmcia_release(struct pcmcia_device *link);
646 static int das16cs_pcmcia_suspend(struct pcmcia_device *p_dev);
647 static int das16cs_pcmcia_resume(struct pcmcia_device *p_dev);
650 The attach() and detach() entry points are used to create and destroy
651 "instances" of the driver, where each instance represents everything
652 needed to manage one actual PCMCIA card.
655 static int das16cs_pcmcia_attach(struct pcmcia_device *);
656 static void das16cs_pcmcia_detach(struct pcmcia_device *);
659 You'll also need to prototype all the functions that will actually
660 be used to talk to your device. See 'memory_cs' for a good example
661 of a fully self-sufficient driver; the other drivers rely more or
662 less on other parts of the kernel.
666 The dev_info variable is the "key" that is used to match up this
667 device driver with appropriate cards, through the card configuration
671 static dev_info_t dev_info = "cb_das16_cs";
673 struct local_info_t {
674 struct pcmcia_device *link;
676 struct bus_operations *bus;
679 /*======================================================================
681 das16cs_pcmcia_attach() creates an "instance" of the driver, allocating
682 local data structures for one device. The device is registered
685 The dev_link structure is initialized, but we don't actually
686 configure the card at this point -- we wait until we receive a
687 card insertion event.
689 ======================================================================*/
691 static int das16cs_pcmcia_attach(struct pcmcia_device *link)
693 struct local_info_t *local;
695 dev_dbg(&link->dev, "das16cs_pcmcia_attach()\n");
697 /* Allocate space for private device-specific data */
698 local = kzalloc(sizeof(struct local_info_t), GFP_KERNEL);
704 /* Initialize the pcmcia_device structure */
705 link->conf.Attributes = 0;
706 link->conf.IntType = INT_MEMORY_AND_IO;
710 das16cs_pcmcia_config(link);
713 } /* das16cs_pcmcia_attach */
715 static void das16cs_pcmcia_detach(struct pcmcia_device *link)
717 dev_dbg(&link->dev, "das16cs_pcmcia_detach\n");
719 ((struct local_info_t *)link->priv)->stop = 1;
720 das16cs_pcmcia_release(link);
721 /* This points to the parent struct local_info_t struct */
723 } /* das16cs_pcmcia_detach */
726 static int das16cs_pcmcia_config_loop(struct pcmcia_device *p_dev,
727 cistpl_cftable_entry_t *cfg,
728 cistpl_cftable_entry_t *dflt,
735 /* Do we need to allocate an interrupt? */
736 p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
738 /* IO window settings */
739 p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
740 if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
741 cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
742 p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
743 if (!(io->flags & CISTPL_IO_8BIT))
744 p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
745 if (!(io->flags & CISTPL_IO_16BIT))
746 p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
747 p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
748 p_dev->io.BasePort1 = io->win[0].base;
749 p_dev->io.NumPorts1 = io->win[0].len;
751 p_dev->io.Attributes2 = p_dev->io.Attributes1;
752 p_dev->io.BasePort2 = io->win[1].base;
753 p_dev->io.NumPorts2 = io->win[1].len;
755 /* This reserves IO space but doesn't actually enable it */
756 return pcmcia_request_io(p_dev, &p_dev->io);
762 static void das16cs_pcmcia_config(struct pcmcia_device *link)
766 dev_dbg(&link->dev, "das16cs_pcmcia_config\n");
768 ret = pcmcia_loop_config(link, das16cs_pcmcia_config_loop, NULL);
770 dev_warn(&link->dev, "no configuration found\n");
778 This actually configures the PCMCIA socket -- setting up
779 the I/O windows and the interrupt mapping, and putting the
780 card and host interface into "Memory and IO" mode.
782 ret = pcmcia_request_configuration(link, &link->conf);
786 /* Finally, report what we've done */
787 dev_info(&link->dev, "index 0x%02x", link->conf.ConfigIndex);
788 if (link->conf.Attributes & CONF_ENABLE_IRQ)
789 printk(", irq %u", link->irq);
790 if (link->io.NumPorts1)
791 printk(", io 0x%04x-0x%04x", link->io.BasePort1,
792 link->io.BasePort1 + link->io.NumPorts1 - 1);
793 if (link->io.NumPorts2)
794 printk(" & 0x%04x-0x%04x", link->io.BasePort2,
795 link->io.BasePort2 + link->io.NumPorts2 - 1);
801 das16cs_pcmcia_release(link);
802 } /* das16cs_pcmcia_config */
804 static void das16cs_pcmcia_release(struct pcmcia_device *link)
806 dev_dbg(&link->dev, "das16cs_pcmcia_release\n");
807 pcmcia_disable_device(link);
808 } /* das16cs_pcmcia_release */
810 static int das16cs_pcmcia_suspend(struct pcmcia_device *link)
812 struct local_info_t *local = link->priv;
814 /* Mark the device as stopped, to block IO until later */
818 } /* das16cs_pcmcia_suspend */
820 static int das16cs_pcmcia_resume(struct pcmcia_device *link)
822 struct local_info_t *local = link->priv;
826 } /* das16cs_pcmcia_resume */
828 /*====================================================================*/
830 static struct pcmcia_device_id das16cs_id_table[] = {
831 PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0039),
832 PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4009),
836 MODULE_DEVICE_TABLE(pcmcia, das16cs_id_table);
837 MODULE_AUTHOR("David A. Schleef <ds@schleef.org>");
838 MODULE_DESCRIPTION("Comedi driver for Computer Boards PC-CARD DAS16/16");
839 MODULE_LICENSE("GPL");
841 struct pcmcia_driver das16cs_driver = {
842 .probe = das16cs_pcmcia_attach,
843 .remove = das16cs_pcmcia_detach,
844 .suspend = das16cs_pcmcia_suspend,
845 .resume = das16cs_pcmcia_resume,
846 .id_table = das16cs_id_table,
847 .owner = THIS_MODULE,
853 static int __init init_das16cs_pcmcia_cs(void)
855 pcmcia_register_driver(&das16cs_driver);
859 static void __exit exit_das16cs_pcmcia_cs(void)
861 pr_debug("das16cs_pcmcia_cs: unloading\n");
862 pcmcia_unregister_driver(&das16cs_driver);
865 int __init init_module(void)
869 ret = init_das16cs_pcmcia_cs();
873 return comedi_driver_register(&driver_das16cs);
876 void __exit cleanup_module(void)
878 exit_das16cs_pcmcia_cs();
879 comedi_driver_unregister(&driver_das16cs);
883 static int __init driver_das16cs_init_module(void)
885 return comedi_driver_register(&driver_das16cs);
888 static void __exit driver_das16cs_cleanup_module(void)
890 comedi_driver_unregister(&driver_das16cs);
893 module_init(driver_das16cs_init_module);
894 module_exit(driver_das16cs_cleanup_module);
895 #endif /* CONFIG_PCMCIA */