1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50#include "../comedidev.h"
51
52#include <pcmcia/cs_types.h>
53#include <pcmcia/cs.h>
54#include <pcmcia/cistpl.h>
55#include <pcmcia/cisreg.h>
56#include <pcmcia/ds.h>
57
58
59
60
61
62
63
64
65
66#ifdef PCMCIA_DEBUG
67static int pc_debug = PCMCIA_DEBUG;
68module_param(pc_debug, int, 0644);
69#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
70static char *version = "quatech_daqp_cs.c 1.10 2003/04/21 (Brent Baccala)";
71#else
72#define DEBUG(n, args...)
73#endif
74
75
76#define MAX_DEV 4
77
78struct local_info_t {
79 struct pcmcia_device *link;
80 dev_node_t node;
81 int stop;
82 int table_index;
83 char board_name[32];
84
85 enum { semaphore, buffer } interrupt_mode;
86
87 struct semaphore eos;
88
89 struct comedi_device *dev;
90 struct comedi_subdevice *s;
91 int count;
92};
93
94
95
96static struct local_info_t *dev_table[MAX_DEV] = { NULL, };
97
98
99
100#define DAQP_FIFO_SIZE 4096
101
102#define DAQP_FIFO 0
103#define DAQP_SCANLIST 1
104#define DAQP_CONTROL 2
105#define DAQP_STATUS 2
106#define DAQP_DIGITAL_IO 3
107#define DAQP_PACER_LOW 4
108#define DAQP_PACER_MID 5
109#define DAQP_PACER_HIGH 6
110#define DAQP_COMMAND 7
111#define DAQP_DA 8
112#define DAQP_TIMER 10
113#define DAQP_AUX 15
114
115#define DAQP_SCANLIST_DIFFERENTIAL 0x4000
116#define DAQP_SCANLIST_GAIN(x) ((x)<<12)
117#define DAQP_SCANLIST_CHANNEL(x) ((x)<<8)
118#define DAQP_SCANLIST_START 0x0080
119#define DAQP_SCANLIST_EXT_GAIN(x) ((x)<<4)
120#define DAQP_SCANLIST_EXT_CHANNEL(x) (x)
121
122#define DAQP_CONTROL_PACER_100kHz 0xc0
123#define DAQP_CONTROL_PACER_1MHz 0x80
124#define DAQP_CONTROL_PACER_5MHz 0x40
125#define DAQP_CONTROL_PACER_EXTERNAL 0x00
126#define DAQP_CONTORL_EXPANSION 0x20
127#define DAQP_CONTROL_EOS_INT_ENABLE 0x10
128#define DAQP_CONTROL_FIFO_INT_ENABLE 0x08
129#define DAQP_CONTROL_TRIGGER_ONESHOT 0x00
130#define DAQP_CONTROL_TRIGGER_CONTINUOUS 0x04
131#define DAQP_CONTROL_TRIGGER_INTERNAL 0x00
132#define DAQP_CONTROL_TRIGGER_EXTERNAL 0x02
133#define DAQP_CONTROL_TRIGGER_RISING 0x00
134#define DAQP_CONTROL_TRIGGER_FALLING 0x01
135
136#define DAQP_STATUS_IDLE 0x80
137#define DAQP_STATUS_RUNNING 0x40
138#define DAQP_STATUS_EVENTS 0x38
139#define DAQP_STATUS_DATA_LOST 0x20
140#define DAQP_STATUS_END_OF_SCAN 0x10
141#define DAQP_STATUS_FIFO_THRESHOLD 0x08
142#define DAQP_STATUS_FIFO_FULL 0x04
143#define DAQP_STATUS_FIFO_NEARFULL 0x02
144#define DAQP_STATUS_FIFO_EMPTY 0x01
145
146#define DAQP_COMMAND_ARM 0x80
147#define DAQP_COMMAND_RSTF 0x40
148#define DAQP_COMMAND_RSTQ 0x20
149#define DAQP_COMMAND_STOP 0x10
150#define DAQP_COMMAND_LATCH 0x08
151#define DAQP_COMMAND_100kHz 0x00
152#define DAQP_COMMAND_50kHz 0x02
153#define DAQP_COMMAND_25kHz 0x04
154#define DAQP_COMMAND_FIFO_DATA 0x01
155#define DAQP_COMMAND_FIFO_PROGRAM 0x00
156
157#define DAQP_AUX_TRIGGER_TTL 0x00
158#define DAQP_AUX_TRIGGER_ANALOG 0x80
159#define DAQP_AUX_TRIGGER_PRETRIGGER 0x40
160#define DAQP_AUX_TIMER_INT_ENABLE 0x20
161#define DAQP_AUX_TIMER_RELOAD 0x00
162#define DAQP_AUX_TIMER_PAUSE 0x08
163#define DAQP_AUX_TIMER_GO 0x10
164#define DAQP_AUX_TIMER_GO_EXTERNAL 0x18
165#define DAQP_AUX_TIMER_EXTERNAL_SRC 0x04
166#define DAQP_AUX_TIMER_INTERNAL_SRC 0x00
167#define DAQP_AUX_DA_DIRECT 0x00
168#define DAQP_AUX_DA_OVERFLOW 0x01
169#define DAQP_AUX_DA_EXTERNAL 0x02
170#define DAQP_AUX_DA_PACER 0x03
171
172#define DAQP_AUX_RUNNING 0x80
173#define DAQP_AUX_TRIGGERED 0x40
174#define DAQP_AUX_DA_BUFFER 0x20
175#define DAQP_AUX_TIMER_OVERFLOW 0x10
176#define DAQP_AUX_CONVERSION 0x08
177#define DAQP_AUX_DATA_LOST 0x04
178#define DAQP_AUX_FIFO_NEARFULL 0x02
179#define DAQP_AUX_FIFO_EMPTY 0x01
180
181
182
183
184
185
186static const struct comedi_lrange range_daqp_ai = { 4, {
187 BIP_RANGE(10),
188 BIP_RANGE(5),
189 BIP_RANGE(2.5),
190 BIP_RANGE(1.25)
191 }
192};
193
194static const struct comedi_lrange range_daqp_ao = { 1, {BIP_RANGE(5)} };
195
196
197
198
199
200static int daqp_attach(struct comedi_device *dev, struct comedi_devconfig *it);
201static int daqp_detach(struct comedi_device *dev);
202static struct comedi_driver driver_daqp = {
203 .driver_name = "quatech_daqp_cs",
204 .module = THIS_MODULE,
205 .attach = daqp_attach,
206 .detach = daqp_detach,
207};
208
209#ifdef DAQP_DEBUG
210
211static void daqp_dump(struct comedi_device *dev)
212{
213 printk("DAQP: status %02x; aux status %02x\n",
214 inb(dev->iobase + DAQP_STATUS), inb(dev->iobase + DAQP_AUX));
215}
216
217static void hex_dump(char *str, void *ptr, int len)
218{
219 unsigned char *cptr = ptr;
220 int i;
221
222 printk(str);
223
224 for (i = 0; i < len; i++) {
225 if (i % 16 == 0) {
226 printk("\n0x%08x:", (unsigned int)cptr);
227 }
228 printk(" %02x", *(cptr++));
229 }
230 printk("\n");
231}
232
233#endif
234
235
236
237static int daqp_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
238{
239 struct local_info_t *local = (struct local_info_t *)s->private;
240
241 if (local->stop) {
242 return -EIO;
243 }
244
245 outb(DAQP_COMMAND_STOP, dev->iobase + DAQP_COMMAND);
246
247
248
249
250 local->interrupt_mode = semaphore;
251
252 return 0;
253}
254
255
256
257
258
259
260
261
262
263
264
265static void daqp_interrupt(int irq, void *dev_id)
266{
267 struct local_info_t *local = (struct local_info_t *)dev_id;
268 struct comedi_device *dev;
269 struct comedi_subdevice *s;
270 int loop_limit = 10000;
271 int status;
272
273 if (local == NULL) {
274 printk(KERN_WARNING
275 "daqp_interrupt(): irq %d for unknown device.\n", irq);
276 return;
277 }
278
279 dev = local->dev;
280 if (dev == NULL) {
281 printk(KERN_WARNING "daqp_interrupt(): NULL comedi_device.\n");
282 return;
283 }
284
285 if (!dev->attached) {
286 printk(KERN_WARNING
287 "daqp_interrupt(): struct comedi_device not yet attached.\n");
288 return;
289 }
290
291 s = local->s;
292 if (s == NULL) {
293 printk(KERN_WARNING
294 "daqp_interrupt(): NULL comedi_subdevice.\n");
295 return;
296 }
297
298 if ((struct local_info_t *)s->private != local) {
299 printk(KERN_WARNING
300 "daqp_interrupt(): invalid comedi_subdevice.\n");
301 return;
302 }
303
304 switch (local->interrupt_mode) {
305
306 case semaphore:
307
308 up(&local->eos);
309 break;
310
311 case buffer:
312
313 while (!((status = inb(dev->iobase + DAQP_STATUS))
314 & DAQP_STATUS_FIFO_EMPTY)) {
315
316 short data;
317
318 if (status & DAQP_STATUS_DATA_LOST) {
319 s->async->events |=
320 COMEDI_CB_EOA | COMEDI_CB_OVERFLOW;
321 printk("daqp: data lost\n");
322 daqp_ai_cancel(dev, s);
323 break;
324 }
325
326 data = inb(dev->iobase + DAQP_FIFO);
327 data |= inb(dev->iobase + DAQP_FIFO) << 8;
328 data ^= 0x8000;
329
330 comedi_buf_put(s->async, data);
331
332
333
334
335
336 if (local->count > 0) {
337 local->count--;
338 if (local->count == 0) {
339 daqp_ai_cancel(dev, s);
340 s->async->events |= COMEDI_CB_EOA;
341 break;
342 }
343 }
344
345 if ((loop_limit--) <= 0)
346 break;
347 }
348
349 if (loop_limit <= 0) {
350 printk(KERN_WARNING
351 "loop_limit reached in daqp_interrupt()\n");
352 daqp_ai_cancel(dev, s);
353 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
354 }
355
356 s->async->events |= COMEDI_CB_BLOCK;
357
358 comedi_event(dev, s);
359 }
360}
361
362
363
364static int daqp_ai_insn_read(struct comedi_device *dev,
365 struct comedi_subdevice *s,
366 struct comedi_insn *insn, unsigned int *data)
367{
368 struct local_info_t *local = (struct local_info_t *)s->private;
369 int i;
370 int v;
371 int counter = 10000;
372
373 if (local->stop) {
374 return -EIO;
375 }
376
377
378 daqp_ai_cancel(dev, s);
379
380 outb(0, dev->iobase + DAQP_AUX);
381
382
383 outb(DAQP_COMMAND_RSTQ, dev->iobase + DAQP_COMMAND);
384
385
386
387 v = DAQP_SCANLIST_CHANNEL(CR_CHAN(insn->chanspec))
388 | DAQP_SCANLIST_GAIN(CR_RANGE(insn->chanspec));
389
390 if (CR_AREF(insn->chanspec) == AREF_DIFF) {
391 v |= DAQP_SCANLIST_DIFFERENTIAL;
392 }
393
394 v |= DAQP_SCANLIST_START;
395
396 outb(v & 0xff, dev->iobase + DAQP_SCANLIST);
397 outb(v >> 8, dev->iobase + DAQP_SCANLIST);
398
399
400
401 outb(DAQP_COMMAND_RSTF, dev->iobase + DAQP_COMMAND);
402
403
404
405 v = DAQP_CONTROL_TRIGGER_ONESHOT | DAQP_CONTROL_TRIGGER_INTERNAL
406 | DAQP_CONTROL_PACER_100kHz | DAQP_CONTROL_EOS_INT_ENABLE;
407
408 outb(v, dev->iobase + DAQP_CONTROL);
409
410
411
412
413
414 while (--counter
415 && (inb(dev->iobase + DAQP_STATUS) & DAQP_STATUS_EVENTS)) ;
416 if (!counter) {
417 printk("daqp: couldn't clear interrupts in status register\n");
418 return -1;
419 }
420
421
422 sema_init(&local->eos, 0);
423 local->interrupt_mode = semaphore;
424 local->dev = dev;
425 local->s = s;
426
427 for (i = 0; i < insn->n; i++) {
428
429
430 outb(DAQP_COMMAND_ARM | DAQP_COMMAND_FIFO_DATA,
431 dev->iobase + DAQP_COMMAND);
432
433
434
435 if (down_interruptible(&local->eos))
436 return -EINTR;
437
438 data[i] = inb(dev->iobase + DAQP_FIFO);
439 data[i] |= inb(dev->iobase + DAQP_FIFO) << 8;
440 data[i] ^= 0x8000;
441 }
442
443 return insn->n;
444}
445
446
447
448
449
450
451
452
453static int daqp_ns_to_timer(unsigned int *ns, int round)
454{
455 int timer;
456
457 timer = *ns / 200;
458 *ns = timer * 200;
459
460 return timer;
461}
462
463
464
465
466
467
468
469
470
471static int daqp_ai_cmdtest(struct comedi_device *dev,
472 struct comedi_subdevice *s, struct comedi_cmd *cmd)
473{
474 int err = 0;
475 int tmp;
476
477
478
479 tmp = cmd->start_src;
480 cmd->start_src &= TRIG_NOW;
481 if (!cmd->start_src || tmp != cmd->start_src)
482 err++;
483
484 tmp = cmd->scan_begin_src;
485 cmd->scan_begin_src &= TRIG_TIMER | TRIG_FOLLOW;
486 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
487 err++;
488
489 tmp = cmd->convert_src;
490 cmd->convert_src &= TRIG_TIMER | TRIG_NOW;
491 if (!cmd->convert_src || tmp != cmd->convert_src)
492 err++;
493
494 tmp = cmd->scan_end_src;
495 cmd->scan_end_src &= TRIG_COUNT;
496 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
497 err++;
498
499 tmp = cmd->stop_src;
500 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
501 if (!cmd->stop_src || tmp != cmd->stop_src)
502 err++;
503
504 if (err)
505 return 1;
506
507
508
509
510 if (cmd->scan_begin_src != TRIG_TIMER &&
511 cmd->scan_begin_src != TRIG_FOLLOW)
512 err++;
513 if (cmd->convert_src != TRIG_NOW && cmd->convert_src != TRIG_TIMER)
514 err++;
515 if (cmd->scan_begin_src == TRIG_FOLLOW && cmd->convert_src == TRIG_NOW)
516 err++;
517 if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
518 err++;
519
520 if (err)
521 return 2;
522
523
524
525 if (cmd->start_arg != 0) {
526 cmd->start_arg = 0;
527 err++;
528 }
529#define MAX_SPEED 10000
530
531 if (cmd->scan_begin_src == TRIG_TIMER
532 && cmd->scan_begin_arg < MAX_SPEED) {
533 cmd->scan_begin_arg = MAX_SPEED;
534 err++;
535 }
536
537
538
539
540
541
542 if (cmd->scan_begin_src == TRIG_TIMER && cmd->convert_src == TRIG_TIMER
543 && cmd->scan_begin_arg != cmd->convert_arg * cmd->scan_end_arg) {
544 err++;
545 }
546
547 if (cmd->convert_src == TRIG_TIMER && cmd->convert_arg < MAX_SPEED) {
548 cmd->convert_arg = MAX_SPEED;
549 err++;
550 }
551
552 if (cmd->scan_end_arg != cmd->chanlist_len) {
553 cmd->scan_end_arg = cmd->chanlist_len;
554 err++;
555 }
556 if (cmd->stop_src == TRIG_COUNT) {
557 if (cmd->stop_arg > 0x00ffffff) {
558 cmd->stop_arg = 0x00ffffff;
559 err++;
560 }
561 } else {
562
563 if (cmd->stop_arg != 0) {
564 cmd->stop_arg = 0;
565 err++;
566 }
567 }
568
569 if (err)
570 return 3;
571
572
573
574 if (cmd->scan_begin_src == TRIG_TIMER) {
575 tmp = cmd->scan_begin_arg;
576 daqp_ns_to_timer(&cmd->scan_begin_arg,
577 cmd->flags & TRIG_ROUND_MASK);
578 if (tmp != cmd->scan_begin_arg)
579 err++;
580 }
581
582 if (cmd->convert_src == TRIG_TIMER) {
583 tmp = cmd->convert_arg;
584 daqp_ns_to_timer(&cmd->convert_arg,
585 cmd->flags & TRIG_ROUND_MASK);
586 if (tmp != cmd->convert_arg)
587 err++;
588 }
589
590 if (err)
591 return 4;
592
593 return 0;
594}
595
596static int daqp_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
597{
598 struct local_info_t *local = (struct local_info_t *)s->private;
599 struct comedi_cmd *cmd = &s->async->cmd;
600 int counter = 100;
601 int scanlist_start_on_every_entry;
602 int threshold;
603
604 int i;
605 int v;
606
607 if (local->stop) {
608 return -EIO;
609 }
610
611
612 daqp_ai_cancel(dev, s);
613
614 outb(0, dev->iobase + DAQP_AUX);
615
616
617 outb(DAQP_COMMAND_RSTQ, dev->iobase + DAQP_COMMAND);
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632 if (cmd->convert_src == TRIG_TIMER) {
633 int counter = daqp_ns_to_timer(&cmd->convert_arg,
634 cmd->flags & TRIG_ROUND_MASK);
635 outb(counter & 0xff, dev->iobase + DAQP_PACER_LOW);
636 outb((counter >> 8) & 0xff, dev->iobase + DAQP_PACER_MID);
637 outb((counter >> 16) & 0xff, dev->iobase + DAQP_PACER_HIGH);
638 scanlist_start_on_every_entry = 1;
639 } else {
640 int counter = daqp_ns_to_timer(&cmd->scan_begin_arg,
641 cmd->flags & TRIG_ROUND_MASK);
642 outb(counter & 0xff, dev->iobase + DAQP_PACER_LOW);
643 outb((counter >> 8) & 0xff, dev->iobase + DAQP_PACER_MID);
644 outb((counter >> 16) & 0xff, dev->iobase + DAQP_PACER_HIGH);
645 scanlist_start_on_every_entry = 0;
646 }
647
648
649
650 for (i = 0; i < cmd->chanlist_len; i++) {
651
652 int chanspec = cmd->chanlist[i];
653
654
655
656 v = DAQP_SCANLIST_CHANNEL(CR_CHAN(chanspec))
657 | DAQP_SCANLIST_GAIN(CR_RANGE(chanspec));
658
659 if (CR_AREF(chanspec) == AREF_DIFF) {
660 v |= DAQP_SCANLIST_DIFFERENTIAL;
661 }
662
663 if (i == 0 || scanlist_start_on_every_entry) {
664 v |= DAQP_SCANLIST_START;
665 }
666
667 outb(v & 0xff, dev->iobase + DAQP_SCANLIST);
668 outb(v >> 8, dev->iobase + DAQP_SCANLIST);
669 }
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737 if (cmd->stop_src == TRIG_COUNT) {
738 local->count = cmd->stop_arg * cmd->scan_end_arg;
739 threshold = 2 * local->count;
740 while (threshold > DAQP_FIFO_SIZE * 3 / 4)
741 threshold /= 2;
742 } else {
743 local->count = -1;
744 threshold = DAQP_FIFO_SIZE / 2;
745 }
746
747
748
749 outb(DAQP_COMMAND_RSTF, dev->iobase + DAQP_COMMAND);
750
751
752
753
754
755
756
757
758
759 outb(0x00, dev->iobase + DAQP_FIFO);
760 outb(0x00, dev->iobase + DAQP_FIFO);
761
762 outb((DAQP_FIFO_SIZE - threshold) & 0xff, dev->iobase + DAQP_FIFO);
763 outb((DAQP_FIFO_SIZE - threshold) >> 8, dev->iobase + DAQP_FIFO);
764
765
766
767 v = DAQP_CONTROL_TRIGGER_CONTINUOUS | DAQP_CONTROL_TRIGGER_INTERNAL
768 | DAQP_CONTROL_PACER_5MHz | DAQP_CONTROL_FIFO_INT_ENABLE;
769
770 outb(v, dev->iobase + DAQP_CONTROL);
771
772
773
774
775
776 while (--counter
777 && (inb(dev->iobase + DAQP_STATUS) & DAQP_STATUS_EVENTS)) ;
778 if (!counter) {
779 printk("daqp: couldn't clear interrupts in status register\n");
780 return -1;
781 }
782
783 local->interrupt_mode = buffer;
784 local->dev = dev;
785 local->s = s;
786
787
788 outb(DAQP_COMMAND_ARM | DAQP_COMMAND_FIFO_DATA,
789 dev->iobase + DAQP_COMMAND);
790
791 return 0;
792}
793
794
795
796static int daqp_ao_insn_write(struct comedi_device *dev,
797 struct comedi_subdevice *s,
798 struct comedi_insn *insn, unsigned int *data)
799{
800 struct local_info_t *local = (struct local_info_t *)s->private;
801 int d;
802 unsigned int chan;
803
804 if (local->stop) {
805 return -EIO;
806 }
807
808 chan = CR_CHAN(insn->chanspec);
809 d = data[0];
810 d &= 0x0fff;
811 d ^= 0x0800;
812 d |= chan << 12;
813
814
815 outb(0, dev->iobase + DAQP_AUX);
816
817 outw(d, dev->iobase + DAQP_DA);
818
819 return 1;
820}
821
822
823
824static int daqp_di_insn_read(struct comedi_device *dev,
825 struct comedi_subdevice *s,
826 struct comedi_insn *insn, unsigned int *data)
827{
828 struct local_info_t *local = (struct local_info_t *)s->private;
829
830 if (local->stop) {
831 return -EIO;
832 }
833
834 data[0] = inb(dev->iobase + DAQP_DIGITAL_IO);
835
836 return 1;
837}
838
839
840
841static int daqp_do_insn_write(struct comedi_device *dev,
842 struct comedi_subdevice *s,
843 struct comedi_insn *insn, unsigned int *data)
844{
845 struct local_info_t *local = (struct local_info_t *)s->private;
846
847 if (local->stop) {
848 return -EIO;
849 }
850
851 outw(data[0] & 0xf, dev->iobase + DAQP_DIGITAL_IO);
852
853 return 1;
854}
855
856
857
858
859
860
861
862static int daqp_attach(struct comedi_device *dev, struct comedi_devconfig *it)
863{
864 int ret;
865 struct local_info_t *local = dev_table[it->options[0]];
866 tuple_t tuple;
867 int i;
868 struct comedi_subdevice *s;
869
870 if (it->options[0] < 0 || it->options[0] >= MAX_DEV || !local) {
871 printk("comedi%d: No such daqp device %d\n",
872 dev->minor, it->options[0]);
873 return -EIO;
874 }
875
876
877
878
879
880
881
882
883
884 strcpy(local->board_name, "DAQP");
885 dev->board_name = local->board_name;
886
887 tuple.DesiredTuple = CISTPL_VERS_1;
888 if (pcmcia_get_first_tuple(local->link, &tuple) == 0) {
889 u_char buf[128];
890
891 buf[0] = buf[sizeof(buf) - 1] = 0;
892 tuple.TupleData = buf;
893 tuple.TupleDataMax = sizeof(buf);
894 tuple.TupleOffset = 2;
895 if (pcmcia_get_tuple_data(local->link, &tuple) == 0) {
896
897 for (i = 0; i < tuple.TupleDataLen - 4; i++)
898 if (buf[i] == 0)
899 break;
900 for (i++; i < tuple.TupleDataLen - 4; i++)
901 if (buf[i] == 0)
902 break;
903 i++;
904 if ((i < tuple.TupleDataLen - 4)
905 && (strncmp(buf + i, "DAQP", 4) == 0)) {
906 strncpy(local->board_name, buf + i,
907 sizeof(local->board_name));
908 }
909 }
910 }
911
912 dev->iobase = local->link->io.BasePort1;
913
914 ret = alloc_subdevices(dev, 4);
915 if (ret < 0)
916 return ret;
917
918 printk("comedi%d: attaching daqp%d (io 0x%04lx)\n",
919 dev->minor, it->options[0], dev->iobase);
920
921 s = dev->subdevices + 0;
922 dev->read_subdev = s;
923 s->private = local;
924 s->type = COMEDI_SUBD_AI;
925 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
926 s->n_chan = 8;
927 s->len_chanlist = 2048;
928 s->maxdata = 0xffff;
929 s->range_table = &range_daqp_ai;
930 s->insn_read = daqp_ai_insn_read;
931 s->do_cmdtest = daqp_ai_cmdtest;
932 s->do_cmd = daqp_ai_cmd;
933 s->cancel = daqp_ai_cancel;
934
935 s = dev->subdevices + 1;
936 dev->write_subdev = s;
937 s->private = local;
938 s->type = COMEDI_SUBD_AO;
939 s->subdev_flags = SDF_WRITEABLE;
940 s->n_chan = 2;
941 s->len_chanlist = 1;
942 s->maxdata = 0x0fff;
943 s->range_table = &range_daqp_ao;
944 s->insn_write = daqp_ao_insn_write;
945
946 s = dev->subdevices + 2;
947 s->private = local;
948 s->type = COMEDI_SUBD_DI;
949 s->subdev_flags = SDF_READABLE;
950 s->n_chan = 1;
951 s->len_chanlist = 1;
952 s->insn_read = daqp_di_insn_read;
953
954 s = dev->subdevices + 3;
955 s->private = local;
956 s->type = COMEDI_SUBD_DO;
957 s->subdev_flags = SDF_WRITEABLE;
958 s->n_chan = 1;
959 s->len_chanlist = 1;
960 s->insn_write = daqp_do_insn_write;
961
962 return 1;
963}
964
965
966
967
968
969static int daqp_detach(struct comedi_device *dev)
970{
971 printk("comedi%d: detaching daqp\n", dev->minor);
972
973 return 0;
974}
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022static void daqp_cs_config(struct pcmcia_device *link);
1023static void daqp_cs_release(struct pcmcia_device *link);
1024static int daqp_cs_suspend(struct pcmcia_device *p_dev);
1025static int daqp_cs_resume(struct pcmcia_device *p_dev);
1026
1027
1028
1029
1030
1031
1032
1033static int daqp_cs_attach(struct pcmcia_device *);
1034static void daqp_cs_detach(struct pcmcia_device *);
1035
1036
1037
1038
1039
1040
1041
1042static const dev_info_t dev_info = "quatech_daqp_cs";
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056static int daqp_cs_attach(struct pcmcia_device *link)
1057{
1058 struct local_info_t *local;
1059 int i;
1060
1061 DEBUG(0, "daqp_cs_attach()\n");
1062
1063 for (i = 0; i < MAX_DEV; i++)
1064 if (dev_table[i] == NULL)
1065 break;
1066 if (i == MAX_DEV) {
1067 printk(KERN_NOTICE "daqp_cs: no devices available\n");
1068 return -ENODEV;
1069 }
1070
1071
1072 local = kzalloc(sizeof(struct local_info_t), GFP_KERNEL);
1073 if (!local)
1074 return -ENOMEM;
1075
1076 local->table_index = i;
1077 dev_table[i] = local;
1078 local->link = link;
1079 link->priv = local;
1080
1081
1082 link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
1083 link->irq.IRQInfo1 = IRQ_LEVEL_ID;
1084 link->irq.Handler = daqp_interrupt;
1085 link->irq.Instance = local;
1086
1087
1088
1089
1090
1091
1092
1093
1094 link->conf.Attributes = 0;
1095 link->conf.IntType = INT_MEMORY_AND_IO;
1096
1097 daqp_cs_config(link);
1098
1099 return 0;
1100}
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111static void daqp_cs_detach(struct pcmcia_device *link)
1112{
1113 struct local_info_t *dev = link->priv;
1114
1115 DEBUG(0, "daqp_cs_detach(0x%p)\n", link);
1116
1117 if (link->dev_node) {
1118 dev->stop = 1;
1119 daqp_cs_release(link);
1120 }
1121
1122
1123 dev_table[dev->table_index] = NULL;
1124 if (dev)
1125 kfree(dev);
1126
1127}
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137static void daqp_cs_config(struct pcmcia_device *link)
1138{
1139 struct local_info_t *dev = link->priv;
1140 tuple_t tuple;
1141 cisparse_t parse;
1142 int last_ret;
1143 u_char buf[64];
1144
1145 DEBUG(0, "daqp_cs_config(0x%p)\n", link);
1146
1147
1148
1149
1150
1151 tuple.DesiredTuple = CISTPL_CONFIG;
1152 tuple.Attributes = 0;
1153 tuple.TupleData = buf;
1154 tuple.TupleDataMax = sizeof(buf);
1155 tuple.TupleOffset = 0;
1156
1157 last_ret = pcmcia_get_first_tuple(link, &tuple);
1158 if (last_ret) {
1159 cs_error(link, GetFirstTuple, last_ret);
1160 goto cs_failed;
1161 }
1162
1163 last_ret = pcmcia_get_tuple_data(link, &tuple);
1164 if (last_ret) {
1165 cs_error(link, GetTupleData, last_ret);
1166 goto cs_failed;
1167 }
1168
1169 last_ret = pcmcia_parse_tuple(&tuple, &parse);
1170 if (last_ret) {
1171 cs_error(link, ParseTuple, last_ret);
1172 goto cs_failed;
1173 }
1174 link->conf.ConfigBase = parse.config.base;
1175 link->conf.Present = parse.config.rmask[0];
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189 tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
1190 last_ret = pcmcia_get_first_tuple(link, &tuple);
1191 if (last_ret) {
1192 cs_error(link, GetFirstTuple, last_ret);
1193 goto cs_failed;
1194 }
1195
1196 while (1) {
1197 cistpl_cftable_entry_t dflt = { 0 };
1198 cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
1199 if (pcmcia_get_tuple_data(link, &tuple))
1200 goto next_entry;
1201 if (pcmcia_parse_tuple(&tuple, &parse))
1202 goto next_entry;
1203
1204 if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
1205 dflt = *cfg;
1206 if (cfg->index == 0)
1207 goto next_entry;
1208 link->conf.ConfigIndex = cfg->index;
1209
1210
1211 if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
1212 link->conf.Attributes |= CONF_ENABLE_IRQ;
1213
1214
1215 link->io.NumPorts1 = link->io.NumPorts2 = 0;
1216 if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
1217 cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
1218 link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
1219 if (!(io->flags & CISTPL_IO_8BIT))
1220 link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
1221 if (!(io->flags & CISTPL_IO_16BIT))
1222 link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
1223 link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
1224 link->io.BasePort1 = io->win[0].base;
1225 link->io.NumPorts1 = io->win[0].len;
1226 if (io->nwin > 1) {
1227 link->io.Attributes2 = link->io.Attributes1;
1228 link->io.BasePort2 = io->win[1].base;
1229 link->io.NumPorts2 = io->win[1].len;
1230 }
1231 }
1232
1233
1234 if (pcmcia_request_io(link, &link->io))
1235 goto next_entry;
1236
1237
1238 break;
1239
1240next_entry:
1241 last_ret = pcmcia_get_next_tuple(link, &tuple);
1242 if (last_ret) {
1243 cs_error(link, GetNextTuple, last_ret);
1244 goto cs_failed;
1245 }
1246 }
1247
1248
1249
1250
1251
1252
1253 if (link->conf.Attributes & CONF_ENABLE_IRQ) {
1254 last_ret = pcmcia_request_irq(link, &link->irq);
1255 if (last_ret) {
1256 cs_error(link, RequestIRQ, last_ret);
1257 goto cs_failed;
1258 }
1259 }
1260
1261
1262
1263
1264
1265
1266 last_ret = pcmcia_request_configuration(link, &link->conf);
1267 if (last_ret) {
1268 cs_error(link, RequestConfiguration, last_ret);
1269 goto cs_failed;
1270 }
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280 sprintf(dev->node.dev_name, "quatech_daqp_cs");
1281 dev->node.major = dev->node.minor = 0;
1282 link->dev_node = &dev->node;
1283
1284
1285 printk(KERN_INFO "%s: index 0x%02x",
1286 dev->node.dev_name, link->conf.ConfigIndex);
1287 if (link->conf.Attributes & CONF_ENABLE_IRQ)
1288 printk(", irq %u", link->irq.AssignedIRQ);
1289 if (link->io.NumPorts1)
1290 printk(", io 0x%04x-0x%04x", link->io.BasePort1,
1291 link->io.BasePort1 + link->io.NumPorts1 - 1);
1292 if (link->io.NumPorts2)
1293 printk(" & 0x%04x-0x%04x", link->io.BasePort2,
1294 link->io.BasePort2 + link->io.NumPorts2 - 1);
1295 printk("\n");
1296
1297 return;
1298
1299cs_failed:
1300 daqp_cs_release(link);
1301
1302}
1303
1304static void daqp_cs_release(struct pcmcia_device *link)
1305{
1306 DEBUG(0, "daqp_cs_release(0x%p)\n", link);
1307
1308 pcmcia_disable_device(link);
1309}
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323static int daqp_cs_suspend(struct pcmcia_device *link)
1324{
1325 struct local_info_t *local = link->priv;
1326
1327
1328 local->stop = 1;
1329 return 0;
1330}
1331
1332static int daqp_cs_resume(struct pcmcia_device *link)
1333{
1334 struct local_info_t *local = link->priv;
1335
1336 local->stop = 0;
1337
1338 return 0;
1339}
1340
1341
1342
1343#ifdef MODULE
1344
1345static struct pcmcia_device_id daqp_cs_id_table[] = {
1346 PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0027),
1347 PCMCIA_DEVICE_NULL
1348};
1349
1350MODULE_DEVICE_TABLE(pcmcia, daqp_cs_id_table);
1351
1352struct pcmcia_driver daqp_cs_driver = {
1353 .probe = daqp_cs_attach,
1354 .remove = daqp_cs_detach,
1355 .suspend = daqp_cs_suspend,
1356 .resume = daqp_cs_resume,
1357 .id_table = daqp_cs_id_table,
1358 .owner = THIS_MODULE,
1359 .drv = {
1360 .name = dev_info,
1361 },
1362};
1363
1364int __init init_module(void)
1365{
1366 DEBUG(0, "%s\n", version);
1367 pcmcia_register_driver(&daqp_cs_driver);
1368 comedi_driver_register(&driver_daqp);
1369 return 0;
1370}
1371
1372void __exit cleanup_module(void)
1373{
1374 DEBUG(0, "daqp_cs: unloading\n");
1375 comedi_driver_unregister(&driver_daqp);
1376 pcmcia_unregister_driver(&daqp_cs_driver);
1377}
1378
1379#endif
1380