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
51
52
53#include <linux/interrupt.h>
54#include "../comedidev.h"
55
56#include <linux/delay.h>
57#include <linux/pci.h>
58
59#include "icp_multi.h"
60
61#define DEVICE_ID 0x8000
62
63#define ICP_MULTI_EXTDEBUG
64
65
66#define TYPE_ICP_MULTI 0
67
68#define IORANGE_ICP_MULTI 32
69
70#define ICP_MULTI_ADC_CSR 0
71#define ICP_MULTI_AI 2
72#define ICP_MULTI_DAC_CSR 4
73#define ICP_MULTI_AO 6
74#define ICP_MULTI_DI 8
75#define ICP_MULTI_DO 0x0A
76#define ICP_MULTI_INT_EN 0x0C
77#define ICP_MULTI_INT_STAT 0x0E
78#define ICP_MULTI_CNTR0 0x10
79#define ICP_MULTI_CNTR1 0x12
80#define ICP_MULTI_CNTR2 0x14
81#define ICP_MULTI_CNTR3 0x16
82
83#define ICP_MULTI_SIZE 0x20
84
85
86#define ADC_ST 0x0001
87#define ADC_BSY 0x0001
88#define ADC_BI 0x0010
89#define ADC_RA 0x0020
90#define ADC_DI 0x0040
91
92
93#define DAC_ST 0x0001
94#define DAC_BSY 0x0001
95#define DAC_BI 0x0010
96#define DAC_RA 0x0020
97
98
99#define ADC_READY 0x0001
100#define DAC_READY 0x0002
101#define DOUT_ERROR 0x0004
102#define DIN_STATUS 0x0008
103#define CIE0 0x0010
104#define CIE1 0x0020
105#define CIE2 0x0040
106#define CIE3 0x0080
107
108
109#define Status_IRQ 0x00ff
110
111
112static const struct comedi_lrange range_analog = { 4, {
113 UNI_RANGE(5),
114 UNI_RANGE(10),
115 BIP_RANGE(5),
116 BIP_RANGE(10)
117 }
118};
119
120static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
121
122
123
124
125
126
127static int icp_multi_attach(struct comedi_device *dev,
128 struct comedi_devconfig *it);
129static int icp_multi_detach(struct comedi_device *dev);
130
131
132
133
134
135
136static unsigned short pci_list_builded;
137
138struct boardtype {
139 const char *name;
140 int device_id;
141 int iorange;
142 char have_irq;
143 char cardtype;
144 int n_aichan;
145 int n_aichand;
146 int n_aochan;
147 int n_dichan;
148 int n_dochan;
149 int n_ctrs;
150 int ai_maxdata;
151 int ao_maxdata;
152 const struct comedi_lrange *rangelist_ai;
153 const char *rangecode;
154 const struct comedi_lrange *rangelist_ao;
155};
156
157static const struct boardtype boardtypes[] = {
158 {"icp_multi",
159 DEVICE_ID,
160 IORANGE_ICP_MULTI,
161 1,
162 TYPE_ICP_MULTI,
163 16,
164 8,
165 4,
166 16,
167 8,
168 4,
169 0x0fff,
170 0x0fff,
171 &range_analog,
172 range_codes_analog,
173 &range_analog},
174};
175
176#define n_boardtypes (sizeof(boardtypes)/sizeof(struct boardtype))
177
178static struct comedi_driver driver_icp_multi = {
179 .driver_name = "icp_multi",
180 .module = THIS_MODULE,
181 .attach = icp_multi_attach,
182 .detach = icp_multi_detach,
183 .num_names = n_boardtypes,
184 .board_name = &boardtypes[0].name,
185 .offset = sizeof(struct boardtype),
186};
187
188static int __init driver_icp_multi_init_module(void)
189{
190 return comedi_driver_register(&driver_icp_multi);
191}
192
193static void __exit driver_icp_multi_cleanup_module(void)
194{
195 comedi_driver_unregister(&driver_icp_multi);
196}
197
198module_init(driver_icp_multi_init_module);
199module_exit(driver_icp_multi_cleanup_module);
200
201struct icp_multi_private {
202 struct pcilst_struct *card;
203 char valid;
204 void *io_addr;
205 resource_size_t phys_iobase;
206 unsigned int AdcCmdStatus;
207 unsigned int DacCmdStatus;
208 unsigned int IntEnable;
209 unsigned int IntStatus;
210 unsigned int act_chanlist[32];
211 unsigned char act_chanlist_len;
212 unsigned char act_chanlist_pos;
213 unsigned int *ai_chanlist;
214 short *ai_data;
215 short ao_data[4];
216 short di_data;
217 unsigned int do_data;
218};
219
220#define devpriv ((struct icp_multi_private *)dev->private)
221#define this_board ((const struct boardtype *)dev->board_ptr)
222
223
224
225
226
227
228
229#if 0
230static int check_channel_list(struct comedi_device *dev,
231 struct comedi_subdevice *s,
232 unsigned int *chanlist, unsigned int n_chan);
233#endif
234static void setup_channel_list(struct comedi_device *dev,
235 struct comedi_subdevice *s,
236 unsigned int *chanlist, unsigned int n_chan);
237static int icp_multi_reset(struct comedi_device *dev);
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263static int icp_multi_insn_read_ai(struct comedi_device *dev,
264 struct comedi_subdevice *s,
265 struct comedi_insn *insn, unsigned int *data)
266{
267 int n, timeout;
268
269#ifdef ICP_MULTI_EXTDEBUG
270 printk(KERN_DEBUG "icp multi EDBG: BGN: icp_multi_insn_read_ai(...)\n");
271#endif
272
273 devpriv->IntEnable &= ~ADC_READY;
274 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
275
276
277 devpriv->IntStatus |= ADC_READY;
278 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
279
280
281 setup_channel_list(dev, s, &insn->chanspec, 1);
282
283#ifdef ICP_MULTI_EXTDEBUG
284 printk(KERN_DEBUG "icp_multi A ST=%4x IO=%p\n",
285 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR),
286 devpriv->io_addr + ICP_MULTI_ADC_CSR);
287#endif
288
289 for (n = 0; n < insn->n; n++) {
290
291 devpriv->AdcCmdStatus |= ADC_ST;
292 writew(devpriv->AdcCmdStatus,
293 devpriv->io_addr + ICP_MULTI_ADC_CSR);
294 devpriv->AdcCmdStatus &= ~ADC_ST;
295
296#ifdef ICP_MULTI_EXTDEBUG
297 printk(KERN_DEBUG "icp multi B n=%d ST=%4x\n", n,
298 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR));
299#endif
300
301 udelay(1);
302
303#ifdef ICP_MULTI_EXTDEBUG
304 printk(KERN_DEBUG "icp multi C n=%d ST=%4x\n", n,
305 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR));
306#endif
307
308
309 timeout = 100;
310 while (timeout--) {
311 if (!(readw(devpriv->io_addr +
312 ICP_MULTI_ADC_CSR) & ADC_BSY))
313 goto conv_finish;
314
315#ifdef ICP_MULTI_EXTDEBUG
316 if (!(timeout % 10))
317 printk(KERN_DEBUG
318 "icp multi D n=%d tm=%d ST=%4x\n", n,
319 timeout,
320 readw(devpriv->io_addr +
321 ICP_MULTI_ADC_CSR));
322#endif
323
324 udelay(1);
325 }
326
327
328 comedi_error(dev, "A/D insn timeout");
329
330
331 devpriv->IntEnable &= ~ADC_READY;
332 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
333
334
335 devpriv->IntStatus |= ADC_READY;
336 writew(devpriv->IntStatus,
337 devpriv->io_addr + ICP_MULTI_INT_STAT);
338
339
340 data[n] = 0;
341
342#ifdef ICP_MULTI_EXTDEBUG
343 printk(KERN_DEBUG
344 "icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n",
345 n);
346#endif
347 return -ETIME;
348
349conv_finish:
350 data[n] =
351 (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff;
352 }
353
354
355 devpriv->IntEnable &= ~ADC_READY;
356 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
357
358
359 devpriv->IntStatus |= ADC_READY;
360 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
361
362#ifdef ICP_MULTI_EXTDEBUG
363 printk(KERN_DEBUG
364 "icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n", n);
365#endif
366 return n;
367}
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387static int icp_multi_insn_write_ao(struct comedi_device *dev,
388 struct comedi_subdevice *s,
389 struct comedi_insn *insn, unsigned int *data)
390{
391 int n, chan, range, timeout;
392
393#ifdef ICP_MULTI_EXTDEBUG
394 printk(KERN_DEBUG
395 "icp multi EDBG: BGN: icp_multi_insn_write_ao(...)\n");
396#endif
397
398 devpriv->IntEnable &= ~DAC_READY;
399 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
400
401
402 devpriv->IntStatus |= DAC_READY;
403 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
404
405
406 chan = CR_CHAN(insn->chanspec);
407 range = CR_RANGE(insn->chanspec);
408
409
410
411
412
413
414 devpriv->DacCmdStatus &= 0xfccf;
415 devpriv->DacCmdStatus |= this_board->rangecode[range];
416 devpriv->DacCmdStatus |= (chan << 8);
417
418 writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR);
419
420 for (n = 0; n < insn->n; n++) {
421
422
423 timeout = 100;
424 while (timeout--) {
425 if (!(readw(devpriv->io_addr +
426 ICP_MULTI_DAC_CSR) & DAC_BSY))
427 goto dac_ready;
428
429#ifdef ICP_MULTI_EXTDEBUG
430 if (!(timeout % 10))
431 printk(KERN_DEBUG
432 "icp multi A n=%d tm=%d ST=%4x\n", n,
433 timeout,
434 readw(devpriv->io_addr +
435 ICP_MULTI_DAC_CSR));
436#endif
437
438 udelay(1);
439 }
440
441
442 comedi_error(dev, "D/A insn timeout");
443
444
445 devpriv->IntEnable &= ~DAC_READY;
446 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
447
448
449 devpriv->IntStatus |= DAC_READY;
450 writew(devpriv->IntStatus,
451 devpriv->io_addr + ICP_MULTI_INT_STAT);
452
453
454 devpriv->ao_data[chan] = 0;
455
456#ifdef ICP_MULTI_EXTDEBUG
457 printk(KERN_DEBUG
458 "icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n",
459 n);
460#endif
461 return -ETIME;
462
463dac_ready:
464
465 writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
466
467
468 devpriv->DacCmdStatus |= DAC_ST;
469 writew(devpriv->DacCmdStatus,
470 devpriv->io_addr + ICP_MULTI_DAC_CSR);
471 devpriv->DacCmdStatus &= ~DAC_ST;
472
473
474 devpriv->ao_data[chan] = data[n];
475 }
476
477#ifdef ICP_MULTI_EXTDEBUG
478 printk(KERN_DEBUG
479 "icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n", n);
480#endif
481 return n;
482}
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502static int icp_multi_insn_read_ao(struct comedi_device *dev,
503 struct comedi_subdevice *s,
504 struct comedi_insn *insn, unsigned int *data)
505{
506 int n, chan;
507
508
509 chan = CR_CHAN(insn->chanspec);
510
511
512 for (n = 0; n < insn->n; n++)
513 data[n] = devpriv->ao_data[chan];
514
515 return n;
516}
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536static int icp_multi_insn_bits_di(struct comedi_device *dev,
537 struct comedi_subdevice *s,
538 struct comedi_insn *insn, unsigned int *data)
539{
540 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
541
542 return 2;
543}
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563static int icp_multi_insn_bits_do(struct comedi_device *dev,
564 struct comedi_subdevice *s,
565 struct comedi_insn *insn, unsigned int *data)
566{
567#ifdef ICP_MULTI_EXTDEBUG
568 printk(KERN_DEBUG "icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n");
569#endif
570
571 if (data[0]) {
572 s->state &= ~data[0];
573 s->state |= (data[0] & data[1]);
574
575 printk(KERN_DEBUG "Digital outputs = %4x \n", s->state);
576
577 writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
578 }
579
580 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
581
582#ifdef ICP_MULTI_EXTDEBUG
583 printk(KERN_DEBUG "icp multi EDBG: END: icp_multi_insn_bits_do(...)\n");
584#endif
585 return 2;
586}
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606static int icp_multi_insn_read_ctr(struct comedi_device *dev,
607 struct comedi_subdevice *s,
608 struct comedi_insn *insn, unsigned int *data)
609{
610 return 0;
611}
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631static int icp_multi_insn_write_ctr(struct comedi_device *dev,
632 struct comedi_subdevice *s,
633 struct comedi_insn *insn,
634 unsigned int *data)
635{
636 return 0;
637}
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
655{
656 struct comedi_device *dev = d;
657 int int_no;
658
659#ifdef ICP_MULTI_EXTDEBUG
660 printk(KERN_DEBUG
661 "icp multi EDBG: BGN: interrupt_service_icp_multi(%d,...)\n",
662 irq);
663#endif
664
665
666 int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
667 if (!int_no)
668
669 return IRQ_NONE;
670
671#ifdef ICP_MULTI_EXTDEBUG
672 printk(KERN_DEBUG
673 "icp multi EDBG: interrupt_service_icp_multi() ST: %4x\n",
674 readw(devpriv->io_addr + ICP_MULTI_INT_STAT));
675#endif
676
677
678 switch (int_no) {
679 case ADC_READY:
680 break;
681 case DAC_READY:
682 break;
683 case DOUT_ERROR:
684 break;
685 case DIN_STATUS:
686 break;
687 case CIE0:
688 break;
689 case CIE1:
690 break;
691 case CIE2:
692 break;
693 case CIE3:
694 break;
695 default:
696 break;
697
698 }
699
700#ifdef ICP_MULTI_EXTDEBUG
701 printk(KERN_DEBUG
702 "icp multi EDBG: END: interrupt_service_icp_multi(...)\n");
703#endif
704 return IRQ_HANDLED;
705}
706
707#if 0
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728static int check_channel_list(struct comedi_device *dev,
729 struct comedi_subdevice *s,
730 unsigned int *chanlist, unsigned int n_chan)
731{
732 unsigned int i;
733
734#ifdef ICP_MULTI_EXTDEBUG
735 printk(KERN_DEBUG
736 "icp multi EDBG: check_channel_list(...,%d)\n", n_chan);
737#endif
738
739 if (n_chan < 1) {
740 comedi_error(dev, "range/channel list is empty!");
741 return 0;
742 }
743
744 for (i = 0; i < n_chan; i++) {
745
746 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
747 if (CR_CHAN(chanlist[i]) > this_board->n_aichand) {
748 comedi_error(dev,
749 "Incorrect differential ai ch-nr");
750 return 0;
751 }
752 } else {
753 if (CR_CHAN(chanlist[i]) > this_board->n_aichan) {
754 comedi_error(dev,
755 "Incorrect ai channel number");
756 return 0;
757 }
758 }
759 }
760 return 1;
761}
762#endif
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784static void setup_channel_list(struct comedi_device *dev,
785 struct comedi_subdevice *s,
786 unsigned int *chanlist, unsigned int n_chan)
787{
788 unsigned int i, range, chanprog;
789 unsigned int diff;
790
791#ifdef ICP_MULTI_EXTDEBUG
792 printk(KERN_DEBUG
793 "icp multi EDBG: setup_channel_list(...,%d)\n", n_chan);
794#endif
795 devpriv->act_chanlist_len = n_chan;
796 devpriv->act_chanlist_pos = 0;
797
798 for (i = 0; i < n_chan; i++) {
799
800 chanprog = CR_CHAN(chanlist[i]);
801
802
803 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
804 diff = 1;
805 chanprog &= 0x0007;
806 } else {
807 diff = 0;
808 chanprog &= 0x000f;
809 }
810
811
812
813 devpriv->AdcCmdStatus &= 0xf00f;
814
815
816 if (diff) {
817
818 devpriv->AdcCmdStatus |= (chanprog << 9);
819 devpriv->AdcCmdStatus |= ADC_DI;
820 } else
821
822 devpriv->AdcCmdStatus |= (chanprog << 8);
823
824
825 range = this_board->rangecode[CR_RANGE(chanlist[i])];
826
827 devpriv->AdcCmdStatus |= range;
828
829
830 writew(devpriv->AdcCmdStatus,
831 devpriv->io_addr + ICP_MULTI_ADC_CSR);
832
833#ifdef ICP_MULTI_EXTDEBUG
834 printk(KERN_DEBUG
835 "GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range,
836 devpriv->act_chanlist[i]);
837#endif
838 }
839
840}
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857static int icp_multi_reset(struct comedi_device *dev)
858{
859 unsigned int i;
860
861#ifdef ICP_MULTI_EXTDEBUG
862 printk(KERN_DEBUG
863 "icp_multi EDBG: BGN: icp_multi_reset(...)\n");
864#endif
865
866 writew(0, devpriv->io_addr + ICP_MULTI_INT_EN);
867 writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT);
868
869 if (this_board->n_aochan)
870
871 for (i = 0; i < this_board->n_aochan; i++) {
872 devpriv->DacCmdStatus &= 0xfcce;
873
874
875 devpriv->DacCmdStatus |= (i << 8);
876
877
878 writew(0, devpriv->io_addr + ICP_MULTI_AO);
879
880
881 devpriv->DacCmdStatus |= DAC_ST;
882
883
884 writew(devpriv->DacCmdStatus,
885 devpriv->io_addr + ICP_MULTI_DAC_CSR);
886
887
888 udelay(1);
889 }
890
891 writew(0, devpriv->io_addr + ICP_MULTI_DO);
892
893#ifdef ICP_MULTI_EXTDEBUG
894 printk(KERN_DEBUG
895 "icp multi EDBG: END: icp_multi_reset(...)\n");
896#endif
897 return 0;
898}
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917static int icp_multi_attach(struct comedi_device *dev,
918 struct comedi_devconfig *it)
919{
920 struct comedi_subdevice *s;
921 int ret, subdev, n_subdevices;
922 unsigned int irq;
923 struct pcilst_struct *card = NULL;
924 resource_size_t io_addr[5], iobase;
925 unsigned char pci_bus, pci_slot, pci_func;
926
927 printk(KERN_WARNING
928 "icp_multi EDBG: BGN: icp_multi_attach(...)\n");
929
930
931 ret = alloc_private(dev, sizeof(struct icp_multi_private));
932 if (ret < 0)
933 return ret;
934
935
936 if (pci_list_builded++ == 0) {
937 pci_card_list_init(PCI_VENDOR_ID_ICP,
938#ifdef ICP_MULTI_EXTDEBUG
939 1
940#else
941 0
942#endif
943 );
944 }
945
946 printk(KERN_WARNING
947 "Anne's comedi%d: icp_multi: board=%s", dev->minor,
948 this_board->name);
949
950 card = select_and_alloc_pci_card(PCI_VENDOR_ID_ICP,
951 this_board->device_id, it->options[0],
952 it->options[1]);
953
954 if (card == NULL)
955 return -EIO;
956
957 devpriv->card = card;
958
959 if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0],
960 &irq)) < 0) {
961 printk(KERN_WARNING " - Can't get configuration data!\n");
962 return -EIO;
963 }
964
965 iobase = io_addr[2];
966 devpriv->phys_iobase = iobase;
967
968 printk(KERN_WARNING
969 ", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus, pci_slot, pci_func,
970 (unsigned long long)iobase);
971
972 devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
973
974 if (devpriv->io_addr == NULL) {
975 printk(KERN_WARNING "ioremap failed.\n");
976 return -ENOMEM;
977 }
978#ifdef ICP_MULTI_EXTDEBUG
979 printk(KERN_DEBUG
980 "0x%08llx mapped to %p, ", (unsigned long long)iobase,
981 devpriv->io_addr);
982#endif
983
984 dev->board_name = this_board->name;
985
986 n_subdevices = 0;
987 if (this_board->n_aichan)
988 n_subdevices++;
989 if (this_board->n_aochan)
990 n_subdevices++;
991 if (this_board->n_dichan)
992 n_subdevices++;
993 if (this_board->n_dochan)
994 n_subdevices++;
995 if (this_board->n_ctrs)
996 n_subdevices++;
997
998 ret = alloc_subdevices(dev, n_subdevices);
999 if (ret < 0)
1000 return ret;
1001
1002 icp_multi_reset(dev);
1003
1004 if (this_board->have_irq) {
1005 if (irq) {
1006 if (request_irq(irq, interrupt_service_icp_multi,
1007 IRQF_SHARED, "Inova Icp Multi", dev)) {
1008 printk(KERN_WARNING
1009 "unable to allocate IRQ %u, DISABLING IT",
1010 irq);
1011 irq = 0;
1012 } else
1013 printk(KERN_WARNING ", irq=%u", irq);
1014 } else
1015 printk(KERN_WARNING ", IRQ disabled");
1016 } else
1017 irq = 0;
1018
1019 dev->irq = irq;
1020
1021 printk(KERN_WARNING ".\n");
1022
1023 subdev = 0;
1024
1025 if (this_board->n_aichan) {
1026 s = dev->subdevices + subdev;
1027 dev->read_subdev = s;
1028 s->type = COMEDI_SUBD_AI;
1029 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
1030 if (this_board->n_aichand)
1031 s->subdev_flags |= SDF_DIFF;
1032 s->n_chan = this_board->n_aichan;
1033 s->maxdata = this_board->ai_maxdata;
1034 s->len_chanlist = this_board->n_aichan;
1035 s->range_table = this_board->rangelist_ai;
1036 s->insn_read = icp_multi_insn_read_ai;
1037 subdev++;
1038 }
1039
1040 if (this_board->n_aochan) {
1041 s = dev->subdevices + subdev;
1042 s->type = COMEDI_SUBD_AO;
1043 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1044 s->n_chan = this_board->n_aochan;
1045 s->maxdata = this_board->ao_maxdata;
1046 s->len_chanlist = this_board->n_aochan;
1047 s->range_table = this_board->rangelist_ao;
1048 s->insn_write = icp_multi_insn_write_ao;
1049 s->insn_read = icp_multi_insn_read_ao;
1050 subdev++;
1051 }
1052
1053 if (this_board->n_dichan) {
1054 s = dev->subdevices + subdev;
1055 s->type = COMEDI_SUBD_DI;
1056 s->subdev_flags = SDF_READABLE;
1057 s->n_chan = this_board->n_dichan;
1058 s->maxdata = 1;
1059 s->len_chanlist = this_board->n_dichan;
1060 s->range_table = &range_digital;
1061 s->io_bits = 0;
1062 s->insn_bits = icp_multi_insn_bits_di;
1063 subdev++;
1064 }
1065
1066 if (this_board->n_dochan) {
1067 s = dev->subdevices + subdev;
1068 s->type = COMEDI_SUBD_DO;
1069 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
1070 s->n_chan = this_board->n_dochan;
1071 s->maxdata = 1;
1072 s->len_chanlist = this_board->n_dochan;
1073 s->range_table = &range_digital;
1074 s->io_bits = (1 << this_board->n_dochan) - 1;
1075 s->state = 0;
1076 s->insn_bits = icp_multi_insn_bits_do;
1077 subdev++;
1078 }
1079
1080 if (this_board->n_ctrs) {
1081 s = dev->subdevices + subdev;
1082 s->type = COMEDI_SUBD_COUNTER;
1083 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1084 s->n_chan = this_board->n_ctrs;
1085 s->maxdata = 0xffff;
1086 s->len_chanlist = this_board->n_ctrs;
1087 s->state = 0;
1088 s->insn_read = icp_multi_insn_read_ctr;
1089 s->insn_write = icp_multi_insn_write_ctr;
1090 subdev++;
1091 }
1092
1093 devpriv->valid = 1;
1094
1095#ifdef ICP_MULTI_EXTDEBUG
1096 printk(KERN_DEBUG "icp multi EDBG: END: icp_multi_attach(...)\n");
1097#endif
1098
1099 return 0;
1100}
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118static int icp_multi_detach(struct comedi_device *dev)
1119{
1120
1121 if (dev->private)
1122 if (devpriv->valid)
1123 icp_multi_reset(dev);
1124
1125 if (dev->irq)
1126 free_irq(dev->irq, dev);
1127
1128 if (dev->private && devpriv->io_addr)
1129 iounmap(devpriv->io_addr);
1130
1131 if (dev->private && devpriv->card)
1132 pci_card_free(devpriv->card);
1133
1134 if (--pci_list_builded == 0)
1135 pci_card_list_cleanup(PCI_VENDOR_ID_ICP);
1136
1137 return 0;
1138}
1139
1140MODULE_AUTHOR("Comedi http://www.comedi.org");
1141MODULE_DESCRIPTION("Comedi low-level driver");
1142MODULE_LICENSE("GPL");
1143