2 * comedi/drivers/pcl812.c
4 * Author: Michal Dobes <dobes@tesnet.cz>
6 * hardware driver for Advantech cards
7 * card: PCL-812, PCL-812PG, PCL-813, PCL-813B
8 * driver: pcl812, pcl812pg, pcl813, pcl813b
10 * card: ACL-8112DG, ACL-8112HG, ACL-8112PG, ACL-8113, ACL-8216
11 * driver: acl8112dg, acl8112hg, acl8112pg, acl8113, acl8216
12 * and for ICP DAS cards
13 * card: ISO-813, A-821PGH, A-821PGL, A-821PGL-NDA, A-822PGH, A-822PGL,
14 * driver: iso813, a821pgh, a-821pgl, a-821pglnda, a822pgh, a822pgl,
15 * card: A-823PGH, A-823PGL, A-826PG
16 * driver: a823pgh, a823pgl, a826pg
20 Description: Advantech PCL-812/PG, PCL-813/B,
21 ADLink ACL-8112DG/HG/PG, ACL-8113, ACL-8216,
22 ICP DAS A-821PGH/PGL/PGL-NDA, A-822PGH/PGL, A-823PGH/PGL, A-826PG,
24 Author: Michal Dobes <dobes@tesnet.cz>
25 Devices: [Advantech] PCL-812 (pcl812), PCL-812PG (pcl812pg),
26 PCL-813 (pcl813), PCL-813B (pcl813b), [ADLink] ACL-8112DG (acl8112dg),
27 ACL-8112HG (acl8112hg), ACL-8113 (acl-8113), ACL-8216 (acl8216),
28 [ICP] ISO-813 (iso813), A-821PGH (a821pgh), A-821PGL (a821pgl),
29 A-821PGL-NDA (a821pclnda), A-822PGH (a822pgh), A-822PGL (a822pgl),
30 A-823PGH (a823pgh), A-823PGL (a823pgl), A-826PG (a826pg)
31 Status: works (I hope. My board fire up under my hands
32 and I cann't test all features.)
34 This driver supports insn and cmd interfaces. Some boards support only insn
35 becouse their hardware don't allow more (PCL-813/B, ACL-8113, ISO-813).
36 Data transfer over DMA is supported only when you measure only one
37 channel, this is too hardware limitation of these boards.
38 See the head of the source file pcl812.c for configuration options.
42 * Options for PCL-812:
44 * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7; 10, 11, 12, 14, 15)
45 * [2] - DMA (0=disable, 1, 3)
46 * [3] - 0=trigger source is internal 8253 with 2MHz clock
47 * 1=trigger source is external
48 * [4] - 0=A/D input range is +/-10V
49 * 1=A/D input range is +/-5V
50 * 2=A/D input range is +/-2.5V
51 * 3=A/D input range is +/-1.25V
52 * 4=A/D input range is +/-0.625V
53 * 5=A/D input range is +/-0.3125V
54 * [5] - 0=D/A outputs 0-5V (internal reference -5V)
55 * 1=D/A outputs 0-10V (internal reference -10V)
56 * 2=D/A outputs unknow (external reference)
58 * Options for PCL-812PG, ACL-8112PG:
60 * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7; 10, 11, 12, 14, 15)
61 * [2] - DMA (0=disable, 1, 3)
62 * [3] - 0=trigger source is internal 8253 with 2MHz clock
63 * 1=trigger source is external
64 * [4] - 0=A/D have max +/-5V input
65 * 1=A/D have max +/-10V input
66 * [5] - 0=D/A outputs 0-5V (internal reference -5V)
67 * 1=D/A outputs 0-10V (internal reference -10V)
68 * 2=D/A outputs unknow (external reference)
70 * Options for ACL-8112DG/HG, A-822PGL/PGH, A-823PGL/PGH, ACL-8216, A-826PG:
72 * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7; 10, 11, 12, 14, 15)
73 * [2] - DMA (0=disable, 1, 3)
74 * [3] - 0=trigger source is internal 8253 with 2MHz clock
75 * 1=trigger source is external
76 * [4] - 0=A/D channels are S.E.
77 * 1=A/D channels are DIFF
78 * [5] - 0=D/A outputs 0-5V (internal reference -5V)
79 * 1=D/A outputs 0-10V (internal reference -10V)
80 * 2=D/A outputs unknow (external reference)
82 * Options for A-821PGL/PGH:
84 * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
85 * [2] - 0=A/D channels are S.E.
86 * 1=A/D channels are DIFF
87 * [3] - 0=D/A output 0-5V (internal reference -5V)
88 * 1=D/A output 0-10V (internal reference -10V)
90 * Options for A-821PGL-NDA:
92 * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
93 * [2] - 0=A/D channels are S.E.
94 * 1=A/D channels are DIFF
96 * Options for PCL-813:
99 * Options for PCL-813B:
101 * [1] - 0= bipolar inputs
104 * Options for ACL-8113, ISO-813:
106 * [1] - 0= 10V bipolar inputs
107 * 1= 10V unipolar inputs
108 * 2= 20V bipolar inputs
109 * 3= 20V unipolar inputs
113 #include <linux/comedidev.h>
115 #include <linux/delay.h>
116 #include <linux/ioport.h>
122 #undef PCL812_EXTDEBUG /* if this is defined then a lot of messages is printed */
125 // hardware types of the cards
126 #define boardPCL812PG 0 /* and ACL-8112PG */
127 #define boardPCL813B 1
128 #define boardPCL812 2
129 #define boardPCL813 3
130 #define boardISO813 5
131 #define boardACL8113 6
132 #define boardACL8112 7 /* ACL-8112DG/HG, A-822PGL/PGH, A-823PGL/PGH */
133 #define boardACL8216 8 /* and ICP DAS A-826PG */
134 #define boardA821 9 /* PGH, PGL, PGL/NDA versions */
136 #define PCLx1x_IORANGE 16
138 #define PCL812_CTR0 0
139 #define PCL812_CTR1 1
140 #define PCL812_CTR2 2
141 #define PCL812_CTRCTL 3
142 #define PCL812_AD_LO 4
143 #define PCL812_DA1_LO 4
144 #define PCL812_AD_HI 5
145 #define PCL812_DA1_HI 5
146 #define PCL812_DA2_LO 6
147 #define PCL812_DI_LO 6
148 #define PCL812_DA2_HI 7
149 #define PCL812_DI_HI 7
150 #define PCL812_CLRINT 8
151 #define PCL812_GAIN 9
152 #define PCL812_MUX 10
153 #define PCL812_MODE 11
154 #define PCL812_CNTENABLE 10
155 #define PCL812_SOFTTRIG 12
156 #define PCL812_DO_LO 13
157 #define PCL812_DO_HI 14
159 #define PCL812_DRDY 0x10 /* =0 data ready */
161 #define ACL8216_STATUS 8 /* 5. bit signalize data ready */
163 #define ACL8216_DRDY 0x20 /* =0 data ready */
165 #define MAX_CHANLIST_LEN 256 /* length of scan list */
167 static comedi_lrange range_pcl812pg_ai = { 5, {
174 static comedi_lrange range_pcl812pg2_ai = { 5, {
181 static comedi_lrange range812_bipolar1_25 = { 1, {
184 static comedi_lrange range812_bipolar0_625 = { 1, {
187 static comedi_lrange range812_bipolar0_3125 = { 1, {
190 static comedi_lrange range_pcl813b_ai = { 4, {
196 static comedi_lrange range_pcl813b2_ai = { 4, {
202 static comedi_lrange range_iso813_1_ai = { 5, {
209 static comedi_lrange range_iso813_1_2_ai = { 5, {
216 static comedi_lrange range_iso813_2_ai = { 4, {
222 static comedi_lrange range_iso813_2_2_ai = { 4, {
228 static comedi_lrange range_acl8113_1_ai = { 4, {
234 static comedi_lrange range_acl8113_1_2_ai = { 4, {
240 static comedi_lrange range_acl8113_2_ai = { 3, {
245 static comedi_lrange range_acl8113_2_2_ai = { 3, {
250 static comedi_lrange range_acl8112dg_ai = { 9, {
261 static comedi_lrange range_acl8112hg_ai = { 12, {
275 static comedi_lrange range_a821pgh_ai = { 4, {
282 static int pcl812_attach(comedi_device *dev,comedi_devconfig *it);
283 static int pcl812_detach(comedi_device *dev);
287 const char *name; // board name
288 int board_type; // type of this board
289 int n_aichan; // num of AI chans in S.E.
290 int n_aichan_diff; // DIFF num of chans
291 int n_aochan; // num of DA chans
292 int n_dichan; // DI and DO chans
294 int ai_maxdata; // AI resolution
295 unsigned int ai_ns_min; // max sample speed of card v ns
296 unsigned int i8254_osc_base; // clock base
297 comedi_lrange *rangelist_ai; // rangelist for A/D
298 comedi_lrange *rangelist_ao; // rangelist for D/A
299 unsigned int IRQbits; // allowed IRQ
300 unsigned char DMAbits; // allowed DMA chans
301 unsigned char io_range; // iorange for this board
302 unsigned char haveMPC508; // 1=board use MPC508A multiplexor
305 static boardtype boardtypes[] =
307 {"pcl812", boardPCL812, 16, 0, 2, 16, 16, 0x0fff,
308 33000, 500, &range_bipolar10, &range_unipolar5,
309 0xdcfc, 0x0a, PCLx1x_IORANGE, 0},
310 {"pcl812pg", boardPCL812PG, 16, 0, 2, 16, 16, 0x0fff,
311 33000, 500, &range_pcl812pg_ai, &range_unipolar5,
312 0xdcfc, 0x0a, PCLx1x_IORANGE, 0},
313 {"acl8112pg", boardPCL812PG, 16, 0, 2, 16, 16, 0x0fff,
314 10000, 500, &range_pcl812pg_ai, &range_unipolar5,
315 0xdcfc, 0x0a, PCLx1x_IORANGE, 0},
316 {"acl8112dg", boardACL8112, 16, 8, 2, 16, 16, 0x0fff,
317 10000, 500, &range_acl8112dg_ai, &range_unipolar5,
318 0xdcfc, 0x0a, PCLx1x_IORANGE, 1},
319 {"acl8112hg", boardACL8112, 16, 8, 2, 16, 16, 0x0fff,
320 10000, 500, &range_acl8112hg_ai, &range_unipolar5,
321 0xdcfc, 0x0a, PCLx1x_IORANGE, 1},
322 {"a821pgl", boardA821, 16, 8, 1, 16, 16, 0x0fff,
323 10000, 500, &range_pcl813b_ai, &range_unipolar5,
324 0x000c, 0x00, PCLx1x_IORANGE, 0},
325 {"a821pglnda", boardA821, 16, 8, 0, 0, 0, 0x0fff,
326 10000, 500, &range_pcl813b_ai, NULL,
327 0x000c, 0x00, PCLx1x_IORANGE, 0},
328 {"a821pgh", boardA821, 16, 8, 1, 16, 16, 0x0fff,
329 10000, 500, &range_a821pgh_ai, &range_unipolar5,
330 0x000c, 0x00, PCLx1x_IORANGE, 0},
331 {"a822pgl", boardACL8112, 16, 8, 2, 16, 16, 0x0fff,
332 10000, 500, &range_acl8112dg_ai, &range_unipolar5,
333 0xdcfc, 0x0a, PCLx1x_IORANGE, 0},
334 {"a822pgh", boardACL8112, 16, 8, 2, 16, 16, 0x0fff,
335 10000, 500, &range_acl8112hg_ai, &range_unipolar5,
336 0xdcfc, 0x0a, PCLx1x_IORANGE, 0},
337 {"a823pgl", boardACL8112, 16, 8, 2, 16, 16, 0x0fff,
338 8000, 500, &range_acl8112dg_ai, &range_unipolar5,
339 0xdcfc, 0x0a, PCLx1x_IORANGE, 0},
340 {"a823pgh", boardACL8112, 16, 8, 2, 16, 16, 0x0fff,
341 8000, 500, &range_acl8112hg_ai, &range_unipolar5,
342 0xdcfc, 0x0a, PCLx1x_IORANGE, 0},
343 {"pcl813", boardPCL813, 32, 0, 0, 0, 0, 0x0fff,
344 0, 0, &range_pcl813b_ai, NULL,
345 0x0000, 0x00, PCLx1x_IORANGE, 0},
346 {"pcl813b", boardPCL813B, 32, 0, 0, 0, 0, 0x0fff,
347 0, 0, &range_pcl813b_ai, NULL,
348 0x0000, 0x00, PCLx1x_IORANGE, 0},
349 {"acl8113", boardACL8113, 32, 0, 0, 0, 0, 0x0fff,
350 0, 0, &range_acl8113_1_ai, NULL,
351 0x0000, 0x00, PCLx1x_IORANGE, 0},
352 {"iso813", boardISO813, 32, 0, 0, 0, 0, 0x0fff,
353 0, 0, &range_iso813_1_ai, NULL,
354 0x0000, 0x00, PCLx1x_IORANGE, 0},
355 {"acl8216", boardACL8216, 16, 8, 2, 16, 16, 0xffff,
356 10000, 500, &range_pcl813b2_ai, &range_unipolar5,
357 0xdcfc, 0x0a, PCLx1x_IORANGE, 1},
358 {"a826pg", boardACL8216, 16, 8, 2, 16, 16, 0xffff,
359 10000, 500, &range_pcl813b2_ai, &range_unipolar5,
360 0xdcfc, 0x0a, PCLx1x_IORANGE, 0},
363 #define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
364 #define this_board ((boardtype *)dev->board_ptr)
366 static comedi_driver driver_pcl812={
367 driver_name: "pcl812",
369 attach: pcl812_attach,
370 detach: pcl812_detach,
371 board_name: &boardtypes[0].name,
372 num_names: n_boardtypes,
373 offset: sizeof(boardtype),
375 COMEDI_INITCLEANUP(driver_pcl812);
378 unsigned char valid; // =1 device is OK
379 unsigned char dma; // >0 use dma ( usedDMA channel)
380 unsigned char use_diff; // =1 diff inputs
381 unsigned char use_MPC; // 1=board uses MPC508A multiplexor
382 unsigned char use_ext_trg; // 1=board uses external trigger
383 unsigned char range_correction; // =1 we must add 1 to range number
384 unsigned char old_chan_reg; // lastly used chan/gain pair
385 unsigned char old_gain_reg;
386 unsigned char mode_reg_int; // there is stored INT number for some card
387 unsigned char ai_neverending; // =1 we do unlimited AI
388 unsigned char ai_eos; // 1=EOS wake up
389 unsigned char ai_dma; // =1 we use DMA
390 unsigned int ai_poll_ptr; // how many sampes transfer poll
391 unsigned int ai_scans; // len of scanlist
392 unsigned int ai_act_scan; // how many scans we finished
393 unsigned int ai_chanlist[MAX_CHANLIST_LEN];// our copy of channel/range list
394 unsigned int ai_n_chan; // how many channels is measured
395 unsigned int ai_flags; // flaglist
396 unsigned int ai_data_len; // len of data buffer
397 sampl_t *ai_data; // data buffer
398 unsigned int ai_is16b; // =1 we have 16 bit card
399 unsigned long dmabuf[2]; // PTR to DMA buf
400 unsigned int dmapages[2]; // how many pages we have allocated
401 unsigned int hwdmaptr[2]; // HW PTR to DMA buf
402 unsigned int hwdmasize[2]; // DMA buf size in bytes
403 unsigned int dmabytestomove[2]; // how many bytes DMA transfer
404 int next_dma_buf; // which buffer is next to use
405 unsigned int dma_runs_to_end; // how many times we must switch DMA buffers
406 unsigned int last_dma_run; // how many bytes to transfer on last DMA buffer
407 unsigned int max_812_ai_mode0_rangewait;// setling time for gain
408 lsampl_t ao_readback[2]; // data for AO readback
411 #define devpriv ((pcl812_private *)dev->private)
414 ==============================================================================
416 static void start_pacer(comedi_device * dev, int mode, unsigned int divisor1, unsigned int divisor2);
417 static void setup_range_channel(comedi_device * dev, comedi_subdevice * s,
418 unsigned int rangechan, char wait);
419 static int pcl812_ai_cancel(comedi_device * dev, comedi_subdevice * s);
421 ==============================================================================
423 static int pcl812_ai_insn_read(comedi_device *dev,comedi_subdevice *s,
424 comedi_insn *insn,lsampl_t *data)
429 outb(devpriv->mode_reg_int|1, dev->iobase + PCL812_MODE); /* select software trigger */
430 setup_range_channel(dev, s, insn->chanspec, 1); // select channel and renge
431 for(n=0;n<insn->n;n++){
432 outb(255, dev->iobase + PCL812_SOFTTRIG); /* start conversion */
434 timeout = 50; /* wait max 50us, it must finish under 33us */
436 hi = inb(dev->iobase + PCL812_AD_HI);
437 if (!(hi & PCL812_DRDY))
441 rt_printk("comedi%d: pcl812: (%s at 0x%lx) A/D insn read timeout\n", dev->minor, dev->board_name, dev->iobase);
442 outb(devpriv->mode_reg_int|0, dev->iobase + PCL812_MODE);
446 data[n] = ((hi & 0xf) << 8) | inb(dev->iobase + PCL812_AD_LO);
448 outb(devpriv->mode_reg_int|0, dev->iobase + PCL812_MODE);
453 ==============================================================================
455 static int acl8216_ai_insn_read(comedi_device *dev,comedi_subdevice *s,
456 comedi_insn *insn,lsampl_t *data)
461 outb(1, dev->iobase + PCL812_MODE); /* select software trigger */
462 setup_range_channel(dev, s, insn->chanspec, 1); // select channel and renge
463 for(n=0;n<insn->n;n++){
464 outb(255, dev->iobase + PCL812_SOFTTRIG); /* start conversion */
466 timeout = 50; /* wait max 50us, it must finish under 33us */
468 if (!(inb(dev->iobase + ACL8216_STATUS) & ACL8216_DRDY))
472 rt_printk("comedi%d: pcl812: (%s at 0x%lx) A/D insn read timeout\n", dev->minor, dev->board_name, dev->iobase);
473 outb(0, dev->iobase + PCL812_MODE);
477 data[n] = (inb(dev->iobase + PCL812_AD_HI) << 8) | inb(dev->iobase + PCL812_AD_LO);
479 outb(0, dev->iobase + PCL812_MODE);
484 ==============================================================================
486 static int pcl812_ao_insn_write(comedi_device *dev,comedi_subdevice *s,
487 comedi_insn *insn,lsampl_t *data)
489 int chan = CR_CHAN(insn->chanspec);
492 for(i=0;i<insn->n;i++){
493 outb((data[i] & 0xff), dev->iobase + (chan ? PCL812_DA2_LO : PCL812_DA1_LO));
494 outb((data[i] >> 8) & 0x0f, dev->iobase + (chan ? PCL812_DA2_HI : PCL812_DA1_HI));
495 devpriv->ao_readback[chan]=data[i];
502 ==============================================================================
504 static int pcl812_ao_insn_read(comedi_device *dev,comedi_subdevice *s,
505 comedi_insn *insn,lsampl_t *data)
507 int chan = CR_CHAN(insn->chanspec);
510 for(i=0;i<insn->n;i++){
511 data[i] = devpriv->ao_readback[chan];
518 ==============================================================================
520 static int pcl812_di_insn_bits(comedi_device *dev,comedi_subdevice *s,
521 comedi_insn *insn,lsampl_t *data)
523 if(insn->n!=2)return -EINVAL;
525 data[1] = inb(dev->iobase + PCL812_DI_LO);
526 data[1] |= inb(dev->iobase + PCL812_DI_HI) << 8;
532 ==============================================================================
534 static int pcl812_do_insn_bits(comedi_device *dev,comedi_subdevice *s,
535 comedi_insn *insn,lsampl_t *data)
537 if(insn->n!=2)return -EINVAL;
540 s->state &= ~data[0];
541 s->state |= data[0]&data[1];
542 outb(s->state & 0xff, dev->iobase + PCL812_DO_LO);
543 outb((s->state >> 8), dev->iobase + PCL812_DO_HI);
550 #ifdef PCL812_EXTDEBUG
552 ==============================================================================
554 static void pcl812_cmdtest_out(int e,comedi_cmd *cmd) {
555 rt_printk("pcl812 e=%d startsrc=%x scansrc=%x convsrc=%x\n",e,cmd->start_src,cmd->scan_begin_src,cmd->convert_src);
556 rt_printk("pcl812 e=%d startarg=%d scanarg=%d convarg=%d\n",e,cmd->start_arg,cmd->scan_begin_arg,cmd->convert_arg);
557 rt_printk("pcl812 e=%d stopsrc=%x scanend=%x\n",e,cmd->stop_src,cmd->scan_end_src);
558 rt_printk("pcl812 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n",e,cmd->stop_arg,cmd->scan_end_arg,cmd->chanlist_len);
563 ==============================================================================
565 static int pcl812_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd)
568 int tmp,divisor1,divisor2;
570 #ifdef PCL812_EXTDEBUG
571 rt_printk("pcl812 EDBG: BGN: pcl812_ai_cmdtest(...)\n");
572 pcl812_cmdtest_out(-1, cmd);
574 /* step 1: make sure trigger sources are trivially valid */
577 cmd->start_src &= TRIG_NOW;
578 if(!cmd->start_src || tmp!=cmd->start_src)err++;
580 tmp=cmd->scan_begin_src;
581 cmd->scan_begin_src &= TRIG_FOLLOW;
582 if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++;
584 tmp=cmd->convert_src;
585 if (devpriv->use_ext_trg) { cmd->convert_src &= TRIG_EXT; }
586 else { cmd->convert_src &= TRIG_TIMER; }
587 if(!cmd->convert_src || tmp!=cmd->convert_src)err++;
589 tmp=cmd->scan_end_src;
590 cmd->scan_end_src &= TRIG_COUNT;
591 if(!cmd->scan_end_src || tmp!=cmd->scan_end_src)err++;
594 cmd->stop_src &= TRIG_COUNT|TRIG_NONE;
595 if(!cmd->stop_src || tmp!=cmd->stop_src)err++;
598 #ifdef PCL812_EXTDEBUG
599 pcl812_cmdtest_out(1, cmd);
600 rt_printk("pcl812 EDBG: BGN: pcl812_ai_cmdtest(...) err=%d ret=1\n",err);
605 /* step 2: make sure trigger sources are unique and mutually compatible */
607 if(cmd->start_src!=TRIG_NOW) {
608 cmd->start_src=TRIG_NOW;
612 if(cmd->scan_begin_src!=TRIG_FOLLOW) {
613 cmd->scan_begin_src=TRIG_FOLLOW;
617 if (devpriv->use_ext_trg) {
618 if(cmd->convert_src!=TRIG_EXT) {
619 cmd->convert_src=TRIG_EXT;
623 if(cmd->convert_src!=TRIG_TIMER) {
624 cmd->convert_src=TRIG_TIMER;
629 if(cmd->scan_end_src!=TRIG_COUNT) {
630 cmd->scan_end_src=TRIG_COUNT;
634 if(cmd->stop_src!=TRIG_NONE &&
635 cmd->stop_src!=TRIG_COUNT) err++;
638 #ifdef PCL812_EXTDEBUG
639 pcl812_cmdtest_out(2, cmd);
640 rt_printk("pcl812 EDBG: BGN: pcl812_ai_cmdtest(...) err=%d ret=2\n",err);
645 /* step 3: make sure arguments are trivially compatible */
647 if(cmd->start_arg!=0){
652 if(cmd->scan_begin_arg!=0){
653 cmd->scan_begin_arg=0;
657 if(cmd->convert_src==TRIG_TIMER){
658 if(cmd->convert_arg<this_board->ai_ns_min){
659 cmd->convert_arg=this_board->ai_ns_min;
662 } else { /* TRIG_EXT */
663 if(cmd->convert_arg!=0){
669 if(!cmd->chanlist_len){
673 if(cmd->chanlist_len>MAX_CHANLIST_LEN){
674 cmd->chanlist_len=this_board->n_aichan;
677 if(cmd->scan_end_arg!=cmd->chanlist_len){
678 cmd->scan_end_arg=cmd->chanlist_len;
681 if(cmd->stop_src==TRIG_COUNT){
686 } else { /* TRIG_NONE */
687 if(cmd->stop_arg!=0){
694 #ifdef PCL812_EXTDEBUG
695 pcl812_cmdtest_out(3, cmd);
696 rt_printk("pcl812 EDBG: BGN: pcl812_ai_cmdtest(...) err=%d ret=3\n",err);
701 /* step 4: fix up any arguments */
703 if(cmd->convert_src==TRIG_TIMER){
704 tmp=cmd->convert_arg;
705 i8253_cascade_ns_to_timer(this_board->i8254_osc_base,&divisor1,&divisor2,&cmd->convert_arg,cmd->flags&TRIG_ROUND_MASK);
706 if(cmd->convert_arg<this_board->ai_ns_min)
707 cmd->convert_arg=this_board->ai_ns_min;
708 if(tmp!=cmd->convert_arg)err++;
712 #ifdef PCL812_EXTDEBUG
713 rt_printk("pcl812 EDBG: BGN: pcl812_ai_cmdtest(...) err=%d ret=4\n",err);
722 ==============================================================================
724 static int pcl812_ai_cmd(comedi_device *dev,comedi_subdevice *s)
726 unsigned int divisor1=0, divisor2=0, i, dma_flags, bytes;
727 comedi_cmd *cmd=&s->async->cmd;
729 #ifdef PCL812_EXTDEBUG
730 rt_printk("pcl812 EDBG: BGN: pcl812_ai_cmd(...)\n");
733 if(cmd->start_src!=TRIG_NOW) return -EINVAL;
734 if(cmd->scan_begin_src!=TRIG_FOLLOW) return -EINVAL;
735 if (devpriv->use_ext_trg) {
736 if(cmd->convert_src!=TRIG_EXT) return -EINVAL;
738 if(cmd->convert_src!=TRIG_TIMER) return -EINVAL;
740 if(cmd->scan_end_src!=TRIG_COUNT) return -EINVAL;
741 if(cmd->scan_end_arg!=cmd->chanlist_len) return -EINVAL;
742 if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL;
744 if (cmd->convert_src==TRIG_TIMER) {
745 if(cmd->convert_arg<this_board->ai_ns_min) cmd->convert_arg=this_board->ai_ns_min;
746 i8253_cascade_ns_to_timer(this_board->i8254_osc_base,
747 &divisor1, &divisor2, &cmd->convert_arg,
748 cmd->flags&TRIG_ROUND_MASK);
751 start_pacer(dev, -1, 0, 0); // stop pacer
753 devpriv->ai_n_chan=cmd->chanlist_len;
754 memcpy(devpriv->ai_chanlist,cmd->chanlist,sizeof(unsigned int)*cmd->scan_end_arg);
755 setup_range_channel(dev, s, devpriv->ai_chanlist[0], 1); // select first channel and range
757 if (devpriv->dma) { // check if we can use DMA transfer
759 for (i=1; i<devpriv->ai_n_chan; i++)
760 if (devpriv->ai_chanlist[0]!=devpriv->ai_chanlist[i]) {
761 devpriv->ai_dma=0; // we cann't use DMA :-(
764 } else devpriv->ai_dma=0;
766 devpriv->ai_flags=cmd->flags;
767 devpriv->ai_data_len=s->async->prealloc_bufsz;
768 devpriv->ai_data=s->async->prealloc_buf;
769 if (cmd->stop_src==TRIG_COUNT) { devpriv->ai_scans=cmd->stop_arg; devpriv->ai_neverending=0; }
770 else { devpriv->ai_scans=0; devpriv->ai_neverending=1; }
772 devpriv->ai_act_scan=0;
773 devpriv->ai_poll_ptr=0;
774 s->async->cur_chan=0;
776 if ((devpriv->ai_flags & TRIG_WAKE_EOS)) { // don't we want wake up every scan?
778 if (devpriv->ai_n_chan==1)
779 devpriv->ai_dma=0; // DMA is useless for this situation
782 if (devpriv->ai_dma) {
783 if (devpriv->ai_eos) { // we use EOS, so adapt DMA buffer to one scan
784 devpriv->dmabytestomove[0]=devpriv->ai_n_chan*sizeof(sampl_t);
785 devpriv->dmabytestomove[1]=devpriv->ai_n_chan*sizeof(sampl_t);
786 devpriv->dma_runs_to_end=1;
788 devpriv->dmabytestomove[0]=devpriv->hwdmasize[0];
789 devpriv->dmabytestomove[1]=devpriv->hwdmasize[1];
790 if (devpriv->ai_data_len<devpriv->hwdmasize[0])
791 devpriv->dmabytestomove[0]=devpriv->ai_data_len;
792 if (devpriv->ai_data_len<devpriv->hwdmasize[1])
793 devpriv->dmabytestomove[1]=devpriv->ai_data_len;
794 if (devpriv->ai_neverending) {
795 devpriv->dma_runs_to_end=1;
797 bytes=devpriv->ai_n_chan*devpriv->ai_scans*sizeof(sampl_t); // how many samples we must transfer?
798 devpriv->dma_runs_to_end=bytes / devpriv->dmabytestomove[0]; // how many DMA pages we must fill
799 devpriv->last_dma_run=bytes % devpriv->dmabytestomove[0]; //on last dma transfer must be moved
800 if (devpriv->dma_runs_to_end==0)
801 devpriv->dmabytestomove[0]=devpriv->last_dma_run;
802 devpriv->dma_runs_to_end--;
805 if (devpriv->dmabytestomove[0]>devpriv->hwdmasize[0]) {
806 devpriv->dmabytestomove[0]=devpriv->hwdmasize[0];
809 if (devpriv->dmabytestomove[1]>devpriv->hwdmasize[1]) {
810 devpriv->dmabytestomove[1]=devpriv->hwdmasize[1];
813 devpriv->next_dma_buf=0;
814 set_dma_mode(devpriv->dma, DMA_MODE_READ);
815 dma_flags=claim_dma_lock();
816 clear_dma_ff(devpriv->dma);
817 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
818 set_dma_count(devpriv->dma, devpriv->dmabytestomove[0]);
819 release_dma_lock(dma_flags);
820 enable_dma(devpriv->dma);
821 #ifdef PCL812_EXTDEBUG
822 rt_printk("pcl812 EDBG: DMA %d PTR 0x%0x/0x%0x LEN %u/%u EOS %d\n",
823 devpriv->dma,devpriv->hwdmaptr[0],devpriv->hwdmaptr[1],
824 devpriv->dmabytestomove[0],devpriv->dmabytestomove[1],
829 switch (cmd->convert_src) {
831 start_pacer(dev, 1, divisor1, divisor2);
835 if (devpriv->ai_dma) {
836 outb(devpriv->mode_reg_int|2, dev->iobase + PCL812_MODE); // let's go!
838 outb(devpriv->mode_reg_int|6, dev->iobase + PCL812_MODE); // let's go!
841 #ifdef PCL812_EXTDEBUG
842 rt_printk("pcl812 EDBG: END: pcl812_ai_cmd(...)\n");
849 ==============================================================================
851 static irqreturn_t interrupt_pcl812_ai_int(int irq, void *d)
854 unsigned int mask, timeout;
855 comedi_device *dev = d;
856 comedi_subdevice *s = dev->subdevices + 0;
858 s->async->events = 0;
860 timeout = 50; /* wait max 50us, it must finish under 33us */
861 if (devpriv->ai_is16b) {
864 if (!(inb(dev->iobase + ACL8216_STATUS) & ACL8216_DRDY)) {
872 if (!(inb(dev->iobase + PCL812_AD_HI) & PCL812_DRDY)) {
880 rt_printk("comedi%d: pcl812: (%s at 0x%lx) A/D cmd IRQ without DRDY!\n", dev->minor, dev->board_name, dev->iobase);
881 pcl812_ai_cancel(dev,s);
882 s->async->events |= COMEDI_CB_EOA|COMEDI_CB_ERROR;
883 comedi_event(dev,s,s->async->events);
887 comedi_buf_put( s->async,
888 ((inb(dev->iobase + PCL812_AD_HI) << 8) | inb(dev->iobase + PCL812_AD_LO)) & mask);
890 outb(0, dev->iobase + PCL812_CLRINT); /* clear INT request */
892 if (s->async->cur_chan == 0 ) { /* one scan done */
893 devpriv->ai_act_scan++;
894 if (!(devpriv->ai_neverending))
895 if (devpriv->ai_act_scan>=devpriv->ai_scans) { /* all data sampled */
896 pcl812_ai_cancel(dev,s);
897 s->async->events |= COMEDI_CB_EOA;
901 comedi_event(dev,s,s->async->events);
906 ==============================================================================
908 static void transfer_from_dma_buf(comedi_device *dev,comedi_subdevice *s,
909 sampl_t *ptr, unsigned int bufptr, unsigned int len)
913 s->async->events = 0;
914 for (i=len; i; i--) {
915 comedi_buf_put( s->async, ptr[bufptr++] ); // get one sample
917 if(s->async->cur_chan == 0){
918 devpriv->ai_act_scan++;
919 if (!devpriv->ai_neverending)
920 if (devpriv->ai_act_scan>=devpriv->ai_scans) { /* all data sampled */
921 pcl812_ai_cancel(dev,s);
922 s->async->events |= COMEDI_CB_EOA;
928 comedi_event(dev,s,s->async->events);
932 ==============================================================================
934 static irqreturn_t interrupt_pcl812_ai_dma(int irq, void *d)
936 comedi_device *dev = d;
937 comedi_subdevice *s = dev->subdevices + 0;
938 unsigned long dma_flags;
942 #ifdef PCL812_EXTDEBUG
943 rt_printk("pcl812 EDBG: BGN: interrupt_pcl812_ai_dma(...)\n");
945 ptr=(sampl_t *)devpriv->dmabuf[devpriv->next_dma_buf];
946 len=(devpriv->dmabytestomove[devpriv->next_dma_buf] >> 1) - devpriv->ai_poll_ptr;
948 devpriv->next_dma_buf=1-devpriv->next_dma_buf;
949 disable_dma(devpriv->dma);
950 set_dma_mode(devpriv->dma, DMA_MODE_READ);
951 dma_flags=claim_dma_lock();
952 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[devpriv->next_dma_buf]);
953 if (devpriv->ai_eos) {
954 set_dma_count(devpriv->dma, devpriv->dmabytestomove[devpriv->next_dma_buf]);
956 if (devpriv->dma_runs_to_end) { set_dma_count(devpriv->dma, devpriv->dmabytestomove[devpriv->next_dma_buf]); }
957 else { set_dma_count(devpriv->dma, devpriv->last_dma_run); }
958 devpriv->dma_runs_to_end--;
960 release_dma_lock(dma_flags);
961 enable_dma(devpriv->dma);
963 outb(0,dev->iobase+PCL812_CLRINT); /* clear INT request */
965 bufptr=devpriv->ai_poll_ptr;
966 devpriv->ai_poll_ptr=0;
968 transfer_from_dma_buf(dev, s, ptr, bufptr, len);
970 #ifdef PCL812_EXTDEBUG
971 rt_printk("pcl812 EDBG: END: interrupt_pcl812_ai_dma(...)\n");
977 ==============================================================================
979 static irqreturn_t interrupt_pcl812(int irq, void *d PT_REGS_ARG)
981 comedi_device *dev = d;
983 if (devpriv->ai_dma) { return interrupt_pcl812_ai_dma(irq, d); }
984 else { return interrupt_pcl812_ai_int(irq, d); };
988 ==============================================================================
990 static int pcl812_ai_poll(comedi_device *dev,comedi_subdevice *s)
993 unsigned int top1,top2,i;
995 if (!devpriv->ai_dma) return 0; // poll is valid only for DMA transfer
997 comedi_spin_lock_irqsave(&dev->spinlock,flags);
999 for (i=0; i<10; i++) {
1000 top1=get_dma_residue(devpriv->ai_dma); // where is now DMA
1001 top2=get_dma_residue(devpriv->ai_dma);
1002 if (top1==top2) break;
1006 comedi_spin_unlock_irqrestore(&dev->spinlock,flags);
1010 top1=devpriv->dmabytestomove[1-devpriv->next_dma_buf]-top1; // where is now DMA in buffer
1011 top1>>=1; // sample position
1012 top2=top1-devpriv->ai_poll_ptr;
1013 if (top2<1) { // no new samples
1014 comedi_spin_unlock_irqrestore(&dev->spinlock,flags);
1018 transfer_from_dma_buf(dev, s, (void *)devpriv->dmabuf[1-devpriv->next_dma_buf],
1019 devpriv->ai_poll_ptr, top2);
1021 devpriv->ai_poll_ptr=top1; // new buffer position
1023 comedi_spin_unlock_irqrestore(&dev->spinlock,flags);
1025 return s->async->buf_write_count-s->async->buf_read_count;
1029 ==============================================================================
1031 static void setup_range_channel(comedi_device * dev, comedi_subdevice * s,
1032 unsigned int rangechan, char wait)
1034 unsigned char chan_reg=CR_CHAN(rangechan); // normal board
1035 unsigned char gain_reg=CR_RANGE(rangechan)+devpriv->range_correction; // gain index
1037 if ((chan_reg==devpriv->old_chan_reg)&&(gain_reg==devpriv->old_gain_reg))
1038 return; // we can return, no change
1040 devpriv->old_chan_reg=chan_reg;
1041 devpriv->old_gain_reg=gain_reg;
1043 if (devpriv->use_MPC) {
1044 if (devpriv->use_diff) {
1045 chan_reg=chan_reg | 0x30; // DIFF inputs
1047 if (chan_reg&0x80) {
1048 chan_reg=chan_reg | 0x20; // SE inputs 8-15
1050 chan_reg=chan_reg | 0x10; // SE inputs 0-7
1055 outb(chan_reg, dev->iobase + PCL812_MUX); /* select channel */
1056 outb(gain_reg, dev->iobase + PCL812_GAIN); /* select gain */
1059 comedi_udelay(devpriv->max_812_ai_mode0_rangewait); // XXX this depends on selected range and can be very long for some high gain ranges!
1065 ==============================================================================
1067 static void start_pacer(comedi_device * dev, int mode, unsigned int divisor1, unsigned int divisor2)
1069 #ifdef PCL812_EXTDEBUG
1070 rt_printk("pcl812 EDBG: BGN: start_pacer(%d,%u,%u)\n",mode,divisor1,divisor2);
1072 outb(0xb4, dev->iobase + PCL812_CTRCTL);
1073 outb(0x74, dev->iobase + PCL812_CTRCTL);
1077 outb(divisor2 & 0xff, dev->iobase + PCL812_CTR2);
1078 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL812_CTR2);
1079 outb(divisor1 & 0xff, dev->iobase + PCL812_CTR1);
1080 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL812_CTR1);
1082 #ifdef PCL812_EXTDEBUG
1083 rt_printk("pcl812 EDBG: END: start_pacer(...)\n");
1088 ==============================================================================
1090 static void free_resources(comedi_device * dev)
1094 if (devpriv->dmabuf[0])
1095 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1096 if (devpriv->dmabuf[1])
1097 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
1099 free_dma(devpriv->dma);
1102 comedi_free_irq(dev->irq, dev);
1104 release_region(dev->iobase, this_board->io_range);
1108 ==============================================================================
1110 static int pcl812_ai_cancel(comedi_device * dev, comedi_subdevice * s)
1112 #ifdef PCL812_EXTDEBUG
1113 rt_printk("pcl812 EDBG: BGN: pcl812_ai_cancel(...)\n");
1115 if (devpriv->ai_dma) disable_dma(devpriv->dma);
1116 outb(0, dev->iobase + PCL812_CLRINT); /* clear INT request */
1117 outb(devpriv->mode_reg_int|0, dev->iobase + PCL812_MODE); /* Stop A/D */
1118 start_pacer(dev,-1,0,0); // stop 8254
1119 outb(0, dev->iobase + PCL812_CLRINT); /* clear INT request */
1120 #ifdef PCL812_EXTDEBUG
1121 rt_printk("pcl812 EDBG: END: pcl812_ai_cancel(...)\n");
1127 ==============================================================================
1129 static void pcl812_reset(comedi_device * dev)
1131 #ifdef PCL812_EXTDEBUG
1132 rt_printk("pcl812 EDBG: BGN: pcl812_reset(...)\n");
1134 outb(0, dev->iobase + PCL812_MUX);
1135 outb(0+devpriv->range_correction, dev->iobase + PCL812_GAIN);
1136 devpriv->old_chan_reg=-1; // invalidate chain/gain memory
1137 devpriv->old_gain_reg=-1;
1139 switch (this_board->board_type) {
1144 outb(0, dev->iobase + PCL812_DA2_LO);
1145 outb(0, dev->iobase + PCL812_DA2_HI);
1147 outb(0, dev->iobase + PCL812_DA1_LO);
1148 outb(0, dev->iobase + PCL812_DA1_HI);
1149 start_pacer(dev,-1,0,0); // stop 8254
1150 outb(0, dev->iobase + PCL812_DO_HI);
1151 outb(0, dev->iobase + PCL812_DO_LO);
1152 outb(devpriv->mode_reg_int|0, dev->iobase + PCL812_MODE);
1153 outb(0, dev->iobase + PCL812_CLRINT);
1163 #ifdef PCL812_EXTDEBUG
1164 rt_printk("pcl812 EDBG: END: pcl812_reset(...)\n");
1170 ==============================================================================
1172 static int pcl812_attach(comedi_device * dev, comedi_devconfig * it)
1175 unsigned long iobase;
1178 unsigned long pages;
1179 comedi_subdevice *s;
1182 iobase = it->options[0];
1183 printk("comedi%d: pcl812: board=%s, ioport=0x%03lx", dev->minor,
1184 this_board->name, iobase);
1186 if (!request_region(iobase, this_board->io_range, "pcl812")) {
1187 printk("I/O port conflict\n");
1190 dev->iobase = iobase;
1192 if ((ret = alloc_private(dev, sizeof(pcl812_private))) < 0) {
1193 free_resources(dev);
1194 return ret; /* Can't alloc mem */
1197 dev->board_name = this_board->name;
1200 if (this_board->IRQbits != 0) { /* board support IRQ */
1201 irq = it->options[1];
1202 if (irq) { /* we want to use IRQ */
1203 if (((1 << irq) & this_board->IRQbits) == 0) {
1204 printk(", IRQ %u is out of allowed range, DISABLING IT", irq);
1205 irq = 0; /* Bad IRQ */
1207 if (comedi_request_irq(irq, interrupt_pcl812, 0, "pcl812", dev)) {
1208 printk(", unable to allocate IRQ %u, DISABLING IT", irq);
1209 irq = 0; /* Can't use IRQ */
1211 printk(", irq=%u", irq);
1222 goto no_dma; /* if we haven't IRQ, we can't use DMA */
1223 if (this_board->DMAbits != 0) { /* board support DMA */
1224 dma = it->options[2];
1225 if (((1 << dma) & this_board->DMAbits) == 0) {
1226 printk(", DMA is out of allowed range, FAIL!\n");
1227 return -EINVAL; /* Bad DMA */
1229 ret = request_dma(dma, "pcl812");
1231 printk(", unable to allocate DMA %u, FAIL!\n", dma);
1232 return -EBUSY; /* DMA isn't free */
1235 printk(", dma=%u", dma);
1236 pages = 1; /* we want 8KB */
1237 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1238 if (!devpriv->dmabuf[0]) {
1239 printk(", unable to allocate DMA buffer, FAIL!\n");
1240 /* maybe experiment with try_to_free_pages() will help .... */
1241 free_resources(dev);
1242 return -EBUSY; /* no buffer :-( */
1244 devpriv->dmapages[0] = pages;
1245 devpriv->hwdmaptr[0] = virt_to_bus((void *) devpriv->dmabuf[0]);
1246 devpriv->hwdmasize[0] = PAGE_SIZE * (1<<pages);
1247 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1248 if (!devpriv->dmabuf[1]) {
1249 printk(", unable to allocate DMA buffer, FAIL!\n");
1250 free_resources(dev);
1253 devpriv->dmapages[1] = pages;
1254 devpriv->hwdmaptr[1] = virt_to_bus((void *) devpriv->dmabuf[1]);
1255 devpriv->hwdmasize[1] = PAGE_SIZE * (1<<pages);
1260 if (this_board->n_aichan > 0) n_subdevices++;
1261 if (this_board->n_aochan > 0) n_subdevices++;
1262 if (this_board->n_dichan > 0) n_subdevices++;
1263 if (this_board->n_dochan > 0) n_subdevices++;
1265 if ((ret = alloc_subdevices(dev, n_subdevices)) < 0) {
1266 free_resources(dev);
1273 if (this_board->n_aichan>0) {
1274 s = dev->subdevices + subdev;
1275 s->type = COMEDI_SUBD_AI;
1276 s->subdev_flags = SDF_READABLE;
1277 switch (this_board->board_type) {
1279 if (it->options[2] == 1) {
1280 s->n_chan = this_board->n_aichan_diff;
1281 s->subdev_flags |= SDF_DIFF;
1282 devpriv->use_diff=1;
1284 s->n_chan = this_board->n_aichan;
1285 s->subdev_flags |= SDF_GROUND;
1290 if (it->options[4] == 1) {
1291 s->n_chan = this_board->n_aichan_diff;
1292 s->subdev_flags |= SDF_DIFF;
1293 devpriv->use_diff=1;
1295 s->n_chan = this_board->n_aichan;
1296 s->subdev_flags |= SDF_GROUND;
1300 s->n_chan = this_board->n_aichan;
1301 s->subdev_flags |= SDF_GROUND;
1304 s->maxdata = this_board->ai_maxdata;
1305 s->len_chanlist = MAX_CHANLIST_LEN;
1306 s->range_table = this_board->rangelist_ai;
1307 if (this_board->board_type==boardACL8216) {
1308 s->insn_read = acl8216_ai_insn_read;
1310 s->insn_read = pcl812_ai_insn_read;
1312 devpriv->use_MPC = this_board->haveMPC508;
1313 s->cancel = pcl812_ai_cancel;
1315 dev->read_subdev = s;
1316 s->subdev_flags |= SDF_CMD_READ;
1317 s->do_cmdtest = pcl812_ai_cmdtest;
1318 s->do_cmd = pcl812_ai_cmd;
1319 s->poll = pcl812_ai_poll;
1321 switch (this_board->board_type) {
1323 if (it->options[4] == 1)
1324 s->range_table = &range_pcl812pg2_ai;
1327 switch (it->options[4]) {
1328 case 0: s->range_table = &range_bipolar10; break;
1329 case 1: s->range_table = &range_bipolar5; break;
1330 case 2: s->range_table = &range_bipolar2_5; break;
1331 case 3: s->range_table = &range812_bipolar1_25; break;
1332 case 4: s->range_table = &range812_bipolar0_625; break;
1333 case 5: s->range_table = &range812_bipolar0_3125; break;
1335 s->range_table = &range_bipolar10; break;
1336 printk(", incorrect range number %d, changing to 0 (+/-10V)", it->options[4]);
1342 if (it->options[1] == 1)
1343 s->range_table = &range_pcl813b2_ai;
1346 switch (it->options[1]) {
1347 case 0: s->range_table = &range_iso813_1_ai; break;
1348 case 1: s->range_table = &range_iso813_1_2_ai; break;
1349 case 2: s->range_table = &range_iso813_2_ai; devpriv->range_correction=1; break;
1350 case 3: s->range_table = &range_iso813_2_2_ai; devpriv->range_correction=1; break;
1352 s->range_table = &range_iso813_1_ai; break;
1353 printk(", incorrect range number %d, changing to 0 ", it->options[1]);
1358 switch (it->options[1]) {
1359 case 0: s->range_table = &range_acl8113_1_ai; break;
1360 case 1: s->range_table = &range_acl8113_1_2_ai; break;
1361 case 2: s->range_table = &range_acl8113_2_ai; devpriv->range_correction=1; break;
1362 case 3: s->range_table = &range_acl8113_2_2_ai; devpriv->range_correction=1; break;
1364 s->range_table = &range_acl8113_1_ai; break;
1365 printk(", incorrect range number %d, changing to 0 ", it->options[1]);
1374 if (this_board->n_aochan>0) {
1375 s = dev->subdevices + subdev;
1376 s->type = COMEDI_SUBD_AO;
1377 s->subdev_flags = SDF_WRITABLE|SDF_GROUND;
1378 s->n_chan = this_board->n_aochan;
1380 s->len_chanlist = 1;
1381 s->range_table = this_board->rangelist_ao;
1382 s->insn_read = pcl812_ao_insn_read;
1383 s->insn_write = pcl812_ao_insn_write;
1384 switch (this_board->board_type) {
1386 if (it->options[3] == 1)
1387 s->range_table = &range_unipolar10;
1393 if (it->options[5] == 1)
1394 s->range_table = &range_unipolar10;
1395 if (it->options[5] == 2)
1396 s->range_table = &range_unknown;
1403 if (this_board->n_dichan>0) {
1404 s = dev->subdevices + subdev;
1405 s->type = COMEDI_SUBD_DI;
1406 s->subdev_flags = SDF_READABLE;
1407 s->n_chan = this_board->n_dichan;
1409 s->len_chanlist = this_board->n_dichan;
1410 s->range_table = &range_digital;
1411 s->insn_bits = pcl812_di_insn_bits;
1415 /* digital output */
1416 if (this_board->n_dochan>0) {
1417 s = dev->subdevices + subdev;
1418 s->type = COMEDI_SUBD_DO;
1419 s->subdev_flags = SDF_WRITABLE;
1420 s->n_chan = this_board->n_dochan;
1422 s->len_chanlist = this_board->n_dochan;
1423 s->range_table = &range_digital;
1424 s->insn_bits = pcl812_do_insn_bits;
1428 switch (this_board->board_type) {
1430 devpriv->ai_is16b=1;
1434 devpriv->max_812_ai_mode0_rangewait = 1;
1435 if (it->options[3] > 0) devpriv->use_ext_trg=1; // we use external trigger
1437 devpriv->max_812_ai_mode0_rangewait = 1;
1438 devpriv->mode_reg_int=(irq<<4) & 0xf0;
1444 devpriv->max_812_ai_mode0_rangewait = 5; /* maybe there must by greatest timeout */
1458 ==============================================================================
1460 static int pcl812_detach(comedi_device * dev)
1463 #ifdef PCL812_EXTDEBUG
1464 rt_printk("comedi%d: pcl812: remove\n", dev->minor);
1466 free_resources(dev);