1b1708a58b677c265fe3c0684e20210bf44d0a07
[safe/jmp/linux-2.6] / drivers / staging / comedi / drivers / pcl816.c
1 /*
2    comedi/drivers/pcl816.c
3
4    Author:  Juan Grigera <juan@grigera.com.ar>
5             based on pcl818 by Michal Dobes <dobes@tesnet.cz> and bits of pcl812
6
7    hardware driver for Advantech cards:
8     card:   PCL-816, PCL814B
9     driver: pcl816
10 */
11 /*
12 Driver: pcl816
13 Description: Advantech PCL-816 cards, PCL-814
14 Author: Juan Grigera <juan@grigera.com.ar>
15 Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b)
16 Status: works
17 Updated: Tue,  2 Apr 2002 23:15:21 -0800
18
19 PCL 816 and 814B have 16 SE/DIFF ADCs, 16 DACs, 16 DI and 16 DO.
20 Differences are at resolution (16 vs 12 bits).
21
22 The driver support AI command mode, other subdevices not written.
23
24 Analog output and digital input and output are not supported.
25
26 Configuration Options:
27   [0] - IO Base
28   [1] - IRQ     (0=disable, 2, 3, 4, 5, 6, 7)
29   [2] - DMA     (0=disable, 1, 3)
30   [3] - 0, 10=10MHz clock for 8254
31             1= 1MHz clock for 8254
32
33 */
34
35 #include "../comedidev.h"
36
37 #include <linux/ioport.h>
38 #include <linux/mc146818rtc.h>
39 #include <linux/delay.h>
40 #include <asm/dma.h>
41
42 #include "8253.h"
43
44 #define DEBUG(x) x
45
46 /* boards constants */
47 /* IO space len */
48 #define PCLx1x_RANGE 16
49
50 /* #define outb(x,y)  printk("OUTB(%x, 200+%d)\n", x,y-0x200); outb(x,y) */
51
52 /* INTEL 8254 counters */
53 #define PCL816_CTR0 4
54 #define PCL816_CTR1 5
55 #define PCL816_CTR2 6
56 /* R: counter read-back register W: counter control */
57 #define PCL816_CTRCTL 7
58
59 /* R: A/D high byte W: A/D range control */
60 #define PCL816_RANGE 9
61 /* W: clear INT request */
62 #define PCL816_CLRINT 10
63 /* R: next mux scan channel W: mux scan channel & range control pointer */
64 #define PCL816_MUX 11
65 /* R/W: operation control register */
66 #define PCL816_CONTROL 12
67
68 /* R: return status byte  W: set DMA/IRQ */
69 #define PCL816_STATUS 13
70 #define PCL816_STATUS_DRDY_MASK 0x80
71
72 /* R: low byte of A/D W: soft A/D trigger */
73 #define PCL816_AD_LO 8
74 /* R: high byte of A/D W: A/D range control */
75 #define PCL816_AD_HI 9
76
77 /* type of interrupt handler */
78 #define INT_TYPE_AI1_INT 1
79 #define INT_TYPE_AI1_DMA 2
80 #define INT_TYPE_AI3_INT 4
81 #define INT_TYPE_AI3_DMA 5
82 #ifdef unused
83 #define INT_TYPE_AI1_DMA_RTC 9
84 #define INT_TYPE_AI3_DMA_RTC 10
85
86 /* RTC stuff... */
87 #define RTC_IRQ         8
88 #define RTC_IO_EXTENT   0x10
89 #endif
90
91 #define MAGIC_DMA_WORD 0x5a5a
92
93 static const struct comedi_lrange range_pcl816 = { 8, {
94                                                        BIP_RANGE(10),
95                                                        BIP_RANGE(5),
96                                                        BIP_RANGE(2.5),
97                                                        BIP_RANGE(1.25),
98                                                        UNI_RANGE(10),
99                                                        UNI_RANGE(5),
100                                                        UNI_RANGE(2.5),
101                                                        UNI_RANGE(1.25),
102                                                        }
103 };
104
105 struct pcl816_board {
106
107         const char *name;       /*  board name */
108         int n_ranges;           /*  len of range list */
109         int n_aichan;           /*  num of A/D chans in diferencial mode */
110         unsigned int ai_ns_min; /*  minimal alllowed delay between samples (in ns) */
111         int n_aochan;           /*  num of D/A chans */
112         int n_dichan;           /*  num of DI chans */
113         int n_dochan;           /*  num of DO chans */
114         const struct comedi_lrange *ai_range_type;      /*  default A/D rangelist */
115         const struct comedi_lrange *ao_range_type;      /*  dafault D/A rangelist */
116         unsigned int io_range;  /*  len of IO space */
117         unsigned int IRQbits;   /*  allowed interrupts */
118         unsigned int DMAbits;   /*  allowed DMA chans */
119         int ai_maxdata;         /*  maxdata for A/D */
120         int ao_maxdata;         /*  maxdata for D/A */
121         int ai_chanlist;        /*  allowed len of channel list A/D */
122         int ao_chanlist;        /*  allowed len of channel list D/A */
123         int i8254_osc_base;     /*  1/frequency of on board oscilator in ns */
124 };
125
126 static const struct pcl816_board boardtypes[] = {
127         {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816,
128          &range_pcl816, PCLx1x_RANGE,
129          0x00fc,                /*  IRQ mask */
130          0x0a,                  /*  DMA mask */
131          0xffff,                /*  16-bit card */
132          0xffff,                /*  D/A maxdata */
133          1024,
134          1,                     /*  ao chan list */
135          100},
136         {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816,
137          &range_pcl816, PCLx1x_RANGE,
138          0x00fc,
139          0x0a,
140          0x3fff,                /* 14 bit card */
141          0x3fff,
142          1024,
143          1,
144          100},
145 };
146
147 #define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl816_board))
148 #define devpriv ((struct pcl816_private *)dev->private)
149 #define this_board ((const struct pcl816_board *)dev->board_ptr)
150
151 static int pcl816_attach(struct comedi_device *dev,
152                          struct comedi_devconfig *it);
153 static int pcl816_detach(struct comedi_device *dev);
154
155 #ifdef unused
156 static int RTC_lock = 0;        /* RTC lock */
157 static int RTC_timer_lock = 0;  /* RTC int lock */
158 #endif
159
160 static struct comedi_driver driver_pcl816 = {
161         .driver_name = "pcl816",
162         .module = THIS_MODULE,
163         .attach = pcl816_attach,
164         .detach = pcl816_detach,
165         .board_name = &boardtypes[0].name,
166         .num_names = n_boardtypes,
167         .offset = sizeof(struct pcl816_board),
168 };
169
170 COMEDI_INITCLEANUP(driver_pcl816);
171
172 struct pcl816_private {
173
174         unsigned int dma;       /*  used DMA, 0=don't use DMA */
175         int dma_rtc;            /*  1=RTC used with DMA, 0=no RTC alloc */
176 #ifdef unused
177         unsigned long rtc_iobase;       /*  RTC port region */
178         unsigned int rtc_iosize;
179         unsigned int rtc_irq;
180 #endif
181         unsigned long dmabuf[2];        /*  pointers to begin of DMA buffers */
182         unsigned int dmapages[2];       /*  len of DMA buffers in PAGE_SIZEs */
183         unsigned int hwdmaptr[2];       /*  hardware address of DMA buffers */
184         unsigned int hwdmasize[2];      /*  len of DMA buffers in Bytes */
185         unsigned int dmasamplsize;      /*  size in samples hwdmasize[0]/2 */
186         unsigned int last_top_dma;      /*  DMA pointer in last RTC int */
187         int next_dma_buf;       /*  which DMA buffer will be used next round */
188         long dma_runs_to_end;   /*  how many we must permorm DMA transfer to end of record */
189         unsigned long last_dma_run;     /*  how many bytes we must transfer on last DMA page */
190
191         unsigned int ai_scans;  /*  len of scanlist */
192         unsigned char ai_neverending;   /*  if=1, then we do neverending record (you must use cancel()) */
193         int irq_free;           /*  1=have allocated IRQ */
194         int irq_blocked;        /*  1=IRQ now uses any subdev */
195 #ifdef unused
196         int rtc_irq_blocked;    /*  1=we now do AI with DMA&RTC */
197 #endif
198         int irq_was_now_closed; /*  when IRQ finish, there's stored int816_mode for last interrupt */
199         int int816_mode;        /*  who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
200         struct comedi_subdevice *last_int_sub;  /*  ptr to subdevice which now finish */
201         int ai_act_scan;        /*  how many scans we finished */
202         unsigned int ai_act_chanlist[16];       /*  MUX setting for actual AI operations */
203         unsigned int ai_act_chanlist_len;       /*  how long is actual MUX list */
204         unsigned int ai_act_chanlist_pos;       /*  actual position in MUX list */
205         unsigned int ai_poll_ptr;       /*  how many sampes transfer poll */
206         struct comedi_subdevice *sub_ai;        /*  ptr to AI subdevice */
207 #ifdef unused
208         struct timer_list rtc_irq_timer;        /*  timer for RTC sanity check */
209         unsigned long rtc_freq; /*  RTC int freq */
210 #endif
211 };
212
213 /*
214 ==============================================================================
215 */
216 static int check_and_setup_channel_list(struct comedi_device *dev,
217                                         struct comedi_subdevice *s,
218                                         unsigned int *chanlist, int chanlen);
219 static int pcl816_ai_cancel(struct comedi_device *dev,
220                             struct comedi_subdevice *s);
221 static void start_pacer(struct comedi_device *dev, int mode,
222                         unsigned int divisor1, unsigned int divisor2);
223 #ifdef unused
224 static int set_rtc_irq_bit(unsigned char bit);
225 #endif
226
227 static int pcl816_ai_cmdtest(struct comedi_device *dev,
228                              struct comedi_subdevice *s,
229                              struct comedi_cmd *cmd);
230 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
231
232 /*
233 ==============================================================================
234    ANALOG INPUT MODE0, 816 cards, slow version
235 */
236 static int pcl816_ai_insn_read(struct comedi_device *dev,
237                                struct comedi_subdevice *s,
238                                struct comedi_insn *insn, unsigned int *data)
239 {
240         int n;
241         int timeout;
242
243         DPRINTK("mode 0 analog input\n");
244         /*  software trigger, DMA and INT off */
245         outb(0, dev->iobase + PCL816_CONTROL);
246         /*  clear INT (conversion end) flag */
247         outb(0, dev->iobase + PCL816_CLRINT);
248
249         /*  Set the input channel */
250         outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
251         outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE);     /* select gain */
252
253         for (n = 0; n < insn->n; n++) {
254
255                 outb(0, dev->iobase + PCL816_AD_LO);    /* start conversion */
256
257                 timeout = 100;
258                 while (timeout--) {
259                         if (!(inb(dev->iobase + PCL816_STATUS) &
260                               PCL816_STATUS_DRDY_MASK)) {
261                                 /*  return read value */
262                                 data[n] =
263                                     ((inb(dev->iobase +
264                                           PCL816_AD_HI) << 8) |
265                                      (inb(dev->iobase + PCL816_AD_LO)));
266
267                                 outb(0, dev->iobase + PCL816_CLRINT);   /* clear INT (conversion end) flag */
268                                 break;
269                         }
270                         udelay(1);
271                 }
272                 /*  Return timeout error */
273                 if (!timeout) {
274                         comedi_error(dev, "A/D insn timeout\n");
275                         data[0] = 0;
276                         outb(0, dev->iobase + PCL816_CLRINT);   /* clear INT (conversion end) flag */
277                         return -EIO;
278                 }
279
280         }
281         return n;
282 }
283
284 /*
285 ==============================================================================
286    analog input interrupt mode 1 & 3, 818 cards
287    one sample per interrupt version
288 */
289 static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
290 {
291         struct comedi_device *dev = d;
292         struct comedi_subdevice *s = dev->subdevices + 0;
293         int low, hi;
294         int timeout = 50;       /* wait max 50us */
295
296         while (timeout--) {
297                 if (!(inb(dev->iobase + PCL816_STATUS) &
298                       PCL816_STATUS_DRDY_MASK))
299                         break;
300                 udelay(1);
301         }
302         if (!timeout) {         /*  timeout, bail error */
303                 outb(0, dev->iobase + PCL816_CLRINT);   /* clear INT request */
304                 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
305                 pcl816_ai_cancel(dev, s);
306                 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
307                 comedi_event(dev, s);
308                 return IRQ_HANDLED;
309
310         }
311
312         /*  get the sample */
313         low = inb(dev->iobase + PCL816_AD_LO);
314         hi = inb(dev->iobase + PCL816_AD_HI);
315
316         comedi_buf_put(s->async, (hi << 8) | low);
317
318         outb(0, dev->iobase + PCL816_CLRINT);   /* clear INT request */
319
320         if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
321                 devpriv->ai_act_chanlist_pos = 0;
322
323         if (s->async->cur_chan == 0) {
324                 devpriv->ai_act_scan++;
325         }
326
327         if (!devpriv->ai_neverending)
328                 if (devpriv->ai_act_scan >= devpriv->ai_scans) {        /* all data sampled */
329                         /* all data sampled */
330                         pcl816_ai_cancel(dev, s);
331                         s->async->events |= COMEDI_CB_EOA;
332                 }
333         comedi_event(dev, s);
334         return IRQ_HANDLED;
335 }
336
337 /*
338 ==============================================================================
339    analog input dma mode 1 & 3, 816 cards
340 */
341 static void transfer_from_dma_buf(struct comedi_device *dev,
342                                   struct comedi_subdevice *s, short *ptr,
343                                   unsigned int bufptr, unsigned int len)
344 {
345         int i;
346
347         s->async->events = 0;
348
349         for (i = 0; i < len; i++) {
350
351                 comedi_buf_put(s->async, ptr[bufptr++]);
352
353                 if (++devpriv->ai_act_chanlist_pos >=
354                     devpriv->ai_act_chanlist_len) {
355                         devpriv->ai_act_chanlist_pos = 0;
356                         devpriv->ai_act_scan++;
357                 }
358
359                 if (!devpriv->ai_neverending)
360                         if (devpriv->ai_act_scan >= devpriv->ai_scans) {        /*  all data sampled */
361                                 pcl816_ai_cancel(dev, s);
362                                 s->async->events |= COMEDI_CB_EOA;
363                                 s->async->events |= COMEDI_CB_BLOCK;
364                                 break;
365                         }
366         }
367
368         comedi_event(dev, s);
369 }
370
371 static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
372 {
373         struct comedi_device *dev = d;
374         struct comedi_subdevice *s = dev->subdevices + 0;
375         int len, bufptr, this_dma_buf;
376         unsigned long dma_flags;
377         short *ptr;
378
379         disable_dma(devpriv->dma);
380         this_dma_buf = devpriv->next_dma_buf;
381
382         if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) {       /*  switch dma bufs */
383
384                 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
385                 set_dma_mode(devpriv->dma, DMA_MODE_READ);
386                 dma_flags = claim_dma_lock();
387 /* clear_dma_ff (devpriv->dma); */
388                 set_dma_addr(devpriv->dma,
389                              devpriv->hwdmaptr[devpriv->next_dma_buf]);
390                 if (devpriv->dma_runs_to_end) {
391                         set_dma_count(devpriv->dma,
392                                       devpriv->hwdmasize[devpriv->
393                                                          next_dma_buf]);
394                 } else {
395                         set_dma_count(devpriv->dma, devpriv->last_dma_run);
396                 }
397                 release_dma_lock(dma_flags);
398                 enable_dma(devpriv->dma);
399         }
400
401         devpriv->dma_runs_to_end--;
402         outb(0, dev->iobase + PCL816_CLRINT);   /* clear INT request */
403
404         ptr = (short *)devpriv->dmabuf[this_dma_buf];
405
406         len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
407         bufptr = devpriv->ai_poll_ptr;
408         devpriv->ai_poll_ptr = 0;
409
410         transfer_from_dma_buf(dev, s, ptr, bufptr, len);
411         return IRQ_HANDLED;
412 }
413
414 /*
415 ==============================================================================
416     INT procedure
417 */
418 static irqreturn_t interrupt_pcl816(int irq, void *d)
419 {
420         struct comedi_device *dev = d;
421         DPRINTK("<I>");
422
423         if (!dev->attached) {
424                 comedi_error(dev, "premature interrupt");
425                 return IRQ_HANDLED;
426         }
427
428         switch (devpriv->int816_mode) {
429         case INT_TYPE_AI1_DMA:
430         case INT_TYPE_AI3_DMA:
431                 return interrupt_pcl816_ai_mode13_dma(irq, d);
432         case INT_TYPE_AI1_INT:
433         case INT_TYPE_AI3_INT:
434                 return interrupt_pcl816_ai_mode13_int(irq, d);
435         }
436
437         outb(0, dev->iobase + PCL816_CLRINT);   /* clear INT request */
438         if ((!dev->irq) | (!devpriv->irq_free) | (!devpriv->irq_blocked) |
439             (!devpriv->int816_mode)) {
440                 if (devpriv->irq_was_now_closed) {
441                         devpriv->irq_was_now_closed = 0;
442                         /*  comedi_error(dev,"last IRQ.."); */
443                         return IRQ_HANDLED;
444                 }
445                 comedi_error(dev, "bad IRQ!");
446                 return IRQ_NONE;
447         }
448         comedi_error(dev, "IRQ from unknow source!");
449         return IRQ_NONE;
450 }
451
452 /*
453 ==============================================================================
454    COMMAND MODE
455 */
456 static void pcl816_cmdtest_out(int e, struct comedi_cmd *cmd)
457 {
458         printk("pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
459                cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
460         printk("pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
461                cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
462         printk("pcl816 e=%d stopsrc=%x scanend=%x\n", e, cmd->stop_src,
463                cmd->scan_end_src);
464         printk("pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n", e,
465                cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
466 }
467
468 /*
469 ==============================================================================
470 */
471 static int pcl816_ai_cmdtest(struct comedi_device *dev,
472                              struct comedi_subdevice *s, struct comedi_cmd *cmd)
473 {
474         int err = 0;
475         int tmp, divisor1 = 0, divisor2 = 0;
476
477         DEBUG(printk("pcl816 pcl812_ai_cmdtest\n"); pcl816_cmdtest_out(-1, cmd);
478             );
479
480         /* step 1: make sure trigger sources are trivially valid */
481         tmp = cmd->start_src;
482         cmd->start_src &= TRIG_NOW;
483         if (!cmd->start_src || tmp != cmd->start_src)
484                 err++;
485
486         tmp = cmd->scan_begin_src;
487         cmd->scan_begin_src &= TRIG_FOLLOW;
488         if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
489                 err++;
490
491         tmp = cmd->convert_src;
492         cmd->convert_src &= TRIG_EXT | TRIG_TIMER;
493         if (!cmd->convert_src || tmp != cmd->convert_src)
494                 err++;
495
496         tmp = cmd->scan_end_src;
497         cmd->scan_end_src &= TRIG_COUNT;
498         if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
499                 err++;
500
501         tmp = cmd->stop_src;
502         cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
503         if (!cmd->stop_src || tmp != cmd->stop_src)
504                 err++;
505
506         if (err) {
507                 return 1;
508         }
509
510         /* step 2: make sure trigger sources are unique and mutually compatible */
511
512         if (cmd->start_src != TRIG_NOW) {
513                 cmd->start_src = TRIG_NOW;
514                 err++;
515         }
516
517         if (cmd->scan_begin_src != TRIG_FOLLOW) {
518                 cmd->scan_begin_src = TRIG_FOLLOW;
519                 err++;
520         }
521
522         if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) {
523                 cmd->convert_src = TRIG_TIMER;
524                 err++;
525         }
526
527         if (cmd->scan_end_src != TRIG_COUNT) {
528                 cmd->scan_end_src = TRIG_COUNT;
529                 err++;
530         }
531
532         if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
533                 err++;
534
535         if (err) {
536                 return 2;
537         }
538
539         /* step 3: make sure arguments are trivially compatible */
540         if (cmd->start_arg != 0) {
541                 cmd->start_arg = 0;
542                 err++;
543         }
544
545         if (cmd->scan_begin_arg != 0) {
546                 cmd->scan_begin_arg = 0;
547                 err++;
548         }
549         if (cmd->convert_src == TRIG_TIMER) {
550                 if (cmd->convert_arg < this_board->ai_ns_min) {
551                         cmd->convert_arg = this_board->ai_ns_min;
552                         err++;
553                 }
554         } else {                /* TRIG_EXT */
555                 if (cmd->convert_arg != 0) {
556                         cmd->convert_arg = 0;
557                         err++;
558                 }
559         }
560
561         if (!cmd->chanlist_len) {
562                 cmd->chanlist_len = 1;
563                 err++;
564         }
565         if (cmd->chanlist_len > this_board->n_aichan) {
566                 cmd->chanlist_len = this_board->n_aichan;
567                 err++;
568         }
569         if (cmd->scan_end_arg != cmd->chanlist_len) {
570                 cmd->scan_end_arg = cmd->chanlist_len;
571                 err++;
572         }
573         if (cmd->stop_src == TRIG_COUNT) {
574                 if (!cmd->stop_arg) {
575                         cmd->stop_arg = 1;
576                         err++;
577                 }
578         } else {                /* TRIG_NONE */
579                 if (cmd->stop_arg != 0) {
580                         cmd->stop_arg = 0;
581                         err++;
582                 }
583         }
584
585         if (err) {
586                 return 3;
587         }
588
589         /* step 4: fix up any arguments */
590         if (cmd->convert_src == TRIG_TIMER) {
591                 tmp = cmd->convert_arg;
592                 i8253_cascade_ns_to_timer(this_board->i8254_osc_base,
593                                           &divisor1, &divisor2,
594                                           &cmd->convert_arg,
595                                           cmd->flags & TRIG_ROUND_MASK);
596                 if (cmd->convert_arg < this_board->ai_ns_min)
597                         cmd->convert_arg = this_board->ai_ns_min;
598                 if (tmp != cmd->convert_arg)
599                         err++;
600         }
601
602         if (err) {
603                 return 4;
604         }
605
606         return 0;
607 }
608
609 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
610 {
611         unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
612         struct comedi_cmd *cmd = &s->async->cmd;
613
614         if (cmd->start_src != TRIG_NOW)
615                 return -EINVAL;
616         if (cmd->scan_begin_src != TRIG_FOLLOW)
617                 return -EINVAL;
618         if (cmd->scan_end_src != TRIG_COUNT)
619                 return -EINVAL;
620         if (cmd->scan_end_arg != cmd->chanlist_len)
621                 return -EINVAL;
622 /* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */
623         if (devpriv->irq_blocked)
624                 return -EBUSY;
625
626         if (cmd->convert_src == TRIG_TIMER) {
627                 if (cmd->convert_arg < this_board->ai_ns_min)
628                         cmd->convert_arg = this_board->ai_ns_min;
629
630                 i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1,
631                                           &divisor2, &cmd->convert_arg,
632                                           cmd->flags & TRIG_ROUND_MASK);
633                 if (divisor1 == 1) {    /*  PCL816 crash if any divisor is set to 1 */
634                         divisor1 = 2;
635                         divisor2 /= 2;
636                 }
637                 if (divisor2 == 1) {
638                         divisor2 = 2;
639                         divisor1 /= 2;
640                 }
641         }
642
643         start_pacer(dev, -1, 0, 0);     /*  stop pacer */
644
645         if (!check_and_setup_channel_list(dev, s, cmd->chanlist,
646                                           cmd->chanlist_len))
647                 return -EINVAL;
648         udelay(1);
649
650         devpriv->ai_act_scan = 0;
651         s->async->cur_chan = 0;
652         devpriv->irq_blocked = 1;
653         devpriv->ai_poll_ptr = 0;
654         devpriv->irq_was_now_closed = 0;
655
656         if (cmd->stop_src == TRIG_COUNT) {
657                 devpriv->ai_scans = cmd->stop_arg;
658                 devpriv->ai_neverending = 0;
659         } else {
660                 devpriv->ai_scans = 0;
661                 devpriv->ai_neverending = 1;
662         }
663
664         if ((cmd->flags & TRIG_WAKE_EOS)) {     /*  don't we want wake up every scan? */
665                 printk("pl816: You wankt WAKE_EOS but I dont want handle it");
666                 /*               devpriv->ai_eos=1; */
667                 /* if (devpriv->ai_n_chan==1) */
668                 /*       devpriv->dma=0; // DMA is useless for this situation */
669         }
670
671         if (devpriv->dma) {
672                 bytes = devpriv->hwdmasize[0];
673                 if (!devpriv->ai_neverending) {
674                         bytes = s->async->cmd.chanlist_len * s->async->cmd.chanlist_len * sizeof(short);        /*  how many */
675                         devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize[0];       /*  how many DMA pages we must fill */
676                         devpriv->last_dma_run = bytes % devpriv->hwdmasize[0];  /* on last dma transfer must be moved */
677                         devpriv->dma_runs_to_end--;
678                         if (devpriv->dma_runs_to_end >= 0)
679                                 bytes = devpriv->hwdmasize[0];
680                 } else
681                         devpriv->dma_runs_to_end = -1;
682
683                 devpriv->next_dma_buf = 0;
684                 set_dma_mode(devpriv->dma, DMA_MODE_READ);
685                 dma_flags = claim_dma_lock();
686                 clear_dma_ff(devpriv->dma);
687                 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
688                 set_dma_count(devpriv->dma, bytes);
689                 release_dma_lock(dma_flags);
690                 enable_dma(devpriv->dma);
691         }
692
693         start_pacer(dev, 1, divisor1, divisor2);
694         dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
695
696         switch (cmd->convert_src) {
697         case TRIG_TIMER:
698                 devpriv->int816_mode = INT_TYPE_AI1_DMA;
699                 outb(0x32, dev->iobase + PCL816_CONTROL);       /*  Pacer+IRQ+DMA */
700                 outb(dmairq, dev->iobase + PCL816_STATUS);      /*  write irq and DMA to card */
701                 break;
702
703         default:
704                 devpriv->int816_mode = INT_TYPE_AI3_DMA;
705                 outb(0x34, dev->iobase + PCL816_CONTROL);       /*  Ext trig+IRQ+DMA */
706                 outb(dmairq, dev->iobase + PCL816_STATUS);      /*  write irq to card */
707                 break;
708         }
709
710         DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
711         return 0;
712 }
713
714 static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
715 {
716         unsigned long flags;
717         unsigned int top1, top2, i;
718
719         if (!devpriv->dma)
720                 return 0;       /*  poll is valid only for DMA transfer */
721
722         spin_lock_irqsave(&dev->spinlock, flags);
723
724         for (i = 0; i < 20; i++) {
725                 top1 = get_dma_residue(devpriv->dma);   /*  where is now DMA */
726                 top2 = get_dma_residue(devpriv->dma);
727                 if (top1 == top2)
728                         break;
729         }
730         if (top1 != top2) {
731                 spin_unlock_irqrestore(&dev->spinlock, flags);
732                 return 0;
733         }
734
735         top1 = devpriv->hwdmasize[0] - top1;    /*  where is now DMA in buffer */
736         top1 >>= 1;             /*  sample position */
737         top2 = top1 - devpriv->ai_poll_ptr;
738         if (top2 < 1) {         /*  no new samples */
739                 spin_unlock_irqrestore(&dev->spinlock, flags);
740                 return 0;
741         }
742
743         transfer_from_dma_buf(dev, s,
744                               (short *)devpriv->dmabuf[devpriv->next_dma_buf],
745                               devpriv->ai_poll_ptr, top2);
746
747         devpriv->ai_poll_ptr = top1;    /*  new buffer position */
748         spin_unlock_irqrestore(&dev->spinlock, flags);
749
750         return s->async->buf_write_count - s->async->buf_read_count;
751 }
752
753 /*
754 ==============================================================================
755  cancel any mode 1-4 AI
756 */
757 static int pcl816_ai_cancel(struct comedi_device *dev,
758                             struct comedi_subdevice *s)
759 {
760 /* DEBUG(printk("pcl816_ai_cancel()\n");) */
761
762         if (devpriv->irq_blocked > 0) {
763                 switch (devpriv->int816_mode) {
764 #ifdef unused
765                 case INT_TYPE_AI1_DMA_RTC:
766                 case INT_TYPE_AI3_DMA_RTC:
767                         set_rtc_irq_bit(0);     /*  stop RTC */
768                         del_timer(&devpriv->rtc_irq_timer);
769 #endif
770                 case INT_TYPE_AI1_DMA:
771                 case INT_TYPE_AI3_DMA:
772                         disable_dma(devpriv->dma);
773                 case INT_TYPE_AI1_INT:
774                 case INT_TYPE_AI3_INT:
775                         outb(inb(dev->iobase + PCL816_CONTROL) & 0x73, dev->iobase + PCL816_CONTROL);   /* Stop A/D */
776                         udelay(1);
777                         outb(0, dev->iobase + PCL816_CONTROL);  /* Stop A/D */
778                         outb(0xb0, dev->iobase + PCL816_CTRCTL);        /* Stop pacer */
779                         outb(0x70, dev->iobase + PCL816_CTRCTL);
780                         outb(0, dev->iobase + PCL816_AD_LO);
781                         inb(dev->iobase + PCL816_AD_LO);
782                         inb(dev->iobase + PCL816_AD_HI);
783                         outb(0, dev->iobase + PCL816_CLRINT);   /* clear INT request */
784                         outb(0, dev->iobase + PCL816_CONTROL);  /* Stop A/D */
785                         devpriv->irq_blocked = 0;
786                         devpriv->irq_was_now_closed = devpriv->int816_mode;
787                         devpriv->int816_mode = 0;
788                         devpriv->last_int_sub = s;
789 /* s->busy = 0; */
790                         break;
791                 }
792         }
793
794         DEBUG(printk("comedi: pcl816_ai_cancel() successful\n");)
795             return 0;
796 }
797
798 /*
799 ==============================================================================
800  chech for PCL816
801 */
802 static int pcl816_check(unsigned long iobase)
803 {
804         outb(0x00, iobase + PCL816_MUX);
805         udelay(1);
806         if (inb(iobase + PCL816_MUX) != 0x00)
807                 return 1;       /* there isn't card */
808         outb(0x55, iobase + PCL816_MUX);
809         udelay(1);
810         if (inb(iobase + PCL816_MUX) != 0x55)
811                 return 1;       /* there isn't card */
812         outb(0x00, iobase + PCL816_MUX);
813         udelay(1);
814         outb(0x18, iobase + PCL816_CONTROL);
815         udelay(1);
816         if (inb(iobase + PCL816_CONTROL) != 0x18)
817                 return 1;       /* there isn't card */
818         return 0;               /*  ok, card exist */
819 }
820
821 /*
822 ==============================================================================
823  reset whole PCL-816 cards
824 */
825 static void pcl816_reset(struct comedi_device *dev)
826 {
827 /* outb (0, dev->iobase + PCL818_DA_LO);         DAC=0V */
828 /* outb (0, dev->iobase + PCL818_DA_HI); */
829 /* udelay (1); */
830 /* outb (0, dev->iobase + PCL818_DO_HI);        DO=$0000 */
831 /* outb (0, dev->iobase + PCL818_DO_LO); */
832 /* udelay (1); */
833         outb(0, dev->iobase + PCL816_CONTROL);
834         outb(0, dev->iobase + PCL816_MUX);
835         outb(0, dev->iobase + PCL816_CLRINT);
836         outb(0xb0, dev->iobase + PCL816_CTRCTL);        /* Stop pacer */
837         outb(0x70, dev->iobase + PCL816_CTRCTL);
838         outb(0x30, dev->iobase + PCL816_CTRCTL);
839         outb(0, dev->iobase + PCL816_RANGE);
840 }
841
842 /*
843 ==============================================================================
844  Start/stop pacer onboard pacer
845 */
846 static void
847 start_pacer(struct comedi_device *dev, int mode, unsigned int divisor1,
848             unsigned int divisor2)
849 {
850         outb(0x32, dev->iobase + PCL816_CTRCTL);
851         outb(0xff, dev->iobase + PCL816_CTR0);
852         outb(0x00, dev->iobase + PCL816_CTR0);
853         udelay(1);
854         outb(0xb4, dev->iobase + PCL816_CTRCTL);        /*  set counter 2 as mode 3 */
855         outb(0x74, dev->iobase + PCL816_CTRCTL);        /*  set counter 1 as mode 3 */
856         udelay(1);
857
858         if (mode == 1) {
859                 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
860                         divisor2);
861                 outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
862                 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
863                 outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
864                 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
865         }
866
867         /* clear pending interrupts (just in case) */
868 /* outb(0, dev->iobase + PCL816_CLRINT); */
869 }
870
871 /*
872 ==============================================================================
873  Check if channel list from user is builded correctly
874  If it's ok, then program scan/gain logic
875 */
876 static int
877 check_and_setup_channel_list(struct comedi_device *dev,
878                              struct comedi_subdevice *s, unsigned int *chanlist,
879                              int chanlen)
880 {
881         unsigned int chansegment[16];
882         unsigned int i, nowmustbechan, seglen, segpos;
883
884         /*  correct channel and range number check itself comedi/range.c */
885         if (chanlen < 1) {
886                 comedi_error(dev, "range/channel list is empty!");
887                 return 0;
888         }
889
890         if (chanlen > 1) {
891                 chansegment[0] = chanlist[0];   /*  first channel is everytime ok */
892                 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
893                         /*  build part of chanlist */
894                         DEBUG(printk("%d. %d %d\n", i, CR_CHAN(chanlist[i]),
895                                      CR_RANGE(chanlist[i]));)
896                             if (chanlist[0] == chanlist[i])
897                                 break;  /*  we detect loop, this must by finish */
898                         nowmustbechan =
899                             (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
900                         if (nowmustbechan != CR_CHAN(chanlist[i])) {
901                                 /*  channel list isn't continous :-( */
902                                 printk
903                                     ("comedi%d: pcl816: channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
904                                      dev->minor, i, CR_CHAN(chanlist[i]),
905                                      nowmustbechan, CR_CHAN(chanlist[0]));
906                                 return 0;
907                         }
908                         chansegment[i] = chanlist[i];   /*  well, this is next correct channel in list */
909                 }
910
911                 for (i = 0, segpos = 0; i < chanlen; i++) {     /*  check whole chanlist */
912                         DEBUG(printk("%d %d=%d %d\n",
913                                      CR_CHAN(chansegment[i % seglen]),
914                                      CR_RANGE(chansegment[i % seglen]),
915                                      CR_CHAN(chanlist[i]),
916                                      CR_RANGE(chanlist[i]));)
917                             if (chanlist[i] != chansegment[i % seglen]) {
918                                 printk
919                                     ("comedi%d: pcl816: bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
920                                      dev->minor, i, CR_CHAN(chansegment[i]),
921                                      CR_RANGE(chansegment[i]),
922                                      CR_AREF(chansegment[i]),
923                                      CR_CHAN(chanlist[i % seglen]),
924                                      CR_RANGE(chanlist[i % seglen]),
925                                      CR_AREF(chansegment[i % seglen]));
926                                 return 0;       /*  chan/gain list is strange */
927                         }
928                 }
929         } else {
930                 seglen = 1;
931         }
932
933         devpriv->ai_act_chanlist_len = seglen;
934         devpriv->ai_act_chanlist_pos = 0;
935
936         for (i = 0; i < seglen; i++) {  /*  store range list to card */
937                 devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
938                 outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
939                 outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE);        /* select gain */
940         }
941
942         udelay(1);
943
944         outb(devpriv->ai_act_chanlist[0] | (devpriv->ai_act_chanlist[seglen - 1] << 4), dev->iobase + PCL816_MUX);      /* select channel interval to scan */
945
946         return 1;               /*  we can serve this with MUX logic */
947 }
948
949 #ifdef unused
950 /*
951 ==============================================================================
952   Enable(1)/disable(0) periodic interrupts from RTC
953 */
954 static int set_rtc_irq_bit(unsigned char bit)
955 {
956         unsigned char val;
957         unsigned long flags;
958
959         if (bit == 1) {
960                 RTC_timer_lock++;
961                 if (RTC_timer_lock > 1)
962                         return 0;
963         } else {
964                 RTC_timer_lock--;
965                 if (RTC_timer_lock < 0)
966                         RTC_timer_lock = 0;
967                 if (RTC_timer_lock > 0)
968                         return 0;
969         }
970
971         save_flags(flags);
972         cli();
973         val = CMOS_READ(RTC_CONTROL);
974         if (bit) {
975                 val |= RTC_PIE;
976         } else {
977                 val &= ~RTC_PIE;
978         }
979         CMOS_WRITE(val, RTC_CONTROL);
980         CMOS_READ(RTC_INTR_FLAGS);
981         restore_flags(flags);
982         return 0;
983 }
984 #endif
985
986 /*
987 ==============================================================================
988   Free any resources that we have claimed
989 */
990 static void free_resources(struct comedi_device *dev)
991 {
992         /* printk("free_resource()\n"); */
993         if (dev->private) {
994                 pcl816_ai_cancel(dev, devpriv->sub_ai);
995                 pcl816_reset(dev);
996                 if (devpriv->dma)
997                         free_dma(devpriv->dma);
998                 if (devpriv->dmabuf[0])
999                         free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1000                 if (devpriv->dmabuf[1])
1001                         free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
1002 #ifdef unused
1003                 if (devpriv->rtc_irq)
1004                         free_irq(devpriv->rtc_irq, dev);
1005                 if ((devpriv->dma_rtc) && (RTC_lock == 1)) {
1006                         if (devpriv->rtc_iobase)
1007                                 release_region(devpriv->rtc_iobase,
1008                                                devpriv->rtc_iosize);
1009                 }
1010 #endif
1011         }
1012
1013         if (dev->irq)
1014                 free_irq(dev->irq, dev);
1015         if (dev->iobase)
1016                 release_region(dev->iobase, this_board->io_range);
1017         /* printk("free_resource() end\n"); */
1018 }
1019
1020 /*
1021 ==============================================================================
1022
1023    Initialization
1024
1025 */
1026 static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1027 {
1028         int ret;
1029         unsigned long iobase;
1030         unsigned int irq, dma;
1031         unsigned long pages;
1032         /* int i; */
1033         struct comedi_subdevice *s;
1034
1035         /* claim our I/O space */
1036         iobase = it->options[0];
1037         printk("comedi%d: pcl816:  board=%s, ioport=0x%03lx", dev->minor,
1038                this_board->name, iobase);
1039
1040         if (!request_region(iobase, this_board->io_range, "pcl816")) {
1041                 printk("I/O port conflict\n");
1042                 return -EIO;
1043         }
1044
1045         dev->iobase = iobase;
1046
1047         if (pcl816_check(iobase)) {
1048                 printk(", I cann't detect board. FAIL!\n");
1049                 return -EIO;
1050         }
1051
1052         ret = alloc_private(dev, sizeof(struct pcl816_private));
1053         if (ret < 0)
1054                 return ret;     /* Can't alloc mem */
1055
1056         /* set up some name stuff */
1057         dev->board_name = this_board->name;
1058
1059         /* grab our IRQ */
1060         irq = 0;
1061         if (this_board->IRQbits != 0) { /* board support IRQ */
1062                 irq = it->options[1];
1063                 if (irq) {      /* we want to use IRQ */
1064                         if (((1 << irq) & this_board->IRQbits) == 0) {
1065                                 printk
1066                                     (", IRQ %u is out of allowed range, DISABLING IT",
1067                                      irq);
1068                                 irq = 0;        /* Bad IRQ */
1069                         } else {
1070                                 if (request_irq
1071                                     (irq, interrupt_pcl816, 0, "pcl816", dev)) {
1072                                         printk
1073                                             (", unable to allocate IRQ %u, DISABLING IT",
1074                                              irq);
1075                                         irq = 0;        /* Can't use IRQ */
1076                                 } else {
1077                                         printk(", irq=%u", irq);
1078                                 }
1079                         }
1080                 }
1081         }
1082
1083         dev->irq = irq;
1084         if (irq) {
1085                 devpriv->irq_free = 1;
1086         } /* 1=we have allocated irq */
1087         else {
1088                 devpriv->irq_free = 0;
1089         }
1090         devpriv->irq_blocked = 0;       /* number of subdevice which use IRQ */
1091         devpriv->int816_mode = 0;       /* mode of irq */
1092
1093 #ifdef unused
1094         /* grab RTC for DMA operations */
1095         devpriv->dma_rtc = 0;
1096         if (it->options[2] > 0) {       /*  we want to use DMA */
1097                 if (RTC_lock == 0) {
1098                         if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
1099                                             "pcl816 (RTC)"))
1100                                 goto no_rtc;
1101                 }
1102                 devpriv->rtc_iobase = RTC_PORT(0);
1103                 devpriv->rtc_iosize = RTC_IO_EXTENT;
1104                 RTC_lock++;
1105 #ifdef UNTESTED_CODE
1106                 if (!request_irq(RTC_IRQ, interrupt_pcl816_ai_mode13_dma_rtc, 0,
1107                                  "pcl816 DMA (RTC)", dev)) {
1108                         devpriv->dma_rtc = 1;
1109                         devpriv->rtc_irq = RTC_IRQ;
1110                         printk(", dma_irq=%u", devpriv->rtc_irq);
1111                 } else {
1112                         RTC_lock--;
1113                         if (RTC_lock == 0) {
1114                                 if (devpriv->rtc_iobase)
1115                                         release_region(devpriv->rtc_iobase,
1116                                                        devpriv->rtc_iosize);
1117                         }
1118                         devpriv->rtc_iobase = 0;
1119                         devpriv->rtc_iosize = 0;
1120                 }
1121 #else
1122                 printk("pcl816: RTC code missing");
1123 #endif
1124
1125         }
1126
1127 no_rtc:
1128 #endif
1129         /* grab our DMA */
1130         dma = 0;
1131         devpriv->dma = dma;
1132         if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
1133                 goto no_dma;    /* if we haven't IRQ, we can't use DMA */
1134
1135         if (this_board->DMAbits != 0) { /* board support DMA */
1136                 dma = it->options[2];
1137                 if (dma < 1)
1138                         goto no_dma;    /* DMA disabled */
1139
1140                 if (((1 << dma) & this_board->DMAbits) == 0) {
1141                         printk(", DMA is out of allowed range, FAIL!\n");
1142                         return -EINVAL; /* Bad DMA */
1143                 }
1144                 ret = request_dma(dma, "pcl816");
1145                 if (ret) {
1146                         printk(", unable to allocate DMA %u, FAIL!\n", dma);
1147                         return -EBUSY;  /* DMA isn't free */
1148                 }
1149
1150                 devpriv->dma = dma;
1151                 printk(", dma=%u", dma);
1152                 pages = 2;      /* we need 16KB */
1153                 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1154
1155                 if (!devpriv->dmabuf[0]) {
1156                         printk(", unable to allocate DMA buffer, FAIL!\n");
1157                         /* maybe experiment with try_to_free_pages() will help .... */
1158                         return -EBUSY;  /* no buffer :-( */
1159                 }
1160                 devpriv->dmapages[0] = pages;
1161                 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1162                 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
1163                 /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
1164
1165                 if (devpriv->dma_rtc == 0) {    /*  we must do duble buff :-( */
1166                         devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1167                         if (!devpriv->dmabuf[1]) {
1168                                 printk
1169                                     (", unable to allocate DMA buffer, FAIL!\n");
1170                                 return -EBUSY;
1171                         }
1172                         devpriv->dmapages[1] = pages;
1173                         devpriv->hwdmaptr[1] =
1174                             virt_to_bus((void *)devpriv->dmabuf[1]);
1175                         devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
1176                 }
1177         }
1178
1179 no_dma:
1180
1181 /*  if (this_board->n_aochan > 0)
1182     subdevs[1] = COMEDI_SUBD_AO;
1183   if (this_board->n_dichan > 0)
1184     subdevs[2] = COMEDI_SUBD_DI;
1185   if (this_board->n_dochan > 0)
1186     subdevs[3] = COMEDI_SUBD_DO;
1187 */
1188
1189         ret = alloc_subdevices(dev, 1);
1190         if (ret < 0)
1191                 return ret;
1192
1193         s = dev->subdevices + 0;
1194         if (this_board->n_aichan > 0) {
1195                 s->type = COMEDI_SUBD_AI;
1196                 devpriv->sub_ai = s;
1197                 dev->read_subdev = s;
1198                 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
1199                 s->n_chan = this_board->n_aichan;
1200                 s->subdev_flags |= SDF_DIFF;
1201                 /* printk (", %dchans DIFF DAC - %d", s->n_chan, i); */
1202                 s->maxdata = this_board->ai_maxdata;
1203                 s->len_chanlist = this_board->ai_chanlist;
1204                 s->range_table = this_board->ai_range_type;
1205                 s->cancel = pcl816_ai_cancel;
1206                 s->do_cmdtest = pcl816_ai_cmdtest;
1207                 s->do_cmd = pcl816_ai_cmd;
1208                 s->poll = pcl816_ai_poll;
1209                 s->insn_read = pcl816_ai_insn_read;
1210         } else {
1211                 s->type = COMEDI_SUBD_UNUSED;
1212         }
1213
1214 #if 0
1215 case COMEDI_SUBD_AO:
1216         s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1217         s->n_chan = this_board->n_aochan;
1218         s->maxdata = this_board->ao_maxdata;
1219         s->len_chanlist = this_board->ao_chanlist;
1220         s->range_table = this_board->ao_range_type;
1221         break;
1222
1223 case COMEDI_SUBD_DI:
1224         s->subdev_flags = SDF_READABLE;
1225         s->n_chan = this_board->n_dichan;
1226         s->maxdata = 1;
1227         s->len_chanlist = this_board->n_dichan;
1228         s->range_table = &range_digital;
1229         break;
1230
1231 case COMEDI_SUBD_DO:
1232         s->subdev_flags = SDF_WRITABLE;
1233         s->n_chan = this_board->n_dochan;
1234         s->maxdata = 1;
1235         s->len_chanlist = this_board->n_dochan;
1236         s->range_table = &range_digital;
1237         break;
1238 #endif
1239
1240         pcl816_reset(dev);
1241
1242         printk("\n");
1243
1244         return 0;
1245 }
1246
1247 /*
1248 ==============================================================================
1249   Removes device
1250  */
1251 static int pcl816_detach(struct comedi_device *dev)
1252 {
1253         DEBUG(printk("comedi%d: pcl816: remove\n", dev->minor);)
1254             free_resources(dev);
1255 #ifdef unused
1256         if (devpriv->dma_rtc)
1257                 RTC_lock--;
1258 #endif
1259         return 0;
1260 }