]> bbs.cooldavid.org Git - net-next-2.6.git/blame - drivers/staging/comedi/drivers/cb_das16_cs.c
pcmcia: do not use io_req_t after call to pcmcia_request_io()
[net-next-2.6.git] / drivers / staging / comedi / drivers / cb_das16_cs.c
CommitLineData
f0922ec5
DS
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/*
24Driver: cb_das16_cs
25Description: Computer Boards PC-CARD DAS16/16
26Devices: [ComputerBoards] PC-CARD DAS16/16 (cb_das16_cs), PC-CARD DAS16/16-AO
27Author: ds
28Updated: Mon, 04 Nov 2002 20:04:21 -0800
29Status: experimental
30
31
32*/
33
25436dc9 34#include <linux/interrupt.h>
5a0e3ad6 35#include <linux/slab.h>
f0922ec5
DS
36#include "../comedidev.h"
37#include <linux/delay.h>
38#include <linux/pci.h>
39
f0922ec5
DS
40#include <pcmcia/cs.h>
41#include <pcmcia/cistpl.h>
42#include <pcmcia/ds.h>
43
44#include "8253.h"
45
46#define DAS16CS_SIZE 18
47
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
57
3281a63d 58struct das16cs_board {
f0922ec5
DS
59 const char *name;
60 int device_id;
61 int n_ao_chans;
3281a63d
BP
62};
63static const struct das16cs_board das16cs_boards[] = {
f0922ec5 64 {
0a85b6f0
MT
65 .device_id = 0x0000, /* unknown */
66 .name = "PC-CARD DAS16/16",
67 .n_ao_chans = 0,
68 },
f0922ec5 69 {
0a85b6f0
MT
70 .device_id = 0x0039,
71 .name = "PC-CARD DAS16/16-AO",
72 .n_ao_chans = 2,
73 },
f0922ec5 74 {
0a85b6f0
MT
75 .device_id = 0x4009,
76 .name = "PCM-DAS16s/16",
77 .n_ao_chans = 0,
78 },
f0922ec5
DS
79};
80
b6ac1613 81#define n_boards ARRAY_SIZE(das16cs_boards)
3281a63d 82#define thisboard ((const struct das16cs_board *)dev->board_ptr)
f0922ec5 83
bfae362a 84struct das16cs_private {
f0922ec5
DS
85 struct pcmcia_device *link;
86
790c5541 87 unsigned int ao_readback[2];
f0922ec5
DS
88 unsigned short status1;
89 unsigned short status2;
bfae362a
BP
90};
91#define devpriv ((struct das16cs_private *)dev->private)
f0922ec5 92
0a85b6f0
MT
93static int das16cs_attach(struct comedi_device *dev,
94 struct comedi_devconfig *it);
da91b269 95static int das16cs_detach(struct comedi_device *dev);
139dfbdf 96static struct comedi_driver driver_das16cs = {
68c3dbff
BP
97 .driver_name = "cb_das16_cs",
98 .module = THIS_MODULE,
99 .attach = das16cs_attach,
100 .detach = das16cs_detach,
f0922ec5
DS
101};
102
103static struct pcmcia_device *cur_dev = NULL;
104
9ced1de6 105static const struct comedi_lrange das16cs_ai_range = { 4, {
0a85b6f0
MT
106 RANGE(-10, 10),
107 RANGE(-5, 5),
108 RANGE(-2.5, 2.5),
109 RANGE(-1.25, 1.25),
110 }
f0922ec5
DS
111};
112
70265d24 113static irqreturn_t das16cs_interrupt(int irq, void *d);
0a85b6f0
MT
114static int das16cs_ai_rinsn(struct comedi_device *dev,
115 struct comedi_subdevice *s,
116 struct comedi_insn *insn, unsigned int *data);
117static int das16cs_ai_cmd(struct comedi_device *dev,
118 struct comedi_subdevice *s);
119static int das16cs_ai_cmdtest(struct comedi_device *dev,
120 struct comedi_subdevice *s,
121 struct comedi_cmd *cmd);
122static int das16cs_ao_winsn(struct comedi_device *dev,
123 struct comedi_subdevice *s,
124 struct comedi_insn *insn, unsigned int *data);
125static int das16cs_ao_rinsn(struct comedi_device *dev,
126 struct comedi_subdevice *s,
127 struct comedi_insn *insn, unsigned int *data);
128static int das16cs_dio_insn_bits(struct comedi_device *dev,
129 struct comedi_subdevice *s,
130 struct comedi_insn *insn, unsigned int *data);
131static int das16cs_dio_insn_config(struct comedi_device *dev,
132 struct comedi_subdevice *s,
133 struct comedi_insn *insn,
134 unsigned int *data);
135static int das16cs_timer_insn_read(struct comedi_device *dev,
136 struct comedi_subdevice *s,
137 struct comedi_insn *insn,
138 unsigned int *data);
139static int das16cs_timer_insn_config(struct comedi_device *dev,
140 struct comedi_subdevice *s,
141 struct comedi_insn *insn,
142 unsigned int *data);
814900c9 143
0a85b6f0
MT
144static const struct das16cs_board *das16cs_probe(struct comedi_device *dev,
145 struct pcmcia_device *link)
f0922ec5 146{
f0922ec5
DS
147 int i;
148
f0922ec5 149 for (i = 0; i < n_boards; i++) {
55a19b39 150 if (das16cs_boards[i].device_id == link->card_id)
f0922ec5 151 return das16cs_boards + i;
f0922ec5
DS
152 }
153
154 printk("unknown board!\n");
155
156 return NULL;
157}
158
0a85b6f0
MT
159static int das16cs_attach(struct comedi_device *dev,
160 struct comedi_devconfig *it)
f0922ec5
DS
161{
162 struct pcmcia_device *link;
34c43922 163 struct comedi_subdevice *s;
f0922ec5
DS
164 int ret;
165 int i;
166
167 printk("comedi%d: cb_das16_cs: ", dev->minor);
168
169 link = cur_dev; /* XXX hack */
170 if (!link)
171 return -EIO;
172
9a017a91 173 dev->iobase = link->resource[0]->start;;
f0922ec5
DS
174 printk("I/O base=0x%04lx ", dev->iobase);
175
176 printk("fingerprint:\n");
7b8f2d1a 177 for (i = 0; i < 48; i += 2)
f0922ec5 178 printk("%04x ", inw(dev->iobase + i));
7b8f2d1a 179
f0922ec5
DS
180 printk("\n");
181
eb14120f 182 ret = request_irq(link->irq, das16cs_interrupt,
5f74ea14 183 IRQF_SHARED, "cb_das16_cs", dev);
7b8f2d1a 184 if (ret < 0)
f0922ec5 185 return ret;
7b8f2d1a 186
eb14120f 187 dev->irq = link->irq;
c8d1a126 188
f0922ec5
DS
189 printk("irq=%u ", dev->irq);
190
191 dev->board_ptr = das16cs_probe(dev, link);
192 if (!dev->board_ptr)
193 return -EIO;
194
195 dev->board_name = thisboard->name;
196
bfae362a 197 if (alloc_private(dev, sizeof(struct das16cs_private)) < 0)
f0922ec5
DS
198 return -ENOMEM;
199
200 if (alloc_subdevices(dev, 4) < 0)
201 return -ENOMEM;
202
203 s = dev->subdevices + 0;
204 dev->read_subdev = s;
205 /* analog input subdevice */
206 s->type = COMEDI_SUBD_AI;
207 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
208 s->n_chan = 16;
209 s->maxdata = 0xffff;
210 s->range_table = &das16cs_ai_range;
211 s->len_chanlist = 16;
212 s->insn_read = das16cs_ai_rinsn;
213 s->do_cmd = das16cs_ai_cmd;
214 s->do_cmdtest = das16cs_ai_cmdtest;
215
216 s = dev->subdevices + 1;
217 /* analog output subdevice */
218 if (thisboard->n_ao_chans) {
219 s->type = COMEDI_SUBD_AO;
220 s->subdev_flags = SDF_WRITABLE;
221 s->n_chan = thisboard->n_ao_chans;
222 s->maxdata = 0xffff;
223 s->range_table = &range_bipolar10;
224 s->insn_write = &das16cs_ao_winsn;
225 s->insn_read = &das16cs_ao_rinsn;
226 }
227
228 s = dev->subdevices + 2;
229 /* digital i/o subdevice */
230 if (1) {
231 s->type = COMEDI_SUBD_DIO;
232 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
233 s->n_chan = 8;
234 s->maxdata = 1;
235 s->range_table = &range_digital;
236 s->insn_bits = das16cs_dio_insn_bits;
237 s->insn_config = das16cs_dio_insn_config;
238 } else {
239 s->type = COMEDI_SUBD_UNUSED;
240 }
241
242 s = dev->subdevices + 3;
243 /* timer subdevice */
244 if (0) {
245 s->type = COMEDI_SUBD_TIMER;
246 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
247 s->n_chan = 1;
248 s->maxdata = 0xff;
249 s->range_table = &range_unknown;
250 s->insn_read = das16cs_timer_insn_read;
251 s->insn_config = das16cs_timer_insn_config;
252 } else {
253 s->type = COMEDI_SUBD_UNUSED;
254 }
255
256 printk("attached\n");
257
258 return 1;
259}
260
da91b269 261static int das16cs_detach(struct comedi_device *dev)
f0922ec5
DS
262{
263 printk("comedi%d: das16cs: remove\n", dev->minor);
264
7b8f2d1a 265 if (dev->irq)
5f74ea14 266 free_irq(dev->irq, dev);
7b8f2d1a 267
f0922ec5
DS
268
269 return 0;
270}
271
70265d24 272static irqreturn_t das16cs_interrupt(int irq, void *d)
f0922ec5 273{
2696fb57 274 /* struct comedi_device *dev = d; */
f0922ec5
DS
275 return IRQ_HANDLED;
276}
277
278/*
279 * "instructions" read/write data in "one-shot" or "software-triggered"
280 * mode.
281 */
0a85b6f0
MT
282static int das16cs_ai_rinsn(struct comedi_device *dev,
283 struct comedi_subdevice *s,
284 struct comedi_insn *insn, unsigned int *data)
f0922ec5
DS
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 printk("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
da91b269 325static int das16cs_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
f0922ec5
DS
326{
327 return -EINVAL;
328}
329
0a85b6f0
MT
330static int das16cs_ai_cmdtest(struct comedi_device *dev,
331 struct comedi_subdevice *s,
332 struct comedi_cmd *cmd)
f0922ec5
DS
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 mutually compatible */
375
828684f9 376 /* note that mutual compatibility is not an issue here */
f0922ec5 377 if (cmd->scan_begin_src != TRIG_TIMER &&
0a85b6f0 378 cmd->scan_begin_src != TRIG_EXT)
f0922ec5
DS
379 err++;
380 if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
381 err++;
382 if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
383 err++;
384
385 if (err)
386 return 2;
387
388 /* step 3: make sure arguments are trivially compatible */
389
390 if (cmd->start_arg != 0) {
391 cmd->start_arg = 0;
392 err++;
393 }
394#define MAX_SPEED 10000 /* in nanoseconds */
395#define MIN_SPEED 1000000000 /* in nanoseconds */
396
397 if (cmd->scan_begin_src == TRIG_TIMER) {
398 if (cmd->scan_begin_arg < MAX_SPEED) {
399 cmd->scan_begin_arg = MAX_SPEED;
400 err++;
401 }
402 if (cmd->scan_begin_arg > MIN_SPEED) {
403 cmd->scan_begin_arg = MIN_SPEED;
404 err++;
405 }
406 } else {
407 /* external trigger */
408 /* should be level/edge, hi/lo specification here */
409 /* should specify multiple external triggers */
410 if (cmd->scan_begin_arg > 9) {
411 cmd->scan_begin_arg = 9;
412 err++;
413 }
414 }
415 if (cmd->convert_src == TRIG_TIMER) {
416 if (cmd->convert_arg < MAX_SPEED) {
417 cmd->convert_arg = MAX_SPEED;
418 err++;
419 }
420 if (cmd->convert_arg > MIN_SPEED) {
421 cmd->convert_arg = MIN_SPEED;
422 err++;
423 }
424 } else {
425 /* external trigger */
426 /* see above */
427 if (cmd->convert_arg > 9) {
428 cmd->convert_arg = 9;
429 err++;
430 }
431 }
432
433 if (cmd->scan_end_arg != cmd->chanlist_len) {
434 cmd->scan_end_arg = cmd->chanlist_len;
435 err++;
436 }
437 if (cmd->stop_src == TRIG_COUNT) {
438 if (cmd->stop_arg > 0x00ffffff) {
439 cmd->stop_arg = 0x00ffffff;
440 err++;
441 }
442 } else {
443 /* TRIG_NONE */
444 if (cmd->stop_arg != 0) {
445 cmd->stop_arg = 0;
446 err++;
447 }
448 }
449
450 if (err)
451 return 3;
452
453 /* step 4: fix up any arguments */
454
455 if (cmd->scan_begin_src == TRIG_TIMER) {
48b1aff5 456 unsigned int div1 = 0, div2 = 0;
f0922ec5
DS
457
458 tmp = cmd->scan_begin_arg;
459 i8253_cascade_ns_to_timer(100, &div1, &div2,
0a85b6f0
MT
460 &cmd->scan_begin_arg,
461 cmd->flags & TRIG_ROUND_MASK);
f0922ec5
DS
462 if (tmp != cmd->scan_begin_arg)
463 err++;
464 }
465 if (cmd->convert_src == TRIG_TIMER) {
48b1aff5 466 unsigned int div1 = 0, div2 = 0;
f0922ec5
DS
467
468 tmp = cmd->convert_arg;
469 i8253_cascade_ns_to_timer(100, &div1, &div2,
0a85b6f0
MT
470 &cmd->scan_begin_arg,
471 cmd->flags & TRIG_ROUND_MASK);
f0922ec5
DS
472 if (tmp != cmd->convert_arg)
473 err++;
474 if (cmd->scan_begin_src == TRIG_TIMER &&
0a85b6f0
MT
475 cmd->scan_begin_arg <
476 cmd->convert_arg * cmd->scan_end_arg) {
f0922ec5 477 cmd->scan_begin_arg =
0a85b6f0 478 cmd->convert_arg * cmd->scan_end_arg;
f0922ec5
DS
479 err++;
480 }
481 }
482
483 if (err)
484 return 4;
485
486 return 0;
487}
488
0a85b6f0
MT
489static int das16cs_ao_winsn(struct comedi_device *dev,
490 struct comedi_subdevice *s,
491 struct comedi_insn *insn, unsigned int *data)
f0922ec5
DS
492{
493 int i;
494 int chan = CR_CHAN(insn->chanspec);
495 unsigned short status1;
496 unsigned short d;
497 int bit;
498
499 for (i = 0; i < insn->n; i++) {
500 devpriv->ao_readback[chan] = data[i];
501 d = data[i];
502
503 outw(devpriv->status1, dev->iobase + 4);
5f74ea14 504 udelay(1);
f0922ec5
DS
505
506 status1 = devpriv->status1 & ~0xf;
507 if (chan)
508 status1 |= 0x0001;
509 else
510 status1 |= 0x0008;
511
512/* printk("0x%04x\n",status1);*/
513 outw(status1, dev->iobase + 4);
5f74ea14 514 udelay(1);
f0922ec5
DS
515
516 for (bit = 15; bit >= 0; bit--) {
517 int b = (d >> bit) & 0x1;
518 b <<= 1;
519/* printk("0x%04x\n",status1 | b | 0x0000);*/
520 outw(status1 | b | 0x0000, dev->iobase + 4);
5f74ea14 521 udelay(1);
f0922ec5
DS
522/* printk("0x%04x\n",status1 | b | 0x0004);*/
523 outw(status1 | b | 0x0004, dev->iobase + 4);
5f74ea14 524 udelay(1);
f0922ec5
DS
525 }
526/* make high both DAC0CS and DAC1CS to load
527 new data and update analog output*/
528 outw(status1 | 0x9, dev->iobase + 4);
529 }
530
531 return i;
532}
533
534/* AO subdevices should have a read insn as well as a write insn.
535 * Usually this means copying a value stored in devpriv. */
0a85b6f0
MT
536static int das16cs_ao_rinsn(struct comedi_device *dev,
537 struct comedi_subdevice *s,
538 struct comedi_insn *insn, unsigned int *data)
f0922ec5
DS
539{
540 int i;
541 int chan = CR_CHAN(insn->chanspec);
542
543 for (i = 0; i < insn->n; i++)
544 data[i] = devpriv->ao_readback[chan];
545
546 return i;
547}
548
549/* DIO devices are slightly special. Although it is possible to
550 * implement the insn_read/insn_write interface, it is much more
551 * useful to applications if you implement the insn_bits interface.
552 * This allows packed reading/writing of the DIO channels. The
553 * comedi core can convert between insn_bits and insn_read/write */
0a85b6f0
MT
554static int das16cs_dio_insn_bits(struct comedi_device *dev,
555 struct comedi_subdevice *s,
556 struct comedi_insn *insn, unsigned int *data)
f0922ec5
DS
557{
558 if (insn->n != 2)
559 return -EINVAL;
560
561 if (data[0]) {
562 s->state &= ~data[0];
563 s->state |= data[0] & data[1];
564
565 outw(s->state, dev->iobase + 16);
566 }
567
568 /* on return, data[1] contains the value of the digital
569 * input and output lines. */
570 data[1] = inw(dev->iobase + 16);
571
572 return 2;
573}
574
0a85b6f0
MT
575static int das16cs_dio_insn_config(struct comedi_device *dev,
576 struct comedi_subdevice *s,
577 struct comedi_insn *insn, unsigned int *data)
f0922ec5
DS
578{
579 int chan = CR_CHAN(insn->chanspec);
580 int bits;
581
582 if (chan < 4)
583 bits = 0x0f;
584 else
585 bits = 0xf0;
586
587 switch (data[0]) {
588 case INSN_CONFIG_DIO_OUTPUT:
589 s->io_bits |= bits;
590 break;
591 case INSN_CONFIG_DIO_INPUT:
592 s->io_bits &= bits;
593 break;
594 case INSN_CONFIG_DIO_QUERY:
595 data[1] =
0a85b6f0 596 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
f0922ec5
DS
597 return insn->n;
598 break;
599 default:
600 return -EINVAL;
601 break;
602 }
603
604 devpriv->status2 &= ~0x00c0;
605 devpriv->status2 |= (s->io_bits & 0xf0) ? 0x0080 : 0;
606 devpriv->status2 |= (s->io_bits & 0x0f) ? 0x0040 : 0;
607
608 outw(devpriv->status2, dev->iobase + 6);
609
610 return insn->n;
611}
612
0a85b6f0
MT
613static int das16cs_timer_insn_read(struct comedi_device *dev,
614 struct comedi_subdevice *s,
615 struct comedi_insn *insn, unsigned int *data)
f0922ec5
DS
616{
617 return -EINVAL;
618}
619
0a85b6f0
MT
620static int das16cs_timer_insn_config(struct comedi_device *dev,
621 struct comedi_subdevice *s,
622 struct comedi_insn *insn,
623 unsigned int *data)
f0922ec5
DS
624{
625 return -EINVAL;
626}
627
628/* PCMCIA stuff */
629
630/*======================================================================
631
632 The following pcmcia code for the pcm-das08 is adapted from the
633 dummy_cs.c driver of the Linux PCMCIA Card Services package.
634
635 The initial developer of the original code is David A. Hinds
636 <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
637 are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
638
639======================================================================*/
640
f0922ec5
DS
641#if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE)
642
f0922ec5
DS
643static void das16cs_pcmcia_config(struct pcmcia_device *link);
644static void das16cs_pcmcia_release(struct pcmcia_device *link);
645static int das16cs_pcmcia_suspend(struct pcmcia_device *p_dev);
646static int das16cs_pcmcia_resume(struct pcmcia_device *p_dev);
647
648/*
649 The attach() and detach() entry points are used to create and destroy
650 "instances" of the driver, where each instance represents everything
651 needed to manage one actual PCMCIA card.
652*/
653
654static int das16cs_pcmcia_attach(struct pcmcia_device *);
655static void das16cs_pcmcia_detach(struct pcmcia_device *);
656
657/*
658 You'll also need to prototype all the functions that will actually
659 be used to talk to your device. See 'memory_cs' for a good example
660 of a fully self-sufficient driver; the other drivers rely more or
661 less on other parts of the kernel.
662*/
663
664/*
665 The dev_info variable is the "key" that is used to match up this
666 device driver with appropriate cards, through the card configuration
667 database.
668*/
669
670static dev_info_t dev_info = "cb_das16_cs";
671
d2755d51 672struct local_info_t {
f0922ec5 673 struct pcmcia_device *link;
f0922ec5
DS
674 int stop;
675 struct bus_operations *bus;
d2755d51 676};
f0922ec5
DS
677
678/*======================================================================
679
680 das16cs_pcmcia_attach() creates an "instance" of the driver, allocating
681 local data structures for one device. The device is registered
682 with Card Services.
683
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.
687
688======================================================================*/
689
690static int das16cs_pcmcia_attach(struct pcmcia_device *link)
691{
d2755d51 692 struct local_info_t *local;
f0922ec5 693
55a19b39 694 dev_dbg(&link->dev, "das16cs_pcmcia_attach()\n");
f0922ec5
DS
695
696 /* Allocate space for private device-specific data */
d2755d51 697 local = kzalloc(sizeof(struct local_info_t), GFP_KERNEL);
f0922ec5
DS
698 if (!local)
699 return -ENOMEM;
700 local->link = link;
701 link->priv = local;
702
703 /* Initialize the pcmcia_device structure */
f0922ec5
DS
704 link->conf.Attributes = 0;
705 link->conf.IntType = INT_MEMORY_AND_IO;
706
707 cur_dev = link;
708
709 das16cs_pcmcia_config(link);
710
711 return 0;
712} /* das16cs_pcmcia_attach */
713
714static void das16cs_pcmcia_detach(struct pcmcia_device *link)
715{
55a19b39 716 dev_dbg(&link->dev, "das16cs_pcmcia_detach\n");
f0922ec5 717
eb8804f6
JMC
718 ((struct local_info_t *)link->priv)->stop = 1;
719 das16cs_pcmcia_release(link);
d2755d51 720 /* This points to the parent struct local_info_t struct */
f0922ec5
DS
721 if (link->priv)
722 kfree(link->priv);
723} /* das16cs_pcmcia_detach */
724
f0922ec5 725
55a19b39
DB
726static 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;
f0922ec5 734
55a19b39 735 /* Do we need to allocate an interrupt? */
eb14120f 736 p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
55a19b39
DB
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;
f0922ec5 754 }
55a19b39
DB
755 /* This reserves IO space but doesn't actually enable it */
756 return pcmcia_request_io(p_dev, &p_dev->io);
757 }
f0922ec5 758
55a19b39
DB
759 return 0;
760}
761
762static void das16cs_pcmcia_config(struct pcmcia_device *link)
763{
55a19b39 764 int ret;
f0922ec5 765
55a19b39 766 dev_dbg(&link->dev, "das16cs_pcmcia_config\n");
c3744138 767
55a19b39
DB
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;
f0922ec5
DS
772 }
773
eb14120f
DB
774 if (!link->irq)
775 goto failed;
776
f0922ec5
DS
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 */
55a19b39
DB
782 ret = pcmcia_request_configuration(link, &link->conf);
783 if (ret)
784 goto failed;
f0922ec5 785
f0922ec5 786 /* Finally, report what we've done */
eb8804f6 787 dev_info(&link->dev, "index 0x%02x", link->conf.ConfigIndex);
f0922ec5 788 if (link->conf.Attributes & CONF_ENABLE_IRQ)
eb14120f 789 printk(", irq %u", link->irq);
9a017a91
DB
790 if (link->resource[0])
791 printk(", io %pR", link->resource[0]);
792 if (link->resource[1])
793 printk(", io %pR", link->resource[1]);
f0922ec5
DS
794 printk("\n");
795
796 return;
797
55a19b39 798failed:
f0922ec5
DS
799 das16cs_pcmcia_release(link);
800} /* das16cs_pcmcia_config */
801
802static void das16cs_pcmcia_release(struct pcmcia_device *link)
803{
55a19b39 804 dev_dbg(&link->dev, "das16cs_pcmcia_release\n");
f0922ec5
DS
805 pcmcia_disable_device(link);
806} /* das16cs_pcmcia_release */
807
808static int das16cs_pcmcia_suspend(struct pcmcia_device *link)
809{
d2755d51 810 struct local_info_t *local = link->priv;
f0922ec5
DS
811
812 /* Mark the device as stopped, to block IO until later */
813 local->stop = 1;
814
815 return 0;
816} /* das16cs_pcmcia_suspend */
817
818static int das16cs_pcmcia_resume(struct pcmcia_device *link)
819{
d2755d51 820 struct local_info_t *local = link->priv;
f0922ec5
DS
821
822 local->stop = 0;
823 return 0;
824} /* das16cs_pcmcia_resume */
825
826/*====================================================================*/
827
828static struct pcmcia_device_id das16cs_id_table[] = {
829 PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0039),
830 PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4009),
831 PCMCIA_DEVICE_NULL
832};
833
834MODULE_DEVICE_TABLE(pcmcia, das16cs_id_table);
6c7f8196
AK
835MODULE_AUTHOR("David A. Schleef <ds@schleef.org>");
836MODULE_DESCRIPTION("Comedi driver for Computer Boards PC-CARD DAS16/16");
837MODULE_LICENSE("GPL");
f0922ec5
DS
838
839struct pcmcia_driver das16cs_driver = {
840 .probe = das16cs_pcmcia_attach,
841 .remove = das16cs_pcmcia_detach,
842 .suspend = das16cs_pcmcia_suspend,
843 .resume = das16cs_pcmcia_resume,
844 .id_table = das16cs_id_table,
845 .owner = THIS_MODULE,
846 .drv = {
0a85b6f0 847 .name = dev_info,
f0922ec5
DS
848 },
849};
850
851static int __init init_das16cs_pcmcia_cs(void)
852{
f0922ec5
DS
853 pcmcia_register_driver(&das16cs_driver);
854 return 0;
855}
856
857static void __exit exit_das16cs_pcmcia_cs(void)
858{
55a19b39 859 pr_debug("das16cs_pcmcia_cs: unloading\n");
f0922ec5
DS
860 pcmcia_unregister_driver(&das16cs_driver);
861}
862
863int __init init_module(void)
864{
865 int ret;
866
867 ret = init_das16cs_pcmcia_cs();
868 if (ret < 0)
869 return ret;
870
871 return comedi_driver_register(&driver_das16cs);
872}
873
874void __exit cleanup_module(void)
875{
876 exit_das16cs_pcmcia_cs();
877 comedi_driver_unregister(&driver_das16cs);
878}
879
880#else
881COMEDI_INITCLEANUP(driver_das16cs);
2696fb57 882#endif /* CONFIG_PCMCIA */