Globally fix spelling of SDF_WRITABLE
[comedi.git] / comedi / drivers / pcl818.c
1 /*
2    module/pcl818.c
3
4    Author:  Michal Dobes <majkl@tesnet.cz>  
5
6    hardware driver for Advantech cards:
7     card:   PCL-818L, PCL-818H, PCL-818HD, PCL-818HG, PCL-818, PCL-718
8     driver: pcl818l,  pcl818h,  pcl818hd,  pcl818hg,  pcl818,  pcl718
9 */
10 /*
11 Driver: pcl818.o
12 Description: Advantech PCL-818 cards, PCL-718
13 Author: Michal Dobes <majkl@tesnet.cz>
14 Devices: [Advantech] PCL-818L (pcl818l), PCL-818H (pcl818h),
15   PCL-818HD (pcl818hd), PCL-818HG (pcl818hg), PCL-818 (pcl818),
16   PCL-718 (pcl718)
17 Status: works
18
19 All cards have 16 SE/8 DIFF ADCs, one or two DACs, 16 DI and 16 DO.
20 Differences are only at maximal sample speed, range list and FIFO
21 support.
22 The driver support AI mode 0, 1, 3 other subdevices (AO, DI, DO) support
23 only mode 0. If DMA/FIFO/INT are disabled then AI support only mode 0.
24 PCL-818HD and PCL-818HG support 1kword FIFO. Driver support this FIFO
25 but this code is untested.
26 A word or two about DMA. Driver support DMA operations at two ways:
27 1) DMA uses two buffers and after one is filled then is generated
28    INT and DMA restart with second buffer. With this mode I'm unable run
29    more that 80Ksamples/secs without data dropouts on K6/233.
30 2) DMA uses one buffer and run in autoinit mode and the data are
31    from DMA buffer moved on the fly with 2kHz interrupts from RTC.
32    This mode is used if the interrupt 8 is available for allocation.
33    If not, then first DMA mode is used. With this I can run at
34    full speed one card (100ksamples/secs) or two cards with
35    60ksamples/secs each (more is problem on account of ISA limitations).
36    To use this mode you must have compiled  kernel with disabled
37    "Enhanced Real Time Clock Support".
38    Maybe you can have problems if you use xntpd or similar.
39    If you've data dropouts with DMA mode 2 then:
40     a) disable IDE DMA
41     b) switch text mode console to fb.
42
43    Options for PCL-818L:
44     [0] - IO Base
45     [1] - IRQ   (0=disable, 2, 3, 4, 5, 6, 7)
46     [2] - DMA   (0=disable, 1, 3)
47     [3] - 0, 10=10MHz clock for 8254
48               1= 1MHz clock for 8254
49     [4] - 0,  5=A/D input  -5V.. +5V
50           1, 10=A/D input -10V..+10V
51     [5] - 0,  5=D/A output 0-5V  (internal reference -5V)
52           1, 10=D/A output 0-10V (internal reference -10V)
53           2    =D/A output unknow (external reference)
54        
55    Options for PCL-818, PCL-818H:
56     [0] - IO Base
57     [1] - IRQ   (0=disable, 2, 3, 4, 5, 6, 7)
58     [2] - DMA   (0=disable, 1, 3)
59     [3] - 0, 10=10MHz clock for 8254
60               1= 1MHz clock for 8254
61     [4] - 0,  5=D/A output 0-5V  (internal reference -5V)
62           1, 10=D/A output 0-10V (internal reference -10V)
63           2    =D/A output unknow (external reference)
64
65    Options for PCL-818HD, PCL-818HG:
66     [0] - IO Base
67     [1] - IRQ   (0=disable, 2, 3, 4, 5, 6, 7)
68     [2] - DMA/FIFO  (-1=use FIFO, 0=disable both FIFO and DMA, 
69                       1=use DMA ch 1, 3=use DMA ch 3)
70     [3] - 0, 10=10MHz clock for 8254
71               1= 1MHz clock for 8254
72     [4] - 0,  5=D/A output 0-5V  (internal reference -5V)
73           1, 10=D/A output 0-10V (internal reference -10V)
74           2    =D/A output unknow (external reference)
75
76    Options for PCL-718:
77     [0] - IO Base
78     [1] - IRQ   (0=disable, 2, 3, 4, 5, 6, 7)
79     [2] - DMA   (0=disable, 1, 3)
80     [3] - 0, 10=10MHz clock for 8254
81               1= 1MHz clock for 8254
82     [4] -     0=A/D Range is +/-10V
83               1=             +/-5V
84               2=             +/-2.5V
85               3=             +/-1V
86               4=             +/-0.5V 
87               5=             user defined bipolar
88               6=             0-10V
89               7=             0-5V
90               8=             0-2V
91               9=             0-1V
92              10=             user defined unipolar
93     [5] - 0,  5=D/A outputs 0-5V  (internal reference -5V)
94           1, 10=D/A outputs 0-10V (internal reference -10V)
95               2=D/A outputs unknow (external reference)
96     [6] - 0, 60=max  60kHz A/D sampling
97           1,100=max 100kHz A/D sampling (PCL-718 with Option 001 installed)
98
99 */
100
101 #include <linux/ioport.h>
102 #include <linux/module.h>
103 #include <linux/mc146818rtc.h>
104 #include <linux/delay.h>
105 #include <asm/dma.h>
106 #include <linux/comedidev.h>
107 #include "8253.h"
108
109 // #define PCL818_MODE13_AO 1
110
111 // boards constants
112
113 #define boardPCL818L 0
114 #define boardPCL818H 1
115 #define boardPCL818HD 2
116 #define boardPCL818HG 3
117 #define boardPCL818 4
118 #define boardPCL718 5
119
120 // IO space len
121 #define PCLx1x_RANGE 16
122 // IO space len if we use FIFO
123 #define PCLx1xFIFO_RANGE 32
124
125 // W: clear INT request
126 #define PCL818_CLRINT 8
127 // R: return status byte
128 #define PCL818_STATUS 8
129 // R: A/D high byte W: A/D range control
130 #define PCL818_RANGE 1
131 // R: next mux scan channel W: mux scan channel & range control pointer
132 #define PCL818_MUX 2
133 // R/W: operation control register
134 #define PCL818_CONTROL 9
135 // W: counter enable
136 #define PCL818_CNTENABLE 10
137
138 // R: low byte of A/D W: soft A/D trigger
139 #define PCL818_AD_LO 0
140 // R: high byte of A/D W: A/D range control
141 #define PCL818_AD_HI 1
142 // W: D/A low&high byte
143 #define PCL818_DA_LO 4
144 #define PCL818_DA_HI 5
145 // R: low&high byte of DI 
146 #define PCL818_DI_LO 3
147 #define PCL818_DI_HI 11
148 // W: low&high byte of DO
149 #define PCL818_DO_LO 3
150 #define PCL818_DO_HI 11
151 // W: PCL718 second D/A
152 #define PCL718_DA2_LO 6
153 #define PCL718_DA2_HI 7
154 // counters
155 #define PCL818_CTR0 12
156 #define PCL818_CTR1 13
157 #define PCL818_CTR2 14
158 // W: counter control
159 #define PCL818_CTRCTL 15
160
161 // W: fifo enable/disable
162 #define PCL818_FI_ENABLE 6
163 // W: fifo interrupt clear
164 #define PCL818_FI_INTCLR 20
165 // W: fifo interrupt clear
166 #define PCL818_FI_FLUSH 25
167 // R: fifo status
168 #define PCL818_FI_STATUS 25
169 // R: one record from FIFO
170 #define PCL818_FI_DATALO 23
171 #define PCL818_FI_DATAHI 23
172
173 // type of interrupt handler
174 #define INT_TYPE_AI1_INT 1
175 #define INT_TYPE_AI1_DMA 2 
176 #define INT_TYPE_AI1_FIFO 3
177 #define INT_TYPE_AI3_INT 4
178 #define INT_TYPE_AI3_DMA 5
179 #define INT_TYPE_AI3_FIFO 6
180 #ifdef PCL818_MODE13_AO
181 #define INT_TYPE_AO1_INT 7
182 #define INT_TYPE_AO3_INT 8
183 #endif
184 #define INT_TYPE_AI1_DMA_RTC 9
185 #define INT_TYPE_AI3_DMA_RTC 10
186
187 // RTC stuff...
188 #define RTC_IRQ         8
189 #define RTC_IO_EXTENT   0x10
190
191 #define MAGIC_DMA_WORD 0x5a5a
192
193 static comedi_lrange range_pcl818h_ai = { 9, {
194         BIP_RANGE(5),
195         BIP_RANGE(2.5),
196         BIP_RANGE(1.25),
197         BIP_RANGE(0.625),
198         UNI_RANGE(10),
199         UNI_RANGE(5),
200         UNI_RANGE(2.5),
201         UNI_RANGE(1.25),
202         BIP_RANGE(10),
203 }};
204
205 static comedi_lrange range_pcl818hg_ai = { 10, {
206         BIP_RANGE(5),
207         BIP_RANGE(0.5),
208         BIP_RANGE(0.05),
209         BIP_RANGE(0.005),
210         UNI_RANGE(10),
211         UNI_RANGE(1),
212         UNI_RANGE(0.1),
213         UNI_RANGE(0.01),
214         BIP_RANGE(10),
215         BIP_RANGE(1),
216         BIP_RANGE(0.1),
217         BIP_RANGE(0.01),
218 }};
219
220 static comedi_lrange range_pcl818l_l_ai = { 4, {
221         BIP_RANGE(5),
222         BIP_RANGE(2.5),
223         BIP_RANGE(1.25),
224         BIP_RANGE(0.625),
225 }};
226
227 static comedi_lrange range_pcl818l_h_ai = { 4, {
228         BIP_RANGE(10),
229         BIP_RANGE(5),
230         BIP_RANGE(2.5),
231         BIP_RANGE(1.25),
232 }};
233
234 static comedi_lrange range718_bipolar1 = { 1, { BIP_RANGE(1), }};
235 static comedi_lrange range718_bipolar0_5 = { 1, { BIP_RANGE(0.5), }};
236 static comedi_lrange range718_unipolar2 = { 1, { UNI_RANGE(2), }};
237 static comedi_lrange range718_unipolar1 = { 1, { BIP_RANGE(1), }};
238
239 static int pcl818_attach(comedi_device *dev,comedi_devconfig *it);
240 static int pcl818_detach(comedi_device *dev);
241
242 static int RTC_lock = 0;                /* RTC lock */
243 static int RTC_timer_lock = 0;          /* RTC int lock */
244
245 typedef struct {
246         char            *name;          // driver name
247         int             n_ranges;       // len of range list
248         int             n_aichan_se;    // num of A/D chans in single ended  mode
249         int             n_aichan_diff;  // num of A/D chans in diferencial mode
250         unsigned int    ns_min;         // minimal alllowed delay between samples (in ns)
251         int             n_aochan;       // num of D/A chans
252         int             n_dichan;       // num of DI chans
253         int             n_dochan;       // num of DO chans
254         comedi_lrange   *ai_range_type; // default A/D rangelist
255         comedi_lrange   *ao_range_type; // dafault D/A rangelist
256         int             io_range;       // len of IO space
257         unsigned int    IRQbits;        // allowed interrupts
258         unsigned int    DMAbits;        // allowed DMA chans
259         int             ai_maxdata;     // maxdata for A/D
260         int             ao_maxdata;     // maxdata for D/A           
261         int             ai_chanlist;    // allowed len of channel list A/D
262         int             ao_chanlist;    // allowed len of channel list D/A
263         unsigned char   fifo;           // 1=board has FIFO
264         int             is_818;
265 } boardtype;
266
267 static boardtype boardtypes[] =
268 {
269         {"pcl818l",   4, 16, 8, 25000, 1, 16, 16, &range_pcl818l_l_ai, &range_unipolar5, PCLx1x_RANGE, 0x00fc, 
270           0x0a, 0xfff, 0xfff, 1024, 1, 0, 1 },
271         {"pcl818h",   9, 16, 8, 10000, 1, 16, 16, &range_pcl818h_ai,   &range_unipolar5, PCLx1x_RANGE, 0x00fc, 
272           0x0a, 0xfff, 0xfff, 1024, 1, 0, 1 },
273         {"pcl818hd",  9, 16, 8, 10000, 1, 16, 16, &range_pcl818h_ai,   &range_unipolar5, PCLx1x_RANGE, 0x00fc, 
274           0x0a, 0xfff, 0xfff, 1024, 1, 1, 1 },
275         {"pcl818hg", 12, 16, 8, 10000, 1, 16, 16, &range_pcl818hg_ai,  &range_unipolar5, PCLx1x_RANGE, 0x00fc,
276           0x0a, 0xfff, 0xfff, 1024, 1, 1, 1 },
277         {"pcl818",    9, 16, 8, 10000, 2, 16, 16, &range_pcl818h_ai,   &range_unipolar5, PCLx1x_RANGE, 0x00fc, 
278           0x0a, 0xfff, 0xfff, 1024, 2, 0, 1 },
279         {"pcl718",    1, 16, 8, 16000, 2, 16, 16, &range_unipolar5,    &range_unipolar5, PCLx1x_RANGE, 0x00fc, 
280           0x0a, 0xfff, 0xfff, 1024, 2, 0, 0 },
281         /* pcm3718 */
282         {"pcm3718",   9, 16, 8, 10000, 0, 16, 16, &range_pcl818h_ai,   &range_unipolar5, PCLx1x_RANGE, 0x00fc, 
283           0x0a, 0xfff, 0xfff, 1024, 0, 0, 1 /* XXX ? */ },
284 };
285
286 #define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
287
288 static comedi_driver driver_pcl818={
289         driver_name:    "pcl818",
290         module:         THIS_MODULE,
291         attach:         pcl818_attach,
292         detach:         pcl818_detach,
293         board_name:     boardtypes,
294         num_names:      n_boardtypes,
295         offset:         sizeof(boardtype),
296 };
297 COMEDI_INITCLEANUP(driver_pcl818);
298
299
300 typedef struct {
301         int             dma;            // used DMA, 0=don't use DMA
302         int             dma_rtc;        // 1=RTC used with DMA, 0=no RTC alloc
303         int             io_range;
304         unsigned int    rtc_iobase;     // RTC port region
305         unsigned int    rtc_iosize;
306         unsigned int    rtc_irq;
307         unsigned long   dmabuf[2];      // pointers to begin of DMA buffers
308         unsigned int    dmapages[2];    // len of DMA buffers in PAGE_SIZEs
309         unsigned int    hwdmaptr[2];    // hardware address of DMA buffers
310         unsigned int    hwdmasize[2];   // len of DMA buffers in Bytes
311         unsigned int    dmasamplsize;   // size in samples hwdmasize[0]/2
312         unsigned int    last_top_dma;   // DMA pointer in last RTC int
313         int             next_dma_buf;   // which DMA buffer will be used next round
314         long            dma_runs_to_end;// how many we must permorm DMA transfer to end of record
315         unsigned long   last_dma_run;   // how many bytes we must transfer on last DMA page
316         unsigned char   neverending_ai; // if=1, then we do neverending record (you must use cancel())
317         unsigned int    ns_min;         // manimal alllowed delay between samples (in us) for actual card
318         int             i8253_osc_base; // 1/frequency of on board oscilator in ns
319         int             irq_free;       // 1=have allocated IRQ
320         int             irq_blocked;    // 1=IRQ now uses any subdev
321         int             rtc_irq_blocked;// 1=we now do AI with DMA&RTC
322         int             irq_was_now_closed;// when IRQ finish, there's stored int818_mode for last interrupt
323         int             int818_mode;    // who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma 
324         comedi_subdevice *last_int_sub; // ptr to subdevice which now finish
325         int             int13_act_scan; // how many scans we finished
326         int             int13_act_chan; // actual position in actual scan
327         unsigned int    act_chanlist[16];// MUX setting for actual AI operations
328         unsigned int    act_chanlist_len;// how long is actual MUX list
329         unsigned int    act_chanlist_pos;// actual position in MUX list
330         unsigned int    buf_ptr;        // data buffer ptr in samples
331         comedi_subdevice *sub_ai;       // ptr to AI subdevice
332         unsigned char   usefifo;        // 1=use fifo
333         struct timer_list rtc_irq_timer;// timer for RTC sanity check
334         unsigned long   rtc_freq;       // RTC int freq
335         lsampl_t        ao_readback[2];
336 } pcl818_private;
337
338
339 static unsigned int muxonechan[] ={ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, // used for gain list programming
340                                     0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
341
342 #define devpriv ((pcl818_private *)dev->private)
343 #define this_board ((boardtype *)dev->board_ptr)
344
345 /* 
346 ==============================================================================
347 */
348 #ifdef unused
349 static int check_and_setup_channel_list(comedi_device * dev, comedi_subdevice * s, comedi_trig * it);
350 #endif
351 static int pcl818_ai_cancel(comedi_device * dev, comedi_subdevice * s);
352 #ifdef unused
353 static void start_pacer(comedi_device * dev, int mode, unsigned int divisor1, unsigned int divisor2);
354 #endif
355 static int set_rtc_irq_bit(unsigned char bit);
356 #ifdef unused
357 static void rtc_dropped_irq(unsigned long data);
358 static int rtc_setfreq_irq(int freq);
359 #endif
360
361 /* 
362 ==============================================================================
363    ANALOG INPUT MODE0, 818 cards, slow version
364 */
365 static int pcl818_ai_insn_read(comedi_device *dev, comedi_subdevice *s,
366         comedi_insn *insn, lsampl_t *data)
367 {
368         int n;
369         int timeout; 
370
371         /* software trigger, DMA and INT off */
372         outb(0, dev->iobase+PCL818_CONTROL);
373
374         /* select channel */
375         outb(muxonechan[CR_CHAN(insn->chanspec)],
376                 dev->iobase+PCL818_MUX);
377
378         /* select gain */
379         outb(CR_RANGE(insn->chanspec),
380                 dev->iobase+PCL818_RANGE);
381
382         for(n=0;n<insn->n;n++){
383
384                 /* clear INT (conversion end) flag */
385                 outb(0, dev->iobase+PCL818_CLRINT);
386
387                 /* start conversion */
388                 outb(0, dev->iobase+PCL818_AD_LO);
389
390                 timeout=100;
391                 while (timeout--) {
392                         if (inb(dev->iobase + PCL818_STATUS) & 0x10)
393                                 goto conv_finish;
394                         udelay(1);
395                 }
396                 comedi_error(dev,"A/D insn timeout");
397                 /* clear INT (conversion end) flag */
398                 outb(0, dev->iobase+PCL818_CLRINT);
399                 return -EIO;
400
401 conv_finish:
402                 data[n] = ((inb(dev->iobase + PCL818_AD_HI) << 4) |
403                         (inb(dev->iobase + PCL818_AD_LO) >> 4));
404         }
405
406         return n;
407 }
408
409 /* 
410 ==============================================================================
411    ANALOG OUTPUT MODE0, 818 cards
412    only one sample per call is supported
413 */
414 static int pcl818_ao_insn_read(comedi_device *dev, comedi_subdevice *s,
415         comedi_insn *insn, lsampl_t *data)
416 {
417         int n;
418         int chan = CR_CHAN(insn->chanspec);
419
420         for(n=0;n<insn->n;n++){
421                 data[n] = devpriv->ao_readback[chan];
422         }
423
424         return n;
425 }
426
427 static int pcl818_ao_insn_write(comedi_device *dev, comedi_subdevice *s,
428         comedi_insn *insn, lsampl_t *data)
429 {
430         int n;
431         int chan = CR_CHAN(insn->chanspec);
432
433         for(n=0;n<insn->n;n++){
434                 devpriv->ao_readback[chan] = data[n];
435                 outb((data[n] & 0x000f) << 4, dev->iobase+
436                         (chan)?PCL718_DA2_LO:PCL818_DA_LO);
437                 outb((data[n] & 0x0ff0) >> 4, dev->iobase+
438                         (chan)?PCL718_DA2_HI:PCL818_DA_HI);
439         }
440
441         return n;
442 }
443
444 /* 
445 ==============================================================================
446    DIGITAL INPUT MODE0, 818 cards
447    
448    only one sample per call is supported
449 */
450 static int pcl818_di_insn_bits(comedi_device *dev, comedi_subdevice *s,
451         comedi_insn *insn, lsampl_t *data)
452 {
453         if(insn->n!=2)return -EINVAL;
454
455         data[1] = inb(dev->iobase + PCL818_DI_LO) |
456                 (inb(dev->iobase + PCL818_DI_HI) << 8);
457
458         return 2;
459 }
460
461 /* 
462 ==============================================================================
463    DIGITAL OUTPUT MODE0, 818 cards
464    
465    only one sample per call is supported
466 */
467 static int pcl818_do_insn_bits(comedi_device *dev, comedi_subdevice *s,
468         comedi_insn *insn, lsampl_t *data)
469 {
470         if(insn->n!=2)return -EINVAL;
471
472         s->state &= ~data[0];
473         s->state |= (data[0]&data[1]);
474
475         outb(s->state & 0xff, dev->iobase + PCL818_DO_LO);
476         outb((s->state >> 8), dev->iobase + PCL818_DO_HI);
477
478         data[1] = s->state;
479         
480         return 2;
481 }
482
483 /*
484 ==============================================================================
485    analog input interrupt mode 1 & 3, 818 cards
486    one sample per interrupt version   
487 */
488 static void interrupt_pcl818_ai_mode13_int(int irq, void *d, struct pt_regs *regs) 
489 {
490         comedi_device *dev = d;
491         comedi_subdevice *s = dev->subdevices + 0;
492         int low;
493         int timeout=50; /* wait max 50us */
494
495         while (timeout--) {
496                 if (inb(dev->iobase + PCL818_STATUS) & 0x10) goto conv_finish;
497                 udelay(1);
498         }
499         outb(0,dev->iobase+PCL818_STATUS); /* clear INT request */
500         comedi_error(dev,"A/D mode1/3 IRQ without DRDY!");
501         pcl818_ai_cancel(dev,s);
502         s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
503         comedi_event(dev, s, s->async->events);
504         return;
505
506 conv_finish:
507         low=inb(dev->iobase + PCL818_AD_LO);
508         *(sampl_t *)(s->async->data+devpriv->buf_ptr)=((inb(dev->iobase + PCL818_AD_HI) << 4) | (low >> 4)); // get one sample
509         devpriv->buf_ptr+=sizeof(sampl_t);
510         outb(0,dev->iobase+PCL818_CLRINT); /* clear INT request */
511
512         if ((low & 0xf)!=devpriv->act_chanlist[devpriv->act_chanlist_pos]) { // dropout!
513                 rt_printk("comedi: A/D mode1/3 IRQ - channel dropout %x!=%x !\n",(low & 0xf),devpriv->act_chanlist[devpriv->act_chanlist_pos]);
514                 pcl818_ai_cancel(dev,s);
515                 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
516                 comedi_event(dev, s, s->async->events);
517                 return;
518         }
519         s->async->buf_int_ptr+=sizeof(sampl_t);
520         s->async->buf_int_count+=sizeof(sampl_t);
521
522         s->async->cur_chan++;
523         if (s->async->cur_chan>=s->async->cmd.chanlist_len){
524                 s->async->cur_chan=0;
525                 s->async->events |= COMEDI_CB_BLOCK;
526                 // rt_printk("E");
527                 devpriv->int13_act_scan--;
528         }
529
530         if (s->async->buf_int_ptr>=s->async->data_len) { /* buffer rollover */
531                 s->async->buf_int_ptr=0;
532                 devpriv->buf_ptr=0;
533                 //printk("B ");
534                 s->async->events |= COMEDI_CB_EOBUF;
535         }
536
537         if (!devpriv->neverending_ai){
538                 if ( devpriv->int13_act_scan == 0 ) { /* all data sampled */
539                         pcl818_ai_cancel(dev,s);
540                         s->async->events |= COMEDI_CB_EOA;
541                         return;
542                 }
543         }
544         comedi_event(dev, s, s->async->events);
545 }
546
547 /*
548 ==============================================================================
549    analog input dma mode 1 & 3, 818 cards
550 */
551 static void interrupt_pcl818_ai_mode13_dma(int irq, void *d, struct pt_regs *regs)
552 {
553         comedi_device *dev = d;
554         comedi_subdevice *s = dev->subdevices + 0;
555         int i,len,bufptr;
556         unsigned long flags;
557         sampl_t *ptr;
558
559         disable_dma(devpriv->dma);
560         devpriv->next_dma_buf=1-devpriv->next_dma_buf;
561         if ((devpriv->dma_runs_to_end)>-1) {  // switch dma bufs
562                 set_dma_mode(devpriv->dma, DMA_MODE_READ);
563                 flags=claim_dma_lock();
564                 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[devpriv->next_dma_buf]);
565                 if (devpriv->dma_runs_to_end) { set_dma_count(devpriv->dma, devpriv->hwdmasize[devpriv->next_dma_buf]); }
566                                         else { set_dma_count(devpriv->dma, devpriv->last_dma_run); }
567                 release_dma_lock(flags);
568                 enable_dma(devpriv->dma);
569         }
570
571         devpriv->dma_runs_to_end--;
572         outb(0,dev->iobase+PCL818_CLRINT); /* clear INT request */
573         ptr=(sampl_t *)devpriv->dmabuf[1-devpriv->next_dma_buf];
574
575         len=devpriv->hwdmasize[0] >> 1;
576         bufptr=0;
577
578         for (i=0;i<len;i++) {
579                 if ((ptr[bufptr] & 0xf)!=devpriv->act_chanlist[devpriv->act_chanlist_pos]) { // dropout!
580                         rt_printk("comedi: A/D mode1/3 DMA - channel dropout %d!=%d !\n",(ptr[bufptr] & 0xf),devpriv->act_chanlist[devpriv->act_chanlist_pos]);
581                         pcl818_ai_cancel(dev,s);
582                         s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
583                         comedi_event(dev, s, s->async->events);
584                         return;
585                 }
586
587                 *(sampl_t *)(s->async->data+s->async->buf_int_ptr)=
588                         ptr[bufptr++] >> 4; // get one sample
589                 devpriv->buf_ptr++;
590
591                 s->async->buf_int_ptr+=sizeof(sampl_t);
592                 s->async->buf_int_count+=sizeof(sampl_t);
593                 devpriv->act_chanlist_pos++;
594
595                 s->async->cur_chan++;
596                 if(s->async->cur_chan>=s->async->cmd.chanlist_len){
597                         s->async->cur_chan=0;
598                 }
599
600                 if (s->async->buf_int_ptr>=s->async->data_len) { /* buffer rollover */
601                         s->async->buf_int_ptr=0;
602                         devpriv->buf_ptr=0;
603                         s->async->events |= COMEDI_CB_EOBUF;
604                 }
605
606                 if (!devpriv->neverending_ai)
607                         if ( devpriv->int13_act_scan == 0 ) { /* all data sampled */
608                                 pcl818_ai_cancel(dev,s);
609                                 s->async->events |= COMEDI_CB_EOA;
610                                 comedi_event(dev, s, s->async->events);
611                                 // printk("done int ai13 dma\n");
612                                 return;
613                         }
614         }
615
616         if (len>0) comedi_event(dev, s, s->async->events);
617 }
618
619 /*
620 ==============================================================================
621    analog input dma mode 1 & 3 over RTC, 818 cards
622 */
623 static void interrupt_pcl818_ai_mode13_dma_rtc(int irq, void *d, struct pt_regs *regs)
624 {
625         comedi_device *dev = d;
626         comedi_subdevice *s = dev->subdevices + 0;
627         unsigned long tmp;
628         unsigned int top1,top2,i,bufptr;
629         long ofs_dats;
630         sampl_t *dmabuf=(sampl_t *)devpriv->dmabuf[0];
631
632         //outb(2,0x378);
633         switch(devpriv->int818_mode) {
634         case INT_TYPE_AI1_DMA_RTC:
635         case INT_TYPE_AI3_DMA_RTC:
636                 tmp = (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);
637                 mod_timer(&devpriv->rtc_irq_timer, jiffies + HZ/devpriv->rtc_freq + 2*HZ/100);
638
639                 for (i=0; i<10; i++) {
640                         top1=get_dma_residue(devpriv->dma);
641                         top2=get_dma_residue(devpriv->dma);
642                         if (top1==top2) break;
643                 }
644
645                 if (top1!=top2) return;
646                 top1=devpriv->hwdmasize[0]-top1; // where is now DMA in buffer
647                 top1>>=1;
648                 ofs_dats=top1-devpriv->last_top_dma;  // new samples from last call
649                 if (ofs_dats<0) ofs_dats=(devpriv->dmasamplsize)+ofs_dats;
650                 if (!ofs_dats) return; // exit=no new samples from last call
651                 // obsluz data
652                 i=devpriv->last_top_dma-1;
653                 i&=(devpriv->dmasamplsize-1);
654
655                 if (dmabuf[i]!=MAGIC_DMA_WORD) { // DMA overflow!
656                         comedi_error(dev,"A/D mode1/3 DMA buffer overflow!");
657                         //rt_printk("I %d dmabuf[i] %d %d\n",i,dmabuf[i],devpriv->dmasamplsize);
658                         pcl818_ai_cancel(dev,s);
659                         s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
660                         comedi_event(dev, s, s->async->events);
661                         return;
662                 }
663                 //rt_printk("r %ld ",ofs_dats);
664
665                 bufptr=devpriv->last_top_dma;
666
667                 for (i=0; i<ofs_dats; i++) {
668                         if ((dmabuf[bufptr] & 0xf)!=devpriv->act_chanlist[devpriv->act_chanlist_pos]) { // dropout!
669                                 rt_printk("comedi: A/D mode1/3 DMA - channel dropout %d!=%d !\n",(dmabuf[bufptr] & 0xf),devpriv->act_chanlist[devpriv->act_chanlist_pos]);
670                                 pcl818_ai_cancel(dev,s);
671                                 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
672                                 comedi_event(dev, s, s->async->events);
673                                 return;
674                         }
675
676                         *(sampl_t *)(s->async->data+s->async->buf_int_ptr)=
677                                 dmabuf[bufptr++] >> 4; // get one sample
678                         devpriv->buf_ptr++;
679                         bufptr&=(devpriv->dmasamplsize-1);
680
681                         s->async->buf_int_ptr+=sizeof(sampl_t);
682                         s->async->buf_int_count+=sizeof(sampl_t);
683
684                         s->async->cur_chan++;
685                         if(s->async->cur_chan>=s->async->cmd.chanlist_len){
686                                 s->async->cur_chan++;
687                                 devpriv->int13_act_scan--;
688                         }
689
690                         if (s->async->buf_int_ptr>=s->async->data_len) { /* buffer rollover */
691                                 s->async->buf_int_ptr=0;
692                                 devpriv->buf_ptr=0;
693                                 s->async->events |= COMEDI_CB_EOBUF;
694                         }
695
696                         if (!devpriv->neverending_ai)
697                                 if ( devpriv->int13_act_scan == 0 ) { /* all data sampled */
698                                         pcl818_ai_cancel(dev,s);
699                                         s->async->events |= COMEDI_CB_EOA;
700                                         comedi_event(dev, s, s->async->events);
701                                         //printk("done int ai13 dma\n");
702                                         return;
703                                 }
704                 }
705
706                 devpriv->last_top_dma=bufptr;
707                 bufptr--;
708                 bufptr&=(devpriv->dmasamplsize-1);
709                 dmabuf[bufptr]=MAGIC_DMA_WORD;
710                 comedi_event(dev, s, s->async->events);
711                 //outb(0,0x378);
712                 return;
713         }
714
715         //outb(0,0x378);
716 }
717
718 /*
719 ==============================================================================
720    analog input interrupt mode 1 & 3, 818HD/HG cards
721 */
722 static void interrupt_pcl818_ai_mode13_fifo(int irq, void *d, struct pt_regs *regs)
723 {
724         comedi_device *dev = d;
725         comedi_subdevice *s = dev->subdevices + 0;
726         int i,len,lo;
727
728         outb(0, dev->iobase + PCL818_FI_INTCLR);  // clear fifo int request
729
730         lo=inb(dev->iobase + PCL818_FI_STATUS);
731
732         if (lo&4) {
733                 comedi_error(dev,"A/D mode1/3 FIFO overflow!");
734                 pcl818_ai_cancel(dev,s);
735                 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
736                 comedi_event(dev, s, s->async->events);
737                 return;
738         }
739
740         if (lo&1) {
741                 comedi_error(dev,"A/D mode1/3 FIFO interrupt without data!");
742                 pcl818_ai_cancel(dev,s);
743                 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
744                 comedi_event(dev, s, s->async->events);
745                 return;
746         }
747
748         if (lo&2) { len=512; }
749             else { len=0; }
750
751         for (i=0;i<len;i++) {
752                 lo=inb(dev->iobase + PCL818_FI_DATALO);
753                 if ((lo & 0xf)!=devpriv->act_chanlist[devpriv->act_chanlist_pos]) { // dropout!
754                         rt_printk("comedi: A/D mode1/3 FIFO - channel dropout %d!=%d !\n",(lo & 0xf),devpriv->act_chanlist[devpriv->act_chanlist_pos]);
755                         pcl818_ai_cancel(dev,s);
756                         s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
757                         comedi_event(dev, s, s->async->events);
758                         return;
759                 }
760
761                 *(sampl_t *)(s->async->data+s->async->buf_int_ptr)=
762                         (lo >> 4)|(inb(dev->iobase + PCL818_FI_DATAHI) << 4); // get one sample
763                 devpriv->buf_ptr++;
764                 s->async->buf_int_ptr+=sizeof(sampl_t);
765                 s->async->buf_int_count+=sizeof(sampl_t);
766
767                 s->async->cur_chan++;
768                 if(s->async->cur_chan>=s->async->cmd.chanlist_len){
769                         s->async->cur_chan = 0;
770                         devpriv->int13_act_scan--;
771                 }
772
773                 if (s->async->buf_int_ptr>=s->async->data_len) { /* buffer rollover */
774                         s->async->buf_int_ptr=0;
775                         devpriv->buf_ptr=0;
776                         s->async->events |= COMEDI_CB_EOBUF;
777                 }
778
779                 if (!devpriv->neverending_ai)
780                         if ( devpriv->int13_act_scan == 0 ) { /* all data sampled */
781                                 pcl818_ai_cancel(dev,s);
782                                 s->async->events |= COMEDI_CB_EOA;
783                                 comedi_event(dev, s, s->async->events);
784                                 return;
785                         }
786         }
787
788         if (len>0) comedi_event(dev, s, s->async->events);
789 }
790
791 /*
792 ==============================================================================
793     INT procedure
794 */
795 static void interrupt_pcl818(int irq, void *d, struct pt_regs *regs)
796 {
797         comedi_device *dev = d;
798
799         if(!dev->attached)
800         {
801                 comedi_error(dev, "premature interrupt");
802                 return;
803         }
804
805         //rt_printk("I\n");
806
807         switch (devpriv->int818_mode) {
808         case INT_TYPE_AI1_DMA:
809         case INT_TYPE_AI3_DMA:
810                 interrupt_pcl818_ai_mode13_dma(irq, d, regs);
811                 return;
812         case INT_TYPE_AI1_INT:
813         case INT_TYPE_AI3_INT:
814                 interrupt_pcl818_ai_mode13_int(irq, d, regs);
815                 return;
816         case INT_TYPE_AI1_FIFO:
817         case INT_TYPE_AI3_FIFO:
818                 interrupt_pcl818_ai_mode13_fifo(irq, d, regs);
819                 return;
820 #ifdef PCL818_MODE13_AO
821         case INT_TYPE_AO1_INT:
822         case INT_TYPE_AO3_INT:
823                 interrupt_pcl818_ao_mode13_int(irq, d, regs);
824                 return;
825 #endif
826         }
827
828         outb(0,dev->iobase+PCL818_CLRINT); /* clear INT request */
829
830         if ((!dev->irq)|(!devpriv->irq_free)|(!devpriv->irq_blocked)|(!devpriv->int818_mode)) {
831                 if (devpriv->irq_was_now_closed) {
832                         devpriv->irq_was_now_closed=0;
833                         // comedi_error(dev,"last IRQ..");
834                         return;
835                 }
836                 comedi_error(dev,"bad IRQ!");
837                 return;
838         }
839
840         comedi_error(dev,"IRQ from unknow source!");
841 }
842
843 /*
844 ==============================================================================
845    ANALOG INPUT MODE 1 or 3 DMA , 818 cards
846 */
847 #ifdef unused
848 static void pcl818_ai_mode13dma_int(int mode, comedi_device * dev, comedi_subdevice * s, comedi_trig * it) 
849 {
850         unsigned int flags;
851         unsigned int bytes;
852
853         bytes=devpriv->hwdmasize[0];
854         if (!devpriv->neverending_ai) {
855                 bytes=it->n_chan*it->n*sizeof(sampl_t); // how many 
856                 devpriv->dma_runs_to_end=bytes / devpriv->hwdmasize[0]; // how many DMA pages we must fiil
857                 devpriv->last_dma_run=bytes % devpriv->hwdmasize[0]; //on last dma transfer must be moved
858                 devpriv->dma_runs_to_end--;
859                 if (devpriv->dma_runs_to_end>=0) bytes=devpriv->hwdmasize[0];
860                 //rt_printk("%d %d %d\n",devpriv->dma_runs_to_end,devpriv->last_dma_run,bytes);
861         } 
862
863         devpriv->next_dma_buf=0;
864         set_dma_mode(devpriv->dma, DMA_MODE_READ);
865         flags=claim_dma_lock();
866         clear_dma_ff(devpriv->dma);
867         set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
868         set_dma_count(devpriv->dma, bytes);
869         release_dma_lock(flags);
870         enable_dma(devpriv->dma);
871
872         if (mode==1) { 
873                 devpriv->int818_mode=INT_TYPE_AI1_DMA;
874                 outb(0x87 | (dev->irq << 4), dev->iobase + PCL818_CONTROL);  /* Pacer+IRQ+DMA */ 
875         } else { 
876                 devpriv->int818_mode=INT_TYPE_AI3_DMA;
877                 outb(0x86 | (dev->irq << 4), dev->iobase + PCL818_CONTROL);  /* Ext trig+IRQ+DMA */ 
878         };
879 }
880
881 /* 
882 ==============================================================================
883    ANALOG INPUT MODE 1 or 3 DMA rtc, 818 cards
884 */
885 static void pcl818_ai_mode13dma_rtc(int mode, comedi_device * dev, comedi_subdevice * s, comedi_trig * it) 
886 {
887         unsigned int flags;
888         sampl_t *pole;
889  
890         set_dma_mode(devpriv->dma, DMA_MODE_READ|DMA_AUTOINIT);
891         flags=claim_dma_lock();
892         clear_dma_ff(devpriv->dma);
893         set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
894         set_dma_count(devpriv->dma, devpriv->hwdmasize[0]);
895         release_dma_lock(flags);
896         enable_dma(devpriv->dma);
897         devpriv->last_top_dma=0; //devpriv->hwdmasize[0];
898         pole=(sampl_t *)devpriv->dmabuf[0];
899         devpriv->dmasamplsize=devpriv->hwdmasize[0]/2;
900         pole[devpriv->dmasamplsize-1]=MAGIC_DMA_WORD;
901         devpriv->rtc_freq=rtc_setfreq_irq(2048);
902         devpriv->rtc_irq_timer.expires=jiffies + HZ/devpriv->rtc_freq + 2*HZ/100;
903         devpriv->rtc_irq_timer.data=(unsigned long)dev;
904         devpriv->rtc_irq_timer.function=rtc_dropped_irq;
905     
906         add_timer(&devpriv->rtc_irq_timer);
907     
908         if (mode==1) { 
909                 devpriv->int818_mode=INT_TYPE_AI1_DMA_RTC;
910                 outb(0x07 | (dev->irq << 4), dev->iobase + PCL818_CONTROL);  /* Pacer+DMA */ 
911         } else { 
912                 devpriv->int818_mode=INT_TYPE_AI3_DMA_RTC;
913                 outb(0x06 | (dev->irq << 4), dev->iobase + PCL818_CONTROL);  /* Ext trig+DMA */ 
914     };
915 }
916
917 /* 
918 ==============================================================================
919    ANALOG INPUT MODE 1 or 3, 818 cards
920 */
921 static int pcl818_ai_mode13(int mode, comedi_device * dev, comedi_subdevice * s, comedi_trig * it) 
922 {
923         int divisor1, divisor2;
924
925         if ((!dev->irq)&&(!devpriv->dma_rtc)) {
926                 comedi_error(dev,"IRQ not defined!");
927                 return -EINVAL;
928         }
929
930         if (devpriv->irq_blocked)
931                 return -EBUSY;
932
933         start_pacer(dev, -1, 0, 0); // stop pacer
934
935         if (!check_and_setup_channel_list(dev, s, it)) return -EINVAL;
936         udelay(1);
937
938         devpriv->int13_act_scan=it->n;
939         devpriv->int13_act_chan=0;
940         devpriv->irq_blocked=1;
941         devpriv->irq_was_now_closed=0;
942         devpriv->neverending_ai=0;
943         devpriv->act_chanlist_pos=0;
944         devpriv->buf_ptr=0;
945   
946         if ((it->n==0)||(it->n==-1)) devpriv->neverending_ai=1; //well, user want neverending
947   
948         if (mode==1) {
949                 if (it->trigvar<devpriv->ns_min) it->trigvar=devpriv->ns_min;
950                 i8253_cascade_ns_to_timer(devpriv->i8253_osc_base,&divisor1,&divisor2,&it->trigvar,TRIG_ROUND_NEAREST);
951                 if (divisor1==1) {      /* PCL718/818 crash if any divisor is set to 1 */
952                         divisor1=2;
953                         divisor2/=2;
954                 }
955                 if (divisor2==1) {
956                         divisor2=2;
957                         divisor1/=2;
958                 }
959         }
960    
961         outb(0 , dev->iobase + PCL818_CNTENABLE); /* enable pacer */
962
963         switch (devpriv->dma>0) {
964         case 1:         // DMA
965         case 3:
966                 if (devpriv->dma_rtc==0) { pcl818_ai_mode13dma_int(mode, dev, s, it); }
967                                 else { pcl818_ai_mode13dma_rtc(mode, dev, s, it); }
968                 break;
969         case 0:         // IRQ
970                 // rt_printk("IRQ\n");
971                 if (mode==1) { 
972                         devpriv->int818_mode=INT_TYPE_AI1_INT;
973                         outb(0x83 | (dev->irq << 4), dev->iobase + PCL818_CONTROL);  /* Pacer+IRQ */
974                 } else {
975                         devpriv->int818_mode=INT_TYPE_AI3_INT;
976                         outb(0x82 | (dev->irq << 4), dev->iobase + PCL818_CONTROL);  /* Ext trig+IRQ */ 
977                 };
978                 break;
979         case -1:                // FIFO
980                 outb(1, dev->iobase + PCL818_FI_ENABLE);        // enable FIFO
981                 if (mode==1) { 
982                         devpriv->int818_mode=INT_TYPE_AI1_FIFO;
983                         outb(0x03, dev->iobase + PCL818_CONTROL);  /* Pacer */ 
984                 } else { 
985                         devpriv->int818_mode=INT_TYPE_AI3_FIFO;
986                         outb(0x02, dev->iobase + PCL818_CONTROL); 
987                 }; /* Ext trig */ 
988                 break;
989         }
990
991         start_pacer(dev, mode, divisor1, divisor2);
992     
993         switch(devpriv->int818_mode) {
994         case INT_TYPE_AI1_DMA_RTC:
995         case INT_TYPE_AI3_DMA_RTC:
996                 set_rtc_irq_bit(1); /* start RTC */
997                 break;
998         }
999
1000         return 0;
1001 }
1002
1003 /* 
1004 ==============================================================================
1005    ANALOG INPUT MODE 1, 818 cards
1006 */
1007 static int pcl818_ai_mode1(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) 
1008 {
1009         return pcl818_ai_mode13(1, dev, s, it);
1010 }
1011
1012 /* 
1013 ==============================================================================
1014    ANALOG INPUT MODE 3, 818 cards
1015 */
1016 static int pcl818_ai_mode3(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) 
1017 {
1018         return pcl818_ai_mode13(3, dev, s, it);
1019 }
1020
1021 /*
1022 ==============================================================================
1023    ANALOG OUTPUT MODE 1 or 3, 818 cards
1024 */
1025 #ifdef PCL818_MODE13_AO
1026 static int pcl818_ao_mode13(int mode, comedi_device * dev, comedi_subdevice * s, comedi_trig * it) 
1027 {
1028         int divisor1, divisor2;
1029  
1030
1031         if (!dev->irq) {
1032                 comedi_error(dev,"IRQ not defined!");
1033                 return -EINVAL;
1034         }
1035
1036         if (devpriv->irq_blocked)
1037                 return -EBUSY;
1038
1039         start_pacer(dev, -1, 0, 0); // stop pacer
1040
1041         devpriv->int13_act_scan=it->n;
1042         devpriv->int13_act_chan=0;
1043         devpriv->irq_blocked=1;
1044         devpriv->irq_was_now_closed=0;
1045         devpriv->neverending_ai=0;
1046         devpriv->act_chanlist_pos=0;
1047         devpriv->buf_ptr=0;
1048       
1049         if (mode==1) {
1050                 i8253_cascade_ns_to_timer(devpriv->i8253_osc_base,&divisor1,&divisor2,&it->trigvar,TRIG_ROUND_NEAREST);
1051                 if (divisor1==1) {      /* PCL818 crash if any divisor is set to 1 */
1052                         divisor1=2;
1053                         divisor2/=2;
1054                 }
1055                 if (divisor2==1) {
1056                         divisor2=2;
1057                         divisor1/=2;
1058                 }
1059         }
1060
1061         outb(0 , dev->iobase + PCL818_CNTENABLE); /* enable pacer */
1062         if (mode==1) { 
1063                 devpriv->int818_mode=INT_TYPE_AO1_INT;
1064                 outb(0x83 | (dev->irq << 4), dev->iobase + PCL818_CONTROL);  /* Pacer+IRQ */ 
1065         } else { 
1066                 devpriv->int818_mode=INT_TYPE_AO3_INT;
1067                 outb(0x82 | (dev->irq << 4), dev->iobase + PCL818_CONTROL);  /* Ext trig+IRQ */
1068         };
1069
1070         start_pacer(dev, mode, divisor1, divisor2);
1071
1072         return 0;
1073 }
1074
1075 /* 
1076 ==============================================================================
1077    ANALOG OUTPUT MODE 1, 818 cards
1078 */
1079 static int pcl818_ao_mode1(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) 
1080 {
1081         return pcl818_ao_mode13(1, dev, s, it);
1082 }
1083
1084 /* 
1085 ==============================================================================
1086    ANALOG OUTPUT MODE 3, 818 cards
1087 */
1088 static int pcl818_ao_mode3(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) 
1089 {
1090         return pcl818_ao_mode13(3, dev, s, it);
1091 }
1092 #endif
1093 #endif
1094
1095 /*
1096 ==============================================================================
1097  Start/stop pacer onboard pacer
1098 */
1099 #ifdef unused
1100 static void start_pacer(comedi_device * dev, int mode, unsigned int divisor1, unsigned int divisor2) 
1101 {
1102         outb(0xb4, dev->iobase + PCL818_CTRCTL);
1103         outb(0x74, dev->iobase + PCL818_CTRCTL);
1104         outb(0x30, dev->iobase + PCL818_CTRCTL);
1105         udelay(1);
1106   
1107         if (mode==1) {
1108                 outb(divisor2 & 0xff, dev->iobase + PCL818_CTR2);
1109                 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL818_CTR2);
1110                 outb(divisor1  & 0xff, dev->iobase + PCL818_CTR1);
1111                 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL818_CTR1);
1112         }
1113 }
1114
1115 /*
1116 ==============================================================================
1117  Check if channel list from user is builded correctly
1118  If it's ok, then program scan/gain logic
1119 */
1120 static int check_and_setup_channel_list(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
1121 {
1122         unsigned int chansegment[16];  
1123         unsigned int i, nowmustbechan, seglen, segpos;
1124     
1125     /* correct channel and range number check itself comedi/range.c */
1126         if (it->n_chan<1) {
1127                 comedi_error(dev,"range/channel list is empty!");
1128                 return 0;             
1129         }
1130
1131         if (it->n_chan > 1) {
1132                 // first channel is everytime ok
1133                 chansegment[0]=it->chanlist[0];
1134                 // build part of chanlist
1135                 for (i=1, seglen=1; i<it->n_chan; i++, seglen++) {
1136                         // rt_printk("%d. %d %d\n",i,CR_CHAN(it->chanlist[i]),CR_RANGE(it->chanlist[i]));
1137                         // we detect loop, this must by finish
1138                         if (it->chanlist[0]==it->chanlist[i]) break;
1139                         nowmustbechan=(CR_CHAN(chansegment[i-1])+1) % s->n_chan;
1140                         // channel list isn't continous :-(
1141                         if (nowmustbechan!=CR_CHAN(it->chanlist[i])) {
1142                                 rt_printk("comedi%d: pcl818: channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
1143                                             dev->minor,i,CR_CHAN(it->chanlist[i]),
1144                                             nowmustbechan,CR_CHAN(it->chanlist[0]) );
1145                                 return 0;             
1146                         }
1147                         // well, this is next correct channel in list
1148                         chansegment[i]=it->chanlist[i];
1149                 }
1150
1151                 // check whole chanlist
1152                 for (i=0, segpos=0; i<it->n_chan; i++) {
1153                         //rt_printk("%d %d=%d %d\n",CR_CHAN(chansegment[i%seglen]),CR_RANGE(chansegment[i%seglen]),CR_CHAN(it->chanlist[i]),CR_RANGE(it->chanlist[i]));
1154                         if (it->chanlist[i]!=chansegment[i%seglen]) {
1155                                 rt_printk("comedi%d: pcl818: bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
1156                                   dev->minor,i,CR_CHAN(chansegment[i]),
1157                                   CR_RANGE(chansegment[i]),
1158                                   CR_AREF(chansegment[i]),
1159                                   CR_CHAN(it->chanlist[i%seglen]),
1160                                   CR_RANGE(it->chanlist[i%seglen]),
1161                                   CR_AREF(chansegment[i%seglen]));
1162                                 return 0; // chan/gain list is strange
1163                         }
1164                 }
1165         } else {
1166                 seglen=1;
1167         }
1168     
1169         devpriv->act_chanlist_len=seglen;
1170         devpriv->act_chanlist_pos=0;
1171
1172         for (i=0; i<seglen; i++) {  // store range list to card
1173                 devpriv->act_chanlist[i]=CR_CHAN(it->chanlist[i]);
1174                 outb(muxonechan[CR_CHAN(it->chanlist[i])], dev->iobase+PCL818_MUX); /* select channel */
1175                 outb(CR_RANGE(it->chanlist[i]), dev->iobase+PCL818_RANGE); /* select gain */
1176         }
1177
1178         udelay(1);
1179
1180         /* select channel interval to sca n*/
1181         outb(devpriv->act_chanlist[0] | (devpriv->act_chanlist[seglen-1] << 4),
1182                 dev->iobase+PCL818_MUX);
1183         // printk(" MUX %x\n",devpriv->act_chanlist[0] | (devpriv->act_chanlist[seglen-1] << 4));
1184         
1185         // we can serve this with MUX logic
1186         return 1;
1187 }
1188 #endif
1189
1190 /* 
1191 ==============================================================================
1192  Check if board is switched to SE (1) or DIFF(0) mode
1193 */
1194 static int check_single_ended(unsigned int port)
1195 {
1196         if (inb(port+PCL818_STATUS)&0x20) { return 1; }
1197                                         else { return 0; }
1198 }
1199
1200 /* 
1201 ==============================================================================
1202  cancel any mode 1-4 AI
1203 */
1204 static int pcl818_ai_cancel(comedi_device * dev, comedi_subdevice * s) 
1205 {
1206         if (devpriv->irq_blocked>0) {
1207                 // rt_printk("pcl818_ai_cancel()\n");
1208                 switch (devpriv->int818_mode) {
1209                 case INT_TYPE_AI1_DMA_RTC:
1210                 case INT_TYPE_AI3_DMA_RTC:
1211                         set_rtc_irq_bit(0); // stop RTC
1212                         del_timer(&devpriv->rtc_irq_timer);
1213                 case INT_TYPE_AI1_DMA:
1214                 case INT_TYPE_AI3_DMA:
1215                         disable_dma(devpriv->dma);
1216                 case INT_TYPE_AI1_INT:
1217                 case INT_TYPE_AI3_INT:
1218                 case INT_TYPE_AI1_FIFO:
1219                 case INT_TYPE_AI3_FIFO:
1220 #ifdef PCL818_MODE13_AO
1221                 case INT_TYPE_AO1_INT:
1222                 case INT_TYPE_AO3_INT:
1223 #endif
1224                         outb(inb(dev->iobase+PCL818_CONTROL)& 0x73, dev->iobase+PCL818_CONTROL); /* Stop A/D */
1225                         udelay(1);
1226                         outb(0, dev->iobase+PCL818_CONTROL); /* Stop A/D */
1227                         outb(0xb4, dev->iobase + PCL818_CTRCTL);/* Stop pacer */
1228                         outb(0x74, dev->iobase + PCL818_CTRCTL);
1229                         outb(0, dev->iobase+PCL818_AD_LO);
1230                         inb(dev->iobase+PCL818_AD_LO);
1231                         inb(dev->iobase+PCL818_AD_HI);
1232                         outb(0, dev->iobase+PCL818_CLRINT); /* clear INT request */
1233                         outb(0, dev->iobase+PCL818_CONTROL); /* Stop A/D */
1234                         if (devpriv->usefifo) { // FIFO shutdown
1235                                 outb(0, dev->iobase + PCL818_FI_INTCLR);
1236                                 outb(0, dev->iobase + PCL818_FI_FLUSH);
1237                                 outb(0, dev->iobase + PCL818_FI_ENABLE);
1238                         }
1239                         devpriv->irq_blocked=0;
1240                         devpriv->irq_was_now_closed=devpriv->int818_mode;
1241                         devpriv->int818_mode=0;
1242                         devpriv->last_int_sub=s;
1243                         break;
1244                 }
1245         }
1246
1247         //rt_printk("pcl818_ai_cancel() end\n");
1248         return 0;
1249 }
1250
1251 /* 
1252 ==============================================================================
1253  chech for PCL818
1254 */
1255 static int pcl818_check(int iobase) 
1256 {
1257         outb(0x00, iobase + PCL818_MUX);
1258         udelay(1); 
1259         if (inb(iobase + PCL818_MUX)!=0x00) return 1; //there isn't card
1260         outb(0x55, iobase + PCL818_MUX);
1261         udelay(1); 
1262         if (inb(iobase + PCL818_MUX)!=0x55) return 1; //there isn't card
1263         outb(0x00, iobase + PCL818_MUX);
1264         udelay(1); 
1265         outb(0x18, iobase + PCL818_CONTROL); 
1266         udelay(1); 
1267         if (inb(iobase + PCL818_CONTROL)!=0x18) return 1; //there isn't card
1268         return 0; // ok, card exist
1269 }
1270
1271 /* 
1272 ==============================================================================
1273  reset whole PCL-818 cards
1274 */
1275 static void pcl818_reset(comedi_device * dev) 
1276 {
1277         if (devpriv->usefifo) { // FIFO shutdown
1278                 outb(0, dev->iobase + PCL818_FI_INTCLR);
1279                 outb(0, dev->iobase + PCL818_FI_FLUSH);
1280                 outb(0, dev->iobase + PCL818_FI_ENABLE);
1281         }
1282         outb(0, dev->iobase + PCL818_DA_LO); // DAC=0V
1283         outb(0, dev->iobase + PCL818_DA_HI);
1284         udelay(1);
1285         outb(0, dev->iobase + PCL818_DO_HI); // DO=$0000
1286         outb(0, dev->iobase + PCL818_DO_LO);
1287         udelay(1);
1288         outb(0, dev->iobase + PCL818_CONTROL);
1289         outb(0, dev->iobase + PCL818_CNTENABLE);
1290         outb(0, dev->iobase + PCL818_MUX);
1291         outb(0, dev->iobase + PCL818_CLRINT);
1292         outb(0xb0, dev->iobase + PCL818_CTRCTL);/* Stop pacer */
1293         outb(0x70, dev->iobase + PCL818_CTRCTL);
1294         outb(0x30, dev->iobase + PCL818_CTRCTL);
1295         if(this_board->is_818){
1296                 outb(0, dev->iobase + PCL818_RANGE);
1297         }else{
1298                 outb(0, dev->iobase + PCL718_DA2_LO);
1299                 outb(0, dev->iobase + PCL718_DA2_HI);
1300         }
1301 }
1302
1303 /* 
1304 ==============================================================================
1305   Enable(1)/disable(0) periodic interrupts from RTC
1306 */
1307 static int set_rtc_irq_bit(unsigned char bit)
1308 {
1309         unsigned char val;
1310         unsigned long flags;
1311  
1312         if (bit==1) {
1313                 RTC_timer_lock++;
1314                 if (RTC_timer_lock>1) return 0;
1315         } else {
1316                 RTC_timer_lock--;
1317                 if (RTC_timer_lock<0) RTC_timer_lock=0;
1318                 if (RTC_timer_lock>0) return 0;
1319         }
1320   
1321         save_flags(flags);
1322         cli();
1323         val = CMOS_READ(RTC_CONTROL);
1324         if (bit) { val |= RTC_PIE; }
1325             else { val &=  ~RTC_PIE; }
1326         CMOS_WRITE(val, RTC_CONTROL);
1327         CMOS_READ(RTC_INTR_FLAGS);
1328         restore_flags(flags);
1329         return 0;
1330 }
1331
1332 /*
1333 ==============================================================================
1334   Restart RTC if something stop it (xntpd every 11 mins or large IDE transfers)
1335 */
1336 #ifdef unused
1337 static void rtc_dropped_irq(unsigned long data) 
1338 {
1339         comedi_device *dev = (void *)data;
1340         unsigned long flags,tmp;
1341
1342         switch(devpriv->int818_mode) {
1343         case INT_TYPE_AI1_DMA_RTC:
1344         case INT_TYPE_AI3_DMA_RTC:
1345                 mod_timer(&devpriv->rtc_irq_timer, jiffies + HZ/devpriv->rtc_freq + 2*HZ/100);
1346                 save_flags(flags);
1347                 cli();
1348                 tmp=(CMOS_READ(RTC_INTR_FLAGS) & 0xF0); /* restart */
1349                 restore_flags(flags);
1350                 break;
1351         };     
1352 }
1353
1354
1355 /*
1356 ==============================================================================
1357   Set frequency of interrupts from RTC
1358 */
1359 static int rtc_setfreq_irq(int freq) 
1360 {
1361         int tmp = 0;
1362         int rtc_freq;
1363         unsigned char val;
1364         unsigned long flags;
1365
1366         if (freq<2) freq=2;
1367         if (freq>8192) freq=8192;
1368
1369         while (freq>(1<<tmp))
1370                 tmp++;
1371
1372         rtc_freq = 1<<tmp;
1373
1374         save_flags(flags);
1375         cli();
1376         val = CMOS_READ(RTC_FREQ_SELECT) & 0xf0;
1377         val |= (16 - tmp);
1378         CMOS_WRITE(val, RTC_FREQ_SELECT);
1379         restore_flags(flags);
1380         return rtc_freq;
1381 }
1382 #endif
1383
1384 /* 
1385 ==============================================================================
1386   Free any resources that we have claimed  
1387 */
1388 static void free_resources(comedi_device * dev)
1389 {
1390         //rt_printk("free_resource()\n");
1391         if(dev->private)  {
1392                 pcl818_ai_cancel(dev, devpriv->sub_ai);
1393                 pcl818_reset(dev);
1394                 if (devpriv->dma) free_dma(devpriv->dma);
1395                 if (devpriv->dmabuf[0]) free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1396                 if (devpriv->dmabuf[1]) free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
1397                 if (devpriv->rtc_irq) comedi_free_irq(devpriv->rtc_irq, dev);
1398                 if ((devpriv->dma_rtc)&&(RTC_lock==1)) {
1399                         if (devpriv->rtc_iobase)
1400                                 release_region(devpriv->rtc_iobase, devpriv->rtc_iosize);
1401                 }
1402                 if (devpriv->dma_rtc)
1403                         RTC_lock--;
1404         }
1405
1406         if (dev->irq) free_irq(dev->irq, dev);
1407         if (dev->iobase) release_region(dev->iobase, devpriv->io_range);
1408         //rt_printk("free_resource() end\n");
1409 }
1410
1411 /* 
1412 ==============================================================================
1413
1414    Initialization 
1415
1416 */
1417 static int pcl818_attach(comedi_device * dev, comedi_devconfig * it)
1418 {
1419         int ret;
1420         int iobase;
1421         int irq,dma;
1422         unsigned long pages;
1423         comedi_subdevice *s;
1424
1425
1426         if((ret=alloc_private(dev,sizeof(pcl818_private)))<0)
1427                 return ret; /* Can't alloc mem */
1428
1429         /* claim our I/O space */
1430         iobase = it->options[0];
1431         printk("comedi%d: pcl818:  board=%s, ioport=0x%03x",
1432                 dev->minor, this_board->name, iobase);
1433         devpriv->io_range=this_board->io_range;
1434         if ((this_board->fifo)&&(it->options[2]==-1)) { // we've board with FIFO and we want to use FIFO
1435                 devpriv->io_range=PCLx1xFIFO_RANGE;
1436                 devpriv->usefifo = 1;
1437         }
1438         if (check_region(iobase, this_board->io_range) < 0) {
1439                 rt_printk("I/O port conflict\n");
1440                 return -EIO;
1441         }
1442
1443         request_region(iobase, devpriv->io_range, "pcl818");
1444         dev->iobase=iobase;
1445
1446         if (pcl818_check(iobase)) {
1447                 rt_printk(", I can't detect board. FAIL!\n");
1448                 return -EIO;
1449         }
1450
1451         /* set up some name stuff */
1452         dev->board_name = this_board->name;
1453         /* grab our IRQ */
1454         irq=0;
1455         if (this_board->IRQbits!=0) { /* board support IRQ */
1456                 irq=it->options[1];
1457                 if (irq>0)  {/* we want to use IRQ */
1458                         if (((1<<irq)&this_board->IRQbits)==0) {
1459                                 rt_printk(", IRQ %d is out of allowed range, DISABLING IT",irq);
1460                                 irq=0; /* Bad IRQ */
1461                         } else { 
1462                                 if (comedi_request_irq(irq, interrupt_pcl818, 0, "pcl818", dev)) {
1463                                         rt_printk(", unable to allocate IRQ %d, DISABLING IT", irq);
1464                                         irq=0; /* Can't use IRQ */
1465                                 } else {
1466                                         rt_printk(", irq=%d", irq);
1467                                 }    
1468                         }  
1469                 }
1470         }
1471
1472         dev->irq = irq;
1473         if (irq) { devpriv->irq_free=1; } /* 1=we have allocated irq */
1474             else { devpriv->irq_free=0; } 
1475         devpriv->irq_blocked=0; /* number of subdevice which use IRQ */
1476         devpriv->int818_mode=0; /* mode of irq */
1477
1478         /* grab RTC for DMA operations */
1479         devpriv->dma_rtc=0;
1480         if (it->options[2]>0) { // we want to use DMA
1481                 if (RTC_lock==0) {
1482                         if (check_region(RTC_PORT(0), RTC_IO_EXTENT) < 0) goto no_rtc;
1483                         request_region(RTC_PORT(0), RTC_IO_EXTENT, "pcl818 (RTC)");
1484                 } 
1485                 devpriv->rtc_iobase=RTC_PORT(0);
1486                 devpriv->rtc_iosize=RTC_IO_EXTENT;
1487                 RTC_lock++;
1488                 if (!comedi_request_irq(RTC_IRQ, interrupt_pcl818_ai_mode13_dma_rtc, 0, "pcl818 DMA (RTC)", dev)) {
1489                         devpriv->dma_rtc=1;
1490                         devpriv->rtc_irq=RTC_IRQ;
1491                         rt_printk(", dma_irq=%d", devpriv->rtc_irq);
1492                 } else {
1493                         RTC_lock--;
1494                         if (RTC_lock==0) {
1495                                 if (devpriv->rtc_iobase) release_region(devpriv->rtc_iobase, devpriv->rtc_iosize);
1496                         }
1497                         devpriv->rtc_iobase=0;
1498                         devpriv->rtc_iosize=0;
1499                 }
1500         } 
1501
1502 no_rtc:
1503         /* grab our DMA */
1504         dma=0;
1505         devpriv->dma=dma;
1506         if ((devpriv->irq_free==0)&&(devpriv->dma_rtc==0)) goto no_dma; /* if we haven't IRQ, we can't use DMA */
1507         if (this_board->DMAbits!=0) { /* board support DMA */
1508                 dma=it->options[2];
1509                 if (dma<1) goto no_dma; /* DMA disabled */
1510                 if (((1<<dma)&this_board->DMAbits)==0) {
1511                         rt_printk(", DMA is out of allowed range, FAIL!\n");
1512                         return -EINVAL; /* Bad DMA */
1513                 } 
1514                 ret=request_dma(dma, "pcl818");
1515                 if (ret) {
1516                         rt_printk(", unable to allocate DMA %d, FAIL!\n",dma);
1517                         return -EBUSY; /* DMA isn't free */
1518                 } 
1519                 devpriv->dma=dma;
1520                 rt_printk(", dma=%d", dma);
1521                 pages=2; /* we need 16KB */
1522                 devpriv->dmabuf[0]=__get_dma_pages(GFP_KERNEL, pages);
1523                 if (!devpriv->dmabuf[0]) {
1524                         rt_printk(", unable to allocate DMA buffer, FAIL!\n");
1525                         /* maybe experiment with try_to_free_pages() will help .... */
1526                         return -EBUSY; /* no buffer :-( */
1527                 }
1528                 devpriv->dmapages[0]=pages;
1529                 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1530                 devpriv->hwdmasize[0]=(1<<pages)*PAGE_SIZE;
1531                 //rt_printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE);
1532                 if (devpriv->dma_rtc==0) { // we must do duble buff :-(
1533                         devpriv->dmabuf[1]=__get_dma_pages(GFP_KERNEL, pages);
1534                         if (!devpriv->dmabuf[1]) {
1535                                 rt_printk(", unable to allocate DMA buffer, FAIL!\n");
1536                                 return -EBUSY;
1537                         }
1538                         devpriv->dmapages[1]=pages;
1539                         devpriv->hwdmaptr[1] = virt_to_bus((void *)devpriv->dmabuf[1]);
1540                         devpriv->hwdmasize[1]=(1<<pages)*PAGE_SIZE;
1541                 }
1542         }
1543
1544 no_dma:
1545
1546         dev->n_subdevices = 4;
1547         if((ret=alloc_subdevices(dev))<0) return ret;
1548
1549         s = dev->subdevices + 0;
1550         if(!this_board->n_aichan_se){
1551                 s->type = COMEDI_SUBD_UNUSED;
1552         }else{
1553                 dev->read_subdev = s;
1554                 s->type = COMEDI_SUBD_AI;
1555                 devpriv->sub_ai=s;
1556                 s->subdev_flags = SDF_READABLE;
1557                 if (check_single_ended(dev->iobase)) {
1558                         s->n_chan = this_board->n_aichan_se;
1559                         s->subdev_flags|=SDF_COMMON|SDF_GROUND;
1560                         printk(", %dchans S.E. DAC",s->n_chan);
1561                 } else {
1562                         s->n_chan = this_board->n_aichan_diff;
1563                         s->subdev_flags|=SDF_DIFF;
1564                         printk(", %dchans DIFF DAC",s->n_chan);
1565                 }
1566                 s->maxdata = this_board->ai_maxdata;
1567                 s->len_chanlist = this_board->ai_chanlist;
1568                 s->range_table = this_board->ai_range_type;
1569                 s->cancel=pcl818_ai_cancel;
1570                 s->insn_read = pcl818_ai_insn_read;
1571                 //if ((irq)||(devpriv->dma_rtc)) {
1572                         //s->trig[1] = pcl818_ai_mode1;
1573                         //s->trig[3] = pcl818_ai_mode3;
1574                 //}
1575                 if(this_board->is_818){
1576                         if ((it->options[4]==1)||(it->options[4]==10))
1577                                 s->range_table=&range_pcl818l_h_ai; // secondary range list jumper selectable
1578                 }else{
1579                         switch (it->options[4]) {
1580                         case 0: s->range_table=&range_bipolar10; break;
1581                         case 1: s->range_table=&range_bipolar5; break;
1582                         case 2: s->range_table=&range_bipolar2_5; break;
1583                         case 3: s->range_table=&range718_bipolar1; break;
1584                         case 4: s->range_table=&range718_bipolar0_5; break;
1585                         case 6: s->range_table=&range_unipolar10; break;
1586                         case 7: s->range_table=&range_unipolar5; break;
1587                         case 8: s->range_table=&range718_unipolar2; break;
1588                         case 9: s->range_table=&range718_unipolar1; break;
1589                         default: s->range_table=&range_unknown; break;
1590                         }
1591                 }
1592         }
1593
1594         s = dev->subdevices + 1;
1595         if(!this_board->n_aochan){
1596                 s->type = COMEDI_SUBD_UNUSED;
1597         }else{
1598                 dev->write_subdev = s;
1599                 s->type = COMEDI_SUBD_AO;
1600                 s->subdev_flags = SDF_WRITABLE|SDF_GROUND|SDF_RT;
1601                 s->n_chan = this_board->n_aochan;
1602                 s->maxdata = this_board->ao_maxdata;
1603                 s->len_chanlist = this_board->ao_chanlist;
1604                 s->range_table = this_board->ao_range_type;
1605                 s->insn_read = pcl818_ao_insn_read;
1606                 s->insn_write = pcl818_ao_insn_write;
1607 #ifdef unused
1608 #ifdef PCL818_MODE13_AO
1609                 if (irq) {
1610                         s->trig[1] = pcl818_ao_mode1;
1611                         s->trig[3] = pcl818_ao_mode3;
1612                 } 
1613 #endif
1614 #endif
1615                 if(this_board->is_818){
1616                         if ((it->options[4]==1)||(it->options[4]==10)) 
1617                                 s->range_table=&range_unipolar10; 
1618                         if (it->options[4]==2) 
1619                                 s->range_table=&range_unknown; 
1620                 }else{
1621                         if ((it->options[5]==1)||(it->options[5]==10)) 
1622                                 s->range_table=&range_unipolar10; 
1623                         if (it->options[5]==2) 
1624                                 s->range_table=&range_unknown; 
1625                 }       
1626         }
1627
1628         s = dev->subdevices + 2;
1629         if(!this_board->n_dichan){
1630                 s->type = COMEDI_SUBD_UNUSED;
1631         }else{
1632                 s->type = COMEDI_SUBD_DI;
1633                 s->subdev_flags = SDF_READABLE|SDF_RT;
1634                 s->n_chan = this_board->n_dichan;
1635                 s->maxdata = 1;
1636                 s->len_chanlist = this_board->n_dichan;
1637                 s->range_table = &range_digital;
1638                 s->insn_bits = pcl818_di_insn_bits;
1639         }
1640
1641         s = dev->subdevices + 3;
1642         if(!this_board->n_dochan){
1643                 s->type = COMEDI_SUBD_UNUSED;
1644         }else{
1645                 s->type = COMEDI_SUBD_DO;
1646                 s->subdev_flags = SDF_WRITABLE|SDF_RT;
1647                 s->n_chan = this_board->n_dochan;
1648                 s->maxdata = 1;
1649                 s->len_chanlist = this_board->n_dochan;
1650                 s->range_table = &range_digital;
1651                 s->insn_bits = pcl818_do_insn_bits;
1652         }
1653
1654         /* select 1/10MHz oscilator */
1655         if ((it->options[3]==0)||(it->options[3]==10)) { 
1656                 devpriv->i8253_osc_base= 100; 
1657         } else { 
1658                 devpriv->i8253_osc_base=1000; 
1659         }       
1660
1661          /* max sampling speed */
1662         devpriv->ns_min=this_board->ns_min;
1663           
1664         if(!this_board->is_818){
1665                 if ((it->options[6]==1)||(it->options[6]==100)) 
1666                         devpriv->ns_min=10000;  /* extended PCL718 to 100kHz DAC */
1667         }
1668
1669         pcl818_reset(dev); 
1670
1671         rt_printk("\n");
1672
1673         return 0;
1674 }
1675
1676
1677 /*
1678 ==============================================================================
1679   Removes device
1680  */
1681 static int pcl818_detach(comedi_device * dev) 
1682 {
1683         //  rt_printk("comedi%d: pcl818: remove\n", dev->minor);
1684         free_resources(dev);
1685         return 0;
1686 }
1687