]> Pileus Git - ~andy/linux/blob - drivers/staging/comedi/drivers/cb_das16_cs.c
2063215369cea26180d81dc4f485fb325dd3fb8a
[~andy/linux] / drivers / staging / comedi / drivers / cb_das16_cs.c
1 /*
2     comedi/drivers/das16cs.c
3     Driver for Computer Boards PC-CARD DAS16/16.
4
5     COMEDI - Linux Control and Measurement Device Interface
6     Copyright (C) 2000, 2001, 2002 David A. Schleef <ds@schleef.org>
7
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.
12
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.
17
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.
21
22     PCMCIA support code for the is adapted from the dummy_cs.c driver
23     of the Linux PCMCIA Card Services package.
24
25     The initial developer of the original code is David A. Hinds
26     <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
27     are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
28
29 */
30 /*
31 Driver: cb_das16_cs
32 Description: Computer Boards PC-CARD DAS16/16
33 Devices: [ComputerBoards] PC-CARD DAS16/16 (cb_das16_cs), PC-CARD DAS16/16-AO
34 Author: ds
35 Updated: Mon, 04 Nov 2002 20:04:21 -0800
36 Status: experimental
37
38
39 */
40
41 #include <linux/interrupt.h>
42 #include <linux/slab.h>
43 #include "../comedidev.h"
44 #include <linux/delay.h>
45 #include <linux/pci.h>
46
47 #include <pcmcia/cistpl.h>
48 #include <pcmcia/ds.h>
49
50 #include "8253.h"
51
52 #define DAS16CS_SIZE                    18
53
54 #define DAS16CS_ADC_DATA                0
55 #define DAS16CS_DIO_MUX                 2
56 #define DAS16CS_MISC1                   4
57 #define DAS16CS_MISC2                   6
58 #define DAS16CS_CTR0                    8
59 #define DAS16CS_CTR1                    10
60 #define DAS16CS_CTR2                    12
61 #define DAS16CS_CTR_CONTROL             14
62 #define DAS16CS_DIO                     16
63
64 struct das16cs_board {
65         const char *name;
66         int device_id;
67         int n_ao_chans;
68 };
69 static const struct das16cs_board das16cs_boards[] = {
70         {
71          .device_id = 0x0000,   /* unknown */
72          .name = "PC-CARD DAS16/16",
73          .n_ao_chans = 0,
74          },
75         {
76          .device_id = 0x0039,
77          .name = "PC-CARD DAS16/16-AO",
78          .n_ao_chans = 2,
79          },
80         {
81          .device_id = 0x4009,
82          .name = "PCM-DAS16s/16",
83          .n_ao_chans = 0,
84          },
85 };
86
87 #define n_boards ARRAY_SIZE(das16cs_boards)
88 #define thisboard ((const struct das16cs_board *)dev->board_ptr)
89
90 struct das16cs_private {
91         struct pcmcia_device *link;
92
93         unsigned int ao_readback[2];
94         unsigned short status1;
95         unsigned short status2;
96 };
97 #define devpriv ((struct das16cs_private *)dev->private)
98
99 static int das16cs_attach(struct comedi_device *dev,
100                           struct comedi_devconfig *it);
101 static void das16cs_detach(struct comedi_device *dev);
102 static struct comedi_driver driver_das16cs = {
103         .driver_name = "cb_das16_cs",
104         .module = THIS_MODULE,
105         .attach = das16cs_attach,
106         .detach = das16cs_detach,
107 };
108
109 static struct pcmcia_device *cur_dev;
110
111 static const struct comedi_lrange das16cs_ai_range = { 4, {
112                                                            RANGE(-10, 10),
113                                                            RANGE(-5, 5),
114                                                            RANGE(-2.5, 2.5),
115                                                            RANGE(-1.25, 1.25),
116                                                            }
117 };
118
119 static irqreturn_t das16cs_interrupt(int irq, void *d);
120 static int das16cs_ai_rinsn(struct comedi_device *dev,
121                             struct comedi_subdevice *s,
122                             struct comedi_insn *insn, unsigned int *data);
123 static int das16cs_ai_cmd(struct comedi_device *dev,
124                           struct comedi_subdevice *s);
125 static int das16cs_ai_cmdtest(struct comedi_device *dev,
126                               struct comedi_subdevice *s,
127                               struct comedi_cmd *cmd);
128 static int das16cs_ao_winsn(struct comedi_device *dev,
129                             struct comedi_subdevice *s,
130                             struct comedi_insn *insn, unsigned int *data);
131 static int das16cs_ao_rinsn(struct comedi_device *dev,
132                             struct comedi_subdevice *s,
133                             struct comedi_insn *insn, unsigned int *data);
134 static int das16cs_dio_insn_bits(struct comedi_device *dev,
135                                  struct comedi_subdevice *s,
136                                  struct comedi_insn *insn, unsigned int *data);
137 static int das16cs_dio_insn_config(struct comedi_device *dev,
138                                    struct comedi_subdevice *s,
139                                    struct comedi_insn *insn,
140                                    unsigned int *data);
141 static int das16cs_timer_insn_read(struct comedi_device *dev,
142                                    struct comedi_subdevice *s,
143                                    struct comedi_insn *insn,
144                                    unsigned int *data);
145 static int das16cs_timer_insn_config(struct comedi_device *dev,
146                                      struct comedi_subdevice *s,
147                                      struct comedi_insn *insn,
148                                      unsigned int *data);
149
150 static const struct das16cs_board *das16cs_probe(struct comedi_device *dev,
151                                                  struct pcmcia_device *link)
152 {
153         int i;
154
155         for (i = 0; i < n_boards; i++) {
156                 if (das16cs_boards[i].device_id == link->card_id)
157                         return das16cs_boards + i;
158         }
159
160         dev_dbg(dev->class_dev, "unknown board!\n");
161
162         return NULL;
163 }
164
165 static int das16cs_attach(struct comedi_device *dev,
166                           struct comedi_devconfig *it)
167 {
168         struct pcmcia_device *link;
169         struct comedi_subdevice *s;
170         int ret;
171         int i;
172
173         dev_dbg(dev->class_dev, "cb_das16_cs: attach\n");
174
175         link = cur_dev;         /* XXX hack */
176         if (!link)
177                 return -EIO;
178
179         dev->iobase = link->resource[0]->start;
180         dev_dbg(dev->class_dev, "I/O base=0x%04lx\n", dev->iobase);
181
182         dev_dbg(dev->class_dev, "fingerprint:\n");
183         for (i = 0; i < 48; i += 2)
184                 dev_dbg(dev->class_dev, "%04x\n", inw(dev->iobase + i));
185
186
187         ret = request_irq(link->irq, das16cs_interrupt,
188                           IRQF_SHARED, "cb_das16_cs", dev);
189         if (ret < 0)
190                 return ret;
191
192         dev->irq = link->irq;
193
194         dev_dbg(dev->class_dev, "irq=%u\n", dev->irq);
195
196         dev->board_ptr = das16cs_probe(dev, link);
197         if (!dev->board_ptr)
198                 return -EIO;
199
200         dev->board_name = thisboard->name;
201
202         if (alloc_private(dev, sizeof(struct das16cs_private)) < 0)
203                 return -ENOMEM;
204
205         ret = comedi_alloc_subdevices(dev, 4);
206         if (ret)
207                 return ret;
208
209         s = dev->subdevices + 0;
210         dev->read_subdev = s;
211         /* analog input subdevice */
212         s->type = COMEDI_SUBD_AI;
213         s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
214         s->n_chan = 16;
215         s->maxdata = 0xffff;
216         s->range_table = &das16cs_ai_range;
217         s->len_chanlist = 16;
218         s->insn_read = das16cs_ai_rinsn;
219         s->do_cmd = das16cs_ai_cmd;
220         s->do_cmdtest = das16cs_ai_cmdtest;
221
222         s = dev->subdevices + 1;
223         /* analog output subdevice */
224         if (thisboard->n_ao_chans) {
225                 s->type = COMEDI_SUBD_AO;
226                 s->subdev_flags = SDF_WRITABLE;
227                 s->n_chan = thisboard->n_ao_chans;
228                 s->maxdata = 0xffff;
229                 s->range_table = &range_bipolar10;
230                 s->insn_write = &das16cs_ao_winsn;
231                 s->insn_read = &das16cs_ao_rinsn;
232         }
233
234         s = dev->subdevices + 2;
235         /* digital i/o subdevice */
236         if (1) {
237                 s->type = COMEDI_SUBD_DIO;
238                 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
239                 s->n_chan = 8;
240                 s->maxdata = 1;
241                 s->range_table = &range_digital;
242                 s->insn_bits = das16cs_dio_insn_bits;
243                 s->insn_config = das16cs_dio_insn_config;
244         } else {
245                 s->type = COMEDI_SUBD_UNUSED;
246         }
247
248         s = dev->subdevices + 3;
249         /* timer subdevice */
250         if (0) {
251                 s->type = COMEDI_SUBD_TIMER;
252                 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
253                 s->n_chan = 1;
254                 s->maxdata = 0xff;
255                 s->range_table = &range_unknown;
256                 s->insn_read = das16cs_timer_insn_read;
257                 s->insn_config = das16cs_timer_insn_config;
258         } else {
259                 s->type = COMEDI_SUBD_UNUSED;
260         }
261
262
263         return 1;
264 }
265
266 static void das16cs_detach(struct comedi_device *dev)
267 {
268         if (dev->irq)
269                 free_irq(dev->irq, dev);
270 }
271
272 static irqreturn_t das16cs_interrupt(int irq, void *d)
273 {
274         /* struct comedi_device *dev = d; */
275         return IRQ_HANDLED;
276 }
277
278 /*
279  * "instructions" read/write data in "one-shot" or "software-triggered"
280  * mode.
281  */
282 static int das16cs_ai_rinsn(struct comedi_device *dev,
283                             struct comedi_subdevice *s,
284                             struct comedi_insn *insn, unsigned int *data)
285 {
286         int i;
287         int to;
288         int aref;
289         int range;
290         int chan;
291         static int range_bits[] = { 0x800, 0x000, 0x100, 0x200 };
292
293         chan = CR_CHAN(insn->chanspec);
294         aref = CR_AREF(insn->chanspec);
295         range = CR_RANGE(insn->chanspec);
296
297         outw(chan, dev->iobase + 2);
298
299         devpriv->status1 &= ~0xf320;
300         devpriv->status1 |= (aref == AREF_DIFF) ? 0 : 0x0020;
301         outw(devpriv->status1, dev->iobase + 4);
302
303         devpriv->status2 &= ~0xff00;
304         devpriv->status2 |= range_bits[range];
305         outw(devpriv->status2, dev->iobase + 6);
306
307         for (i = 0; i < insn->n; i++) {
308                 outw(0, dev->iobase);
309
310 #define TIMEOUT 1000
311                 for (to = 0; to < TIMEOUT; to++) {
312                         if (inw(dev->iobase + 4) & 0x0080)
313                                 break;
314                 }
315                 if (to == TIMEOUT) {
316                         dev_dbg(dev->class_dev, "cb_das16_cs: ai timeout\n");
317                         return -ETIME;
318                 }
319                 data[i] = (unsigned short)inw(dev->iobase + 0);
320         }
321
322         return i;
323 }
324
325 static int das16cs_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
326 {
327         return -EINVAL;
328 }
329
330 static int das16cs_ai_cmdtest(struct comedi_device *dev,
331                               struct comedi_subdevice *s,
332                               struct comedi_cmd *cmd)
333 {
334         int err = 0;
335         int tmp;
336
337         /* cmdtest tests a particular command to see if it is valid.
338          * Using the cmdtest ioctl, a user can create a valid cmd
339          * and then have it executes by the cmd ioctl.
340          *
341          * cmdtest returns 1,2,3,4 or 0, depending on which tests
342          * the command passes. */
343
344         /* step 1: make sure trigger sources are trivially valid */
345
346         tmp = cmd->start_src;
347         cmd->start_src &= TRIG_NOW;
348         if (!cmd->start_src || tmp != cmd->start_src)
349                 err++;
350
351         tmp = cmd->scan_begin_src;
352         cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
353         if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
354                 err++;
355
356         tmp = cmd->convert_src;
357         cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
358         if (!cmd->convert_src || tmp != cmd->convert_src)
359                 err++;
360
361         tmp = cmd->scan_end_src;
362         cmd->scan_end_src &= TRIG_COUNT;
363         if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
364                 err++;
365
366         tmp = cmd->stop_src;
367         cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
368         if (!cmd->stop_src || tmp != cmd->stop_src)
369                 err++;
370
371         if (err)
372                 return 1;
373
374         /* step 2: make sure trigger sources are unique and
375          * mutually compatible */
376
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)
380                 err++;
381         if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
382                 err++;
383         if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
384                 err++;
385
386         if (err)
387                 return 2;
388
389         /* step 3: make sure arguments are trivially compatible */
390
391         if (cmd->start_arg != 0) {
392                 cmd->start_arg = 0;
393                 err++;
394         }
395 #define MAX_SPEED       10000   /* in nanoseconds */
396 #define MIN_SPEED       1000000000      /* in nanoseconds */
397
398         if (cmd->scan_begin_src == TRIG_TIMER) {
399                 if (cmd->scan_begin_arg < MAX_SPEED) {
400                         cmd->scan_begin_arg = MAX_SPEED;
401                         err++;
402                 }
403                 if (cmd->scan_begin_arg > MIN_SPEED) {
404                         cmd->scan_begin_arg = MIN_SPEED;
405                         err++;
406                 }
407         } else {
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;
413                         err++;
414                 }
415         }
416         if (cmd->convert_src == TRIG_TIMER) {
417                 if (cmd->convert_arg < MAX_SPEED) {
418                         cmd->convert_arg = MAX_SPEED;
419                         err++;
420                 }
421                 if (cmd->convert_arg > MIN_SPEED) {
422                         cmd->convert_arg = MIN_SPEED;
423                         err++;
424                 }
425         } else {
426                 /* external trigger */
427                 /* see above */
428                 if (cmd->convert_arg > 9) {
429                         cmd->convert_arg = 9;
430                         err++;
431                 }
432         }
433
434         if (cmd->scan_end_arg != cmd->chanlist_len) {
435                 cmd->scan_end_arg = cmd->chanlist_len;
436                 err++;
437         }
438         if (cmd->stop_src == TRIG_COUNT) {
439                 if (cmd->stop_arg > 0x00ffffff) {
440                         cmd->stop_arg = 0x00ffffff;
441                         err++;
442                 }
443         } else {
444                 /* TRIG_NONE */
445                 if (cmd->stop_arg != 0) {
446                         cmd->stop_arg = 0;
447                         err++;
448                 }
449         }
450
451         if (err)
452                 return 3;
453
454         /* step 4: fix up any arguments */
455
456         if (cmd->scan_begin_src == TRIG_TIMER) {
457                 unsigned int div1 = 0, div2 = 0;
458
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)
464                         err++;
465         }
466         if (cmd->convert_src == TRIG_TIMER) {
467                 unsigned int div1 = 0, div2 = 0;
468
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)
474                         err++;
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;
480                         err++;
481                 }
482         }
483
484         if (err)
485                 return 4;
486
487         return 0;
488 }
489
490 static int das16cs_ao_winsn(struct comedi_device *dev,
491                             struct comedi_subdevice *s,
492                             struct comedi_insn *insn, unsigned int *data)
493 {
494         int i;
495         int chan = CR_CHAN(insn->chanspec);
496         unsigned short status1;
497         unsigned short d;
498         int bit;
499
500         for (i = 0; i < insn->n; i++) {
501                 devpriv->ao_readback[chan] = data[i];
502                 d = data[i];
503
504                 outw(devpriv->status1, dev->iobase + 4);
505                 udelay(1);
506
507                 status1 = devpriv->status1 & ~0xf;
508                 if (chan)
509                         status1 |= 0x0001;
510                 else
511                         status1 |= 0x0008;
512
513 /*              printk("0x%04x\n",status1);*/
514                 outw(status1, dev->iobase + 4);
515                 udelay(1);
516
517                 for (bit = 15; bit >= 0; bit--) {
518                         int b = (d >> bit) & 0x1;
519                         b <<= 1;
520 /*                      printk("0x%04x\n",status1 | b | 0x0000);*/
521                         outw(status1 | b | 0x0000, dev->iobase + 4);
522                         udelay(1);
523 /*                      printk("0x%04x\n",status1 | b | 0x0004);*/
524                         outw(status1 | b | 0x0004, dev->iobase + 4);
525                         udelay(1);
526                 }
527 /*              make high both DAC0CS and DAC1CS to load
528                 new data and update analog output*/
529                 outw(status1 | 0x9, dev->iobase + 4);
530         }
531
532         return i;
533 }
534
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)
540 {
541         int i;
542         int chan = CR_CHAN(insn->chanspec);
543
544         for (i = 0; i < insn->n; i++)
545                 data[i] = devpriv->ao_readback[chan];
546
547         return i;
548 }
549
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)
558 {
559         if (data[0]) {
560                 s->state &= ~data[0];
561                 s->state |= data[0] & data[1];
562
563                 outw(s->state, dev->iobase + 16);
564         }
565
566         /* on return, data[1] contains the value of the digital
567          * input and output lines. */
568         data[1] = inw(dev->iobase + 16);
569
570         return insn->n;
571 }
572
573 static int das16cs_dio_insn_config(struct comedi_device *dev,
574                                    struct comedi_subdevice *s,
575                                    struct comedi_insn *insn, unsigned int *data)
576 {
577         int chan = CR_CHAN(insn->chanspec);
578         int bits;
579
580         if (chan < 4)
581                 bits = 0x0f;
582         else
583                 bits = 0xf0;
584
585         switch (data[0]) {
586         case INSN_CONFIG_DIO_OUTPUT:
587                 s->io_bits |= bits;
588                 break;
589         case INSN_CONFIG_DIO_INPUT:
590                 s->io_bits &= bits;
591                 break;
592         case INSN_CONFIG_DIO_QUERY:
593                 data[1] =
594                     (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
595                 return insn->n;
596                 break;
597         default:
598                 return -EINVAL;
599                 break;
600         }
601
602         devpriv->status2 &= ~0x00c0;
603         devpriv->status2 |= (s->io_bits & 0xf0) ? 0x0080 : 0;
604         devpriv->status2 |= (s->io_bits & 0x0f) ? 0x0040 : 0;
605
606         outw(devpriv->status2, dev->iobase + 6);
607
608         return insn->n;
609 }
610
611 static int das16cs_timer_insn_read(struct comedi_device *dev,
612                                    struct comedi_subdevice *s,
613                                    struct comedi_insn *insn, unsigned int *data)
614 {
615         return -EINVAL;
616 }
617
618 static int das16cs_timer_insn_config(struct comedi_device *dev,
619                                      struct comedi_subdevice *s,
620                                      struct comedi_insn *insn,
621                                      unsigned int *data)
622 {
623         return -EINVAL;
624 }
625
626 static void das16cs_pcmcia_config(struct pcmcia_device *link);
627 static void das16cs_pcmcia_release(struct pcmcia_device *link);
628 static int das16cs_pcmcia_suspend(struct pcmcia_device *p_dev);
629 static int das16cs_pcmcia_resume(struct pcmcia_device *p_dev);
630
631 static int das16cs_pcmcia_attach(struct pcmcia_device *);
632 static void das16cs_pcmcia_detach(struct pcmcia_device *);
633
634 struct local_info_t {
635         struct pcmcia_device *link;
636         int stop;
637         struct bus_operations *bus;
638 };
639
640 static int das16cs_pcmcia_attach(struct pcmcia_device *link)
641 {
642         struct local_info_t *local;
643
644         dev_dbg(&link->dev, "das16cs_pcmcia_attach()\n");
645
646         /* Allocate space for private device-specific data */
647         local = kzalloc(sizeof(struct local_info_t), GFP_KERNEL);
648         if (!local)
649                 return -ENOMEM;
650         local->link = link;
651         link->priv = local;
652
653         cur_dev = link;
654
655         das16cs_pcmcia_config(link);
656
657         return 0;
658 }
659
660 static void das16cs_pcmcia_detach(struct pcmcia_device *link)
661 {
662         dev_dbg(&link->dev, "das16cs_pcmcia_detach\n");
663
664         ((struct local_info_t *)link->priv)->stop = 1;
665         das16cs_pcmcia_release(link);
666         /* This points to the parent struct local_info_t struct */
667         kfree(link->priv);
668 }
669
670 static int das16cs_pcmcia_config_loop(struct pcmcia_device *p_dev,
671                                 void *priv_data)
672 {
673         if (p_dev->config_index == 0)
674                 return -EINVAL;
675
676         return pcmcia_request_io(p_dev);
677 }
678
679 static void das16cs_pcmcia_config(struct pcmcia_device *link)
680 {
681         int ret;
682
683         dev_dbg(&link->dev, "das16cs_pcmcia_config\n");
684
685         /* Do we need to allocate an interrupt? */
686         link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
687
688         ret = pcmcia_loop_config(link, das16cs_pcmcia_config_loop, NULL);
689         if (ret) {
690                 dev_warn(&link->dev, "no configuration found\n");
691                 goto failed;
692         }
693
694         if (!link->irq)
695                 goto failed;
696
697         ret = pcmcia_enable_device(link);
698         if (ret)
699                 goto failed;
700
701         return;
702
703 failed:
704         das16cs_pcmcia_release(link);
705 }
706
707 static void das16cs_pcmcia_release(struct pcmcia_device *link)
708 {
709         dev_dbg(&link->dev, "das16cs_pcmcia_release\n");
710         pcmcia_disable_device(link);
711 }
712
713 static int das16cs_pcmcia_suspend(struct pcmcia_device *link)
714 {
715         struct local_info_t *local = link->priv;
716
717         /* Mark the device as stopped, to block IO until later */
718         local->stop = 1;
719
720         return 0;
721 }
722
723 static int das16cs_pcmcia_resume(struct pcmcia_device *link)
724 {
725         struct local_info_t *local = link->priv;
726
727         local->stop = 0;
728         return 0;
729 }
730
731 static const struct pcmcia_device_id das16cs_id_table[] = {
732         PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0039),
733         PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4009),
734         PCMCIA_DEVICE_NULL
735 };
736 MODULE_DEVICE_TABLE(pcmcia, das16cs_id_table);
737
738 static struct pcmcia_driver das16cs_driver = {
739         .name           = "cb_das16_cs",
740         .owner          = THIS_MODULE,
741         .probe          = das16cs_pcmcia_attach,
742         .remove         = das16cs_pcmcia_detach,
743         .suspend        = das16cs_pcmcia_suspend,
744         .resume         = das16cs_pcmcia_resume,
745         .id_table       = das16cs_id_table,
746 };
747
748 static int __init das16cs_init(void)
749 {
750         int ret;
751
752         ret = comedi_driver_register(&driver_das16cs);
753         if (ret < 0)
754                 return ret;
755
756         ret = pcmcia_register_driver(&das16cs_driver);
757         if (ret < 0) {
758                 comedi_driver_unregister(&driver_das16cs);
759                 return ret;
760         }
761
762         return 0;
763 }
764 module_init(das16cs_init);
765
766 static void __exit das16cs_exit(void)
767 {
768         pcmcia_unregister_driver(&das16cs_driver);
769         comedi_driver_unregister(&driver_das16cs);
770 }
771 module_exit(das16cs_exit);
772
773 MODULE_AUTHOR("David A. Schleef <ds@schleef.org>");
774 MODULE_DESCRIPTION("Comedi driver for Computer Boards PC-CARD DAS16/16");
775 MODULE_LICENSE("GPL");