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