Merge branch 'linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes...
[linux-2.6.git] / 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 */
23 /*
24 Driver: cb_das16_cs
25 Description: Computer Boards PC-CARD DAS16/16
26 Devices: [ComputerBoards] PC-CARD DAS16/16 (cb_das16_cs), PC-CARD DAS16/16-AO
27 Author: ds
28 Updated: Mon, 04 Nov 2002 20:04:21 -0800
29 Status: experimental
30
31
32 */
33
34 #include <linux/interrupt.h>
35 #include <linux/slab.h>
36 #include "../comedidev.h"
37 #include <linux/delay.h>
38 #include <linux/pci.h>
39
40 #include <pcmcia/cs_types.h>
41 #include <pcmcia/cs.h>
42 #include <pcmcia/cistpl.h>
43 #include <pcmcia/ds.h>
44
45 #include "8253.h"
46
47 #define DAS16CS_SIZE                    18
48
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
58
59 struct das16cs_board {
60         const char *name;
61         int device_id;
62         int n_ao_chans;
63 };
64 static const struct das16cs_board das16cs_boards[] = {
65         {
66          .device_id = 0x0000,   /* unknown */
67          .name = "PC-CARD DAS16/16",
68          .n_ao_chans = 0,
69          },
70         {
71          .device_id = 0x0039,
72          .name = "PC-CARD DAS16/16-AO",
73          .n_ao_chans = 2,
74          },
75         {
76          .device_id = 0x4009,
77          .name = "PCM-DAS16s/16",
78          .n_ao_chans = 0,
79          },
80 };
81
82 #define n_boards ARRAY_SIZE(das16cs_boards)
83 #define thisboard ((const struct das16cs_board *)dev->board_ptr)
84
85 struct das16cs_private {
86         struct pcmcia_device *link;
87
88         unsigned int ao_readback[2];
89         unsigned short status1;
90         unsigned short status2;
91 };
92 #define devpriv ((struct das16cs_private *)dev->private)
93
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,
102 };
103
104 static struct pcmcia_device *cur_dev = NULL;
105
106 static const struct comedi_lrange das16cs_ai_range = { 4, {
107                                                            RANGE(-10, 10),
108                                                            RANGE(-5, 5),
109                                                            RANGE(-2.5, 2.5),
110                                                            RANGE(-1.25, 1.25),
111                                                            }
112 };
113
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,
135                                    unsigned int *data);
136 static int das16cs_timer_insn_read(struct comedi_device *dev,
137                                    struct comedi_subdevice *s,
138                                    struct comedi_insn *insn,
139                                    unsigned int *data);
140 static int das16cs_timer_insn_config(struct comedi_device *dev,
141                                      struct comedi_subdevice *s,
142                                      struct comedi_insn *insn,
143                                      unsigned int *data);
144
145 static const struct das16cs_board *das16cs_probe(struct comedi_device *dev,
146                                                  struct pcmcia_device *link)
147 {
148         int i;
149
150         for (i = 0; i < n_boards; i++) {
151                 if (das16cs_boards[i].device_id == link->card_id)
152                         return das16cs_boards + i;
153         }
154
155         printk("unknown board!\n");
156
157         return NULL;
158 }
159
160 static int das16cs_attach(struct comedi_device *dev,
161                           struct comedi_devconfig *it)
162 {
163         struct pcmcia_device *link;
164         struct comedi_subdevice *s;
165         int ret;
166         int i;
167
168         printk("comedi%d: cb_das16_cs: ", dev->minor);
169
170         link = cur_dev;         /* XXX hack */
171         if (!link)
172                 return -EIO;
173
174         dev->iobase = link->io.BasePort1;
175         printk("I/O base=0x%04lx ", dev->iobase);
176
177         printk("fingerprint:\n");
178         for (i = 0; i < 48; i += 2)
179                 printk("%04x ", inw(dev->iobase + i));
180
181         printk("\n");
182
183         ret = request_irq(link->irq, das16cs_interrupt,
184                           IRQF_SHARED, "cb_das16_cs", dev);
185         if (ret < 0)
186                 return ret;
187
188         dev->irq = link->irq;
189
190         printk("irq=%u ", dev->irq);
191
192         dev->board_ptr = das16cs_probe(dev, link);
193         if (!dev->board_ptr)
194                 return -EIO;
195
196         dev->board_name = thisboard->name;
197
198         if (alloc_private(dev, sizeof(struct das16cs_private)) < 0)
199                 return -ENOMEM;
200
201         if (alloc_subdevices(dev, 4) < 0)
202                 return -ENOMEM;
203
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;
209         s->n_chan = 16;
210         s->maxdata = 0xffff;
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;
216
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;
223                 s->maxdata = 0xffff;
224                 s->range_table = &range_bipolar10;
225                 s->insn_write = &das16cs_ao_winsn;
226                 s->insn_read = &das16cs_ao_rinsn;
227         }
228
229         s = dev->subdevices + 2;
230         /* digital i/o subdevice */
231         if (1) {
232                 s->type = COMEDI_SUBD_DIO;
233                 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
234                 s->n_chan = 8;
235                 s->maxdata = 1;
236                 s->range_table = &range_digital;
237                 s->insn_bits = das16cs_dio_insn_bits;
238                 s->insn_config = das16cs_dio_insn_config;
239         } else {
240                 s->type = COMEDI_SUBD_UNUSED;
241         }
242
243         s = dev->subdevices + 3;
244         /* timer subdevice */
245         if (0) {
246                 s->type = COMEDI_SUBD_TIMER;
247                 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
248                 s->n_chan = 1;
249                 s->maxdata = 0xff;
250                 s->range_table = &range_unknown;
251                 s->insn_read = das16cs_timer_insn_read;
252                 s->insn_config = das16cs_timer_insn_config;
253         } else {
254                 s->type = COMEDI_SUBD_UNUSED;
255         }
256
257         printk("attached\n");
258
259         return 1;
260 }
261
262 static int das16cs_detach(struct comedi_device *dev)
263 {
264         printk("comedi%d: das16cs: remove\n", dev->minor);
265
266         if (dev->irq)
267                 free_irq(dev->irq, dev);
268
269
270         return 0;
271 }
272
273 static irqreturn_t das16cs_interrupt(int irq, void *d)
274 {
275         /* struct comedi_device *dev = d; */
276         return IRQ_HANDLED;
277 }
278
279 /*
280  * "instructions" read/write data in "one-shot" or "software-triggered"
281  * mode.
282  */
283 static int das16cs_ai_rinsn(struct comedi_device *dev,
284                             struct comedi_subdevice *s,
285                             struct comedi_insn *insn, unsigned int *data)
286 {
287         int i;
288         int to;
289         int aref;
290         int range;
291         int chan;
292         static int range_bits[] = { 0x800, 0x000, 0x100, 0x200 };
293
294         chan = CR_CHAN(insn->chanspec);
295         aref = CR_AREF(insn->chanspec);
296         range = CR_RANGE(insn->chanspec);
297
298         outw(chan, dev->iobase + 2);
299
300         devpriv->status1 &= ~0xf320;
301         devpriv->status1 |= (aref == AREF_DIFF) ? 0 : 0x0020;
302         outw(devpriv->status1, dev->iobase + 4);
303
304         devpriv->status2 &= ~0xff00;
305         devpriv->status2 |= range_bits[range];
306         outw(devpriv->status2, dev->iobase + 6);
307
308         for (i = 0; i < insn->n; i++) {
309                 outw(0, dev->iobase);
310
311 #define TIMEOUT 1000
312                 for (to = 0; to < TIMEOUT; to++) {
313                         if (inw(dev->iobase + 4) & 0x0080)
314                                 break;
315                 }
316                 if (to == TIMEOUT) {
317                         printk("cb_das16_cs: ai timeout\n");
318                         return -ETIME;
319                 }
320                 data[i] = (unsigned short)inw(dev->iobase + 0);
321         }
322
323         return i;
324 }
325
326 static int das16cs_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
327 {
328         return -EINVAL;
329 }
330
331 static int das16cs_ai_cmdtest(struct comedi_device *dev,
332                               struct comedi_subdevice *s,
333                               struct comedi_cmd *cmd)
334 {
335         int err = 0;
336         int tmp;
337
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.
341          *
342          * cmdtest returns 1,2,3,4 or 0, depending on which tests
343          * the command passes. */
344
345         /* step 1: make sure trigger sources are trivially valid */
346
347         tmp = cmd->start_src;
348         cmd->start_src &= TRIG_NOW;
349         if (!cmd->start_src || tmp != cmd->start_src)
350                 err++;
351
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)
355                 err++;
356
357         tmp = cmd->convert_src;
358         cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
359         if (!cmd->convert_src || tmp != cmd->convert_src)
360                 err++;
361
362         tmp = cmd->scan_end_src;
363         cmd->scan_end_src &= TRIG_COUNT;
364         if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
365                 err++;
366
367         tmp = cmd->stop_src;
368         cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
369         if (!cmd->stop_src || tmp != cmd->stop_src)
370                 err++;
371
372         if (err)
373                 return 1;
374
375         /* step 2: make sure trigger sources are unique and 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 (insn->n != 2)
560                 return -EINVAL;
561
562         if (data[0]) {
563                 s->state &= ~data[0];
564                 s->state |= data[0] & data[1];
565
566                 outw(s->state, dev->iobase + 16);
567         }
568
569         /* on return, data[1] contains the value of the digital
570          * input and output lines. */
571         data[1] = inw(dev->iobase + 16);
572
573         return 2;
574 }
575
576 static int das16cs_dio_insn_config(struct comedi_device *dev,
577                                    struct comedi_subdevice *s,
578                                    struct comedi_insn *insn, unsigned int *data)
579 {
580         int chan = CR_CHAN(insn->chanspec);
581         int bits;
582
583         if (chan < 4)
584                 bits = 0x0f;
585         else
586                 bits = 0xf0;
587
588         switch (data[0]) {
589         case INSN_CONFIG_DIO_OUTPUT:
590                 s->io_bits |= bits;
591                 break;
592         case INSN_CONFIG_DIO_INPUT:
593                 s->io_bits &= bits;
594                 break;
595         case INSN_CONFIG_DIO_QUERY:
596                 data[1] =
597                     (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
598                 return insn->n;
599                 break;
600         default:
601                 return -EINVAL;
602                 break;
603         }
604
605         devpriv->status2 &= ~0x00c0;
606         devpriv->status2 |= (s->io_bits & 0xf0) ? 0x0080 : 0;
607         devpriv->status2 |= (s->io_bits & 0x0f) ? 0x0040 : 0;
608
609         outw(devpriv->status2, dev->iobase + 6);
610
611         return insn->n;
612 }
613
614 static int das16cs_timer_insn_read(struct comedi_device *dev,
615                                    struct comedi_subdevice *s,
616                                    struct comedi_insn *insn, unsigned int *data)
617 {
618         return -EINVAL;
619 }
620
621 static int das16cs_timer_insn_config(struct comedi_device *dev,
622                                      struct comedi_subdevice *s,
623                                      struct comedi_insn *insn,
624                                      unsigned int *data)
625 {
626         return -EINVAL;
627 }
628
629 /* PCMCIA stuff */
630
631 /*======================================================================
632
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.
635
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.
639
640 ======================================================================*/
641
642 #if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE)
643
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);
648
649 /*
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.
653 */
654
655 static int das16cs_pcmcia_attach(struct pcmcia_device *);
656 static void das16cs_pcmcia_detach(struct pcmcia_device *);
657
658 /*
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.
663 */
664
665 /*
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
668    database.
669 */
670
671 static dev_info_t dev_info = "cb_das16_cs";
672
673 struct local_info_t {
674         struct pcmcia_device *link;
675         int stop;
676         struct bus_operations *bus;
677 };
678
679 /*======================================================================
680
681     das16cs_pcmcia_attach() creates an "instance" of the driver, allocating
682     local data structures for one device.  The device is registered
683     with Card Services.
684
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.
688
689 ======================================================================*/
690
691 static int das16cs_pcmcia_attach(struct pcmcia_device *link)
692 {
693         struct local_info_t *local;
694
695         dev_dbg(&link->dev, "das16cs_pcmcia_attach()\n");
696
697         /* Allocate space for private device-specific data */
698         local = kzalloc(sizeof(struct local_info_t), GFP_KERNEL);
699         if (!local)
700                 return -ENOMEM;
701         local->link = link;
702         link->priv = local;
703
704         /* Initialize the pcmcia_device structure */
705         link->conf.Attributes = 0;
706         link->conf.IntType = INT_MEMORY_AND_IO;
707
708         cur_dev = link;
709
710         das16cs_pcmcia_config(link);
711
712         return 0;
713 }                               /* das16cs_pcmcia_attach */
714
715 static void das16cs_pcmcia_detach(struct pcmcia_device *link)
716 {
717         dev_dbg(&link->dev, "das16cs_pcmcia_detach\n");
718
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 */
722         kfree(link->priv);
723 }                               /* das16cs_pcmcia_detach */
724
725
726 static int das16cs_pcmcia_config_loop(struct pcmcia_device *p_dev,
727                                 cistpl_cftable_entry_t *cfg,
728                                 cistpl_cftable_entry_t *dflt,
729                                 unsigned int vcc,
730                                 void *priv_data)
731 {
732         if (cfg->index == 0)
733                 return -EINVAL;
734
735         /* Do we need to allocate an interrupt? */
736         p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
737
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;
750                 if (io->nwin > 1) {
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;
754                 }
755                 /* This reserves IO space but doesn't actually enable it */
756                 return pcmcia_request_io(p_dev, &p_dev->io);
757         }
758
759         return 0;
760 }
761
762 static void das16cs_pcmcia_config(struct pcmcia_device *link)
763 {
764         int ret;
765
766         dev_dbg(&link->dev, "das16cs_pcmcia_config\n");
767
768         ret = pcmcia_loop_config(link, das16cs_pcmcia_config_loop, NULL);
769         if (ret) {
770                 dev_warn(&link->dev, "no configuration found\n");
771                 goto failed;
772         }
773
774         if (!link->irq)
775                 goto failed;
776
777         /*
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.
781          */
782         ret = pcmcia_request_configuration(link, &link->conf);
783         if (ret)
784                 goto failed;
785
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);
796         printk("\n");
797
798         return;
799
800 failed:
801         das16cs_pcmcia_release(link);
802 }                               /* das16cs_pcmcia_config */
803
804 static void das16cs_pcmcia_release(struct pcmcia_device *link)
805 {
806         dev_dbg(&link->dev, "das16cs_pcmcia_release\n");
807         pcmcia_disable_device(link);
808 }                               /* das16cs_pcmcia_release */
809
810 static int das16cs_pcmcia_suspend(struct pcmcia_device *link)
811 {
812         struct local_info_t *local = link->priv;
813
814         /* Mark the device as stopped, to block IO until later */
815         local->stop = 1;
816
817         return 0;
818 }                               /* das16cs_pcmcia_suspend */
819
820 static int das16cs_pcmcia_resume(struct pcmcia_device *link)
821 {
822         struct local_info_t *local = link->priv;
823
824         local->stop = 0;
825         return 0;
826 }                               /* das16cs_pcmcia_resume */
827
828 /*====================================================================*/
829
830 static struct pcmcia_device_id das16cs_id_table[] = {
831         PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0039),
832         PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4009),
833         PCMCIA_DEVICE_NULL
834 };
835
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");
840
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,
848         .drv = {
849                 .name = dev_info,
850                 },
851 };
852
853 static int __init init_das16cs_pcmcia_cs(void)
854 {
855         pcmcia_register_driver(&das16cs_driver);
856         return 0;
857 }
858
859 static void __exit exit_das16cs_pcmcia_cs(void)
860 {
861         pr_debug("das16cs_pcmcia_cs: unloading\n");
862         pcmcia_unregister_driver(&das16cs_driver);
863 }
864
865 int __init init_module(void)
866 {
867         int ret;
868
869         ret = init_das16cs_pcmcia_cs();
870         if (ret < 0)
871                 return ret;
872
873         return comedi_driver_register(&driver_das16cs);
874 }
875
876 void __exit cleanup_module(void)
877 {
878         exit_das16cs_pcmcia_cs();
879         comedi_driver_unregister(&driver_das16cs);
880 }
881
882 #else
883 static int __init driver_das16cs_init_module(void)
884 {
885         return comedi_driver_register(&driver_das16cs);
886 }
887
888 static void __exit driver_das16cs_cleanup_module(void)
889 {
890         comedi_driver_unregister(&driver_das16cs);
891 }
892
893 module_init(driver_das16cs_init_module);
894 module_exit(driver_das16cs_cleanup_module);
895 #endif /* CONFIG_PCMCIA */