Staging: comedi: Remove comedi_device typedef
[pandora-kernel.git] / drivers / staging / comedi / drivers / das800.c
1 /*
2     comedi/drivers/das800.c
3     Driver for Keitley das800 series boards and compatibles
4     Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net>
5
6     COMEDI - Linux Control and Measurement Device Interface
7     Copyright (C) 2000 David A. Schleef <ds@schleef.org>
8
9     This program is free software; you can redistribute it and/or modify
10     it under the terms of the GNU General Public License as published by
11     the Free Software Foundation; either version 2 of the License, or
12     (at your option) any later version.
13
14     This program is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18
19     You should have received a copy of the GNU General Public License
20     along with this program; if not, write to the Free Software
21     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22
23 ************************************************************************
24 */
25 /*
26 Driver: das800
27 Description: Keithley Metrabyte DAS800 (& compatibles)
28 Author: Frank Mori Hess <fmhess@users.sourceforge.net>
29 Devices: [Keithley Metrabyte] DAS-800 (das-800), DAS-801 (das-801),
30   DAS-802 (das-802),
31   [Measurement Computing] CIO-DAS800 (cio-das800),
32   CIO-DAS801 (cio-das801), CIO-DAS802 (cio-das802),
33   CIO-DAS802/16 (cio-das802/16)
34 Status: works, cio-das802/16 untested - email me if you have tested it
35
36 Configuration options:
37   [0] - I/O port base address
38   [1] - IRQ (optional, required for timed or externally triggered conversions)
39
40 Notes:
41         IRQ can be omitted, although the cmd interface will not work without it.
42
43         All entries in the channel/gain list must use the same gain and be
44         consecutive channels counting upwards in channel number (these are
45         hardware limitations.)
46
47         I've never tested the gain setting stuff since I only have a
48         DAS-800 board with fixed gain.
49
50         The cio-das802/16 does not have a fifo-empty status bit!  Therefore
51         only fifo-half-full transfers are possible with this card.
52 */
53 /*
54
55 cmd triggers supported:
56         start_src:      TRIG_NOW | TRIG_EXT
57         scan_begin_src: TRIG_FOLLOW
58         scan_end_src:   TRIG_COUNT
59         convert_src:    TRIG_TIMER | TRIG_EXT
60         stop_src:       TRIG_NONE | TRIG_COUNT
61
62
63 */
64
65 #include "../comedidev.h"
66
67 #include <linux/ioport.h>
68 #include <linux/delay.h>
69
70 #include "8253.h"
71 #include "comedi_fc.h"
72
73 #define DAS800_SIZE           8
74 #define TIMER_BASE            1000
75 #define N_CHAN_AI             8 // number of analog input channels
76
77 /* Registers for the das800 */
78
79 #define DAS800_LSB            0
80 #define   FIFO_EMPTY            0x1
81 #define   FIFO_OVF              0x2
82 #define DAS800_MSB            1
83 #define DAS800_CONTROL1       2
84 #define   CONTROL1_INTE         0x8
85 #define DAS800_CONV_CONTROL   2
86 #define   ITE                   0x1
87 #define   CASC                  0x2
88 #define   DTEN                  0x4
89 #define   IEOC                  0x8
90 #define   EACS                  0x10
91 #define   CONV_HCEN             0x80
92 #define DAS800_SCAN_LIMITS    2
93 #define DAS800_STATUS         2
94 #define   IRQ                   0x8
95 #define   BUSY                  0x80
96 #define DAS800_GAIN           3
97 #define   CIO_FFOV              0x8     // fifo overflow for cio-das802/16
98 #define   CIO_ENHF              0x90    // interrupt fifo half full for cio-das802/16
99 #define   CONTROL1              0x80
100 #define   CONV_CONTROL          0xa0
101 #define   SCAN_LIMITS           0xc0
102 #define   ID                    0xe0
103 #define DAS800_8254           4
104 #define DAS800_STATUS2        7
105 #define   STATUS2_HCEN          0x80
106 #define   STATUS2_INTE          0X20
107 #define DAS800_ID             7
108
109 typedef struct das800_board_struct {
110         const char *name;
111         int ai_speed;
112         const comedi_lrange *ai_range;
113         int resolution;
114 } das800_board;
115
116 //analog input ranges
117 static const comedi_lrange range_das800_ai = {
118         1,
119         {
120                         RANGE(-5, 5),
121                 }
122 };
123
124 static const comedi_lrange range_das801_ai = {
125         9,
126         {
127                         RANGE(-5, 5),
128                         RANGE(-10, 10),
129                         RANGE(0, 10),
130                         RANGE(-0.5, 0.5),
131                         RANGE(0, 1),
132                         RANGE(-0.05, 0.05),
133                         RANGE(0, 0.1),
134                         RANGE(-0.01, 0.01),
135                         RANGE(0, 0.02),
136                 }
137 };
138
139 static const comedi_lrange range_cio_das801_ai = {
140         9,
141         {
142                         RANGE(-5, 5),
143                         RANGE(-10, 10),
144                         RANGE(0, 10),
145                         RANGE(-0.5, 0.5),
146                         RANGE(0, 1),
147                         RANGE(-0.05, 0.05),
148                         RANGE(0, 0.1),
149                         RANGE(-0.005, 0.005),
150                         RANGE(0, 0.01),
151                 }
152 };
153
154 static const comedi_lrange range_das802_ai = {
155         9,
156         {
157                         RANGE(-5, 5),
158                         RANGE(-10, 10),
159                         RANGE(0, 10),
160                         RANGE(-2.5, 2.5),
161                         RANGE(0, 5),
162                         RANGE(-1.25, 1.25),
163                         RANGE(0, 2.5),
164                         RANGE(-0.625, 0.625),
165                         RANGE(0, 1.25),
166                 }
167 };
168
169 static const comedi_lrange range_das80216_ai = {
170         8,
171         {
172                         RANGE(-10, 10),
173                         RANGE(0, 10),
174                         RANGE(-5, 5),
175                         RANGE(0, 5),
176                         RANGE(-2.5, 2.5),
177                         RANGE(0, 2.5),
178                         RANGE(-1.25, 1.25),
179                         RANGE(0, 1.25),
180                 }
181 };
182
183 enum { das800, ciodas800, das801, ciodas801, das802, ciodas802, ciodas80216 };
184
185 static const das800_board das800_boards[] = {
186         {
187               name:     "das-800",
188               ai_speed:25000,
189               ai_range:&range_das800_ai,
190               resolution:12,
191                 },
192         {
193               name:     "cio-das800",
194               ai_speed:20000,
195               ai_range:&range_das800_ai,
196               resolution:12,
197                 },
198         {
199               name:     "das-801",
200               ai_speed:25000,
201               ai_range:&range_das801_ai,
202               resolution:12,
203                 },
204         {
205               name:     "cio-das801",
206               ai_speed:20000,
207               ai_range:&range_cio_das801_ai,
208               resolution:12,
209                 },
210         {
211               name:     "das-802",
212               ai_speed:25000,
213               ai_range:&range_das802_ai,
214               resolution:12,
215                 },
216         {
217               name:     "cio-das802",
218               ai_speed:20000,
219               ai_range:&range_das802_ai,
220               resolution:12,
221                 },
222         {
223               name:     "cio-das802/16",
224               ai_speed:10000,
225               ai_range:&range_das80216_ai,
226               resolution:16,
227                 },
228 };
229
230 /*
231  * Useful for shorthand access to the particular board structure
232  */
233 #define thisboard ((const das800_board *)dev->board_ptr)
234
235 typedef struct {
236         volatile unsigned int count;    /* number of data points left to be taken */
237         volatile int forever;   /* flag indicating whether we should take data forever */
238         unsigned int divisor1;  /* value to load into board's counter 1 for timed conversions */
239         unsigned int divisor2;  /* value to load into board's counter 2 for timed conversions */
240         volatile int do_bits;   /* digital output bits */
241 } das800_private;
242
243 #define devpriv ((das800_private *)dev->private)
244
245 static int das800_attach(struct comedi_device * dev, comedi_devconfig * it);
246 static int das800_detach(struct comedi_device * dev);
247 static int das800_cancel(struct comedi_device * dev, comedi_subdevice * s);
248
249 static comedi_driver driver_das800 = {
250       driver_name:"das800",
251       module:THIS_MODULE,
252       attach:das800_attach,
253       detach:das800_detach,
254       num_names:sizeof(das800_boards) / sizeof(das800_board),
255       board_name:&das800_boards[0].name,
256       offset:sizeof(das800_board),
257 };
258
259 static irqreturn_t das800_interrupt(int irq, void *d PT_REGS_ARG);
260 static void enable_das800(struct comedi_device * dev);
261 static void disable_das800(struct comedi_device * dev);
262 static int das800_ai_do_cmdtest(struct comedi_device * dev, comedi_subdevice * s,
263         comedi_cmd * cmd);
264 static int das800_ai_do_cmd(struct comedi_device * dev, comedi_subdevice * s);
265 static int das800_ai_rinsn(struct comedi_device * dev, comedi_subdevice * s,
266         comedi_insn * insn, unsigned int * data);
267 static int das800_di_rbits(struct comedi_device * dev, comedi_subdevice * s,
268         comedi_insn * insn, unsigned int * data);
269 static int das800_do_wbits(struct comedi_device * dev, comedi_subdevice * s,
270         comedi_insn * insn, unsigned int * data);
271 static int das800_probe(struct comedi_device * dev);
272 static int das800_set_frequency(struct comedi_device * dev);
273
274 /* checks and probes das-800 series board type */
275 static int das800_probe(struct comedi_device * dev)
276 {
277         int id_bits;
278         unsigned long irq_flags;
279         int board;
280
281         // 'comedi spin lock irqsave' disables even rt interrupts, we use them to protect indirect addressing
282         comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
283         outb(ID, dev->iobase + DAS800_GAIN);    /* select base address + 7 to be ID register */
284         id_bits = inb(dev->iobase + DAS800_ID) & 0x3;   /* get id bits */
285         comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
286
287         board = thisboard - das800_boards;
288
289         switch (id_bits) {
290         case 0x0:
291                 if (board == das800) {
292                         printk(" Board model: DAS-800\n");
293                         return board;
294                 }
295                 if (board == ciodas800) {
296                         printk(" Board model: CIO-DAS800\n");
297                         return board;
298                 }
299                 printk(" Board model (probed): DAS-800\n");
300                 return das800;
301                 break;
302         case 0x2:
303                 if (board == das801) {
304                         printk(" Board model: DAS-801\n");
305                         return board;
306                 }
307                 if (board == ciodas801) {
308                         printk(" Board model: CIO-DAS801\n");
309                         return board;
310                 }
311                 printk(" Board model (probed): DAS-801\n");
312                 return das801;
313                 break;
314         case 0x3:
315                 if (board == das802) {
316                         printk(" Board model: DAS-802\n");
317                         return board;
318                 }
319                 if (board == ciodas802) {
320                         printk(" Board model: CIO-DAS802\n");
321                         return board;
322                 }
323                 if (board == ciodas80216) {
324                         printk(" Board model: CIO-DAS802/16\n");
325                         return board;
326                 }
327                 printk(" Board model (probed): DAS-802\n");
328                 return das802;
329                 break;
330         default:
331                 printk(" Board model: probe returned 0x%x (unknown)\n",
332                         id_bits);
333                 return board;
334                 break;
335         }
336         return -1;
337 }
338
339 /*
340  * A convenient macro that defines init_module() and cleanup_module(),
341  * as necessary.
342  */
343 COMEDI_INITCLEANUP(driver_das800);
344
345 /* interrupt service routine */
346 static irqreturn_t das800_interrupt(int irq, void *d PT_REGS_ARG)
347 {
348         short i;                /* loop index */
349         short dataPoint = 0;
350         struct comedi_device *dev = d;
351         comedi_subdevice *s = dev->read_subdev; /* analog input subdevice */
352         comedi_async *async;
353         int status;
354         unsigned long irq_flags;
355         static const int max_loops = 128;       // half-fifo size for cio-das802/16
356         // flags
357         int fifo_empty = 0;
358         int fifo_overflow = 0;
359
360         status = inb(dev->iobase + DAS800_STATUS);
361         /* if interrupt was not generated by board or driver not attached, quit */
362         if (!(status & IRQ))
363                 return IRQ_NONE;
364         if (!(dev->attached))
365                 return IRQ_HANDLED;
366
367         /* wait until here to initialize async, since we will get null dereference
368          * if interrupt occurs before driver is fully attached!
369          */
370         async = s->async;
371
372         // if hardware conversions are not enabled, then quit
373         comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
374         outb(CONTROL1, dev->iobase + DAS800_GAIN);      /* select base address + 7 to be STATUS2 register */
375         status = inb(dev->iobase + DAS800_STATUS2) & STATUS2_HCEN;
376         /* don't release spinlock yet since we want to make sure noone else disables hardware conversions */
377         if (status == 0) {
378                 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
379                 return IRQ_HANDLED;
380         }
381
382         /* loop while card's fifo is not empty (and limit to half fifo for cio-das802/16) */
383         for (i = 0; i < max_loops; i++) {
384                 /* read 16 bits from dev->iobase and dev->iobase + 1 */
385                 dataPoint = inb(dev->iobase + DAS800_LSB);
386                 dataPoint += inb(dev->iobase + DAS800_MSB) << 8;
387                 if (thisboard->resolution == 12) {
388                         fifo_empty = dataPoint & FIFO_EMPTY;
389                         fifo_overflow = dataPoint & FIFO_OVF;
390                         if (fifo_overflow)
391                                 break;
392                 } else {
393                         fifo_empty = 0; // cio-das802/16 has no fifo empty status bit
394                 }
395                 if (fifo_empty) {
396                         break;
397                 }
398                 /* strip off extraneous bits for 12 bit cards */
399                 if (thisboard->resolution == 12)
400                         dataPoint = (dataPoint >> 4) & 0xfff;
401                 /* if there are more data points to collect */
402                 if (devpriv->count > 0 || devpriv->forever == 1) {
403                         /* write data point to buffer */
404                         cfc_write_to_buffer(s, dataPoint);
405                         if (devpriv->count > 0)
406                                 devpriv->count--;
407                 }
408         }
409         async->events |= COMEDI_CB_BLOCK;
410         /* check for fifo overflow */
411         if (thisboard->resolution == 12) {
412                 fifo_overflow = dataPoint & FIFO_OVF;
413                 // else cio-das802/16
414         } else {
415                 fifo_overflow = inb(dev->iobase + DAS800_GAIN) & CIO_FFOV;
416         }
417         if (fifo_overflow) {
418                 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
419                 comedi_error(dev, "DAS800 FIFO overflow");
420                 das800_cancel(dev, dev->subdevices + 0);
421                 async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
422                 comedi_event(dev, s);
423                 async->events = 0;
424                 return IRQ_HANDLED;
425         }
426         if (devpriv->count > 0 || devpriv->forever == 1) {
427                 /* Re-enable card's interrupt.
428                  * We already have spinlock, so indirect addressing is safe */
429                 outb(CONTROL1, dev->iobase + DAS800_GAIN);      /* select dev->iobase + 2 to be control register 1 */
430                 outb(CONTROL1_INTE | devpriv->do_bits,
431                         dev->iobase + DAS800_CONTROL1);
432                 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
433                 /* otherwise, stop taking data */
434         } else {
435                 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
436                 disable_das800(dev);    /* diable hardware triggered conversions */
437                 async->events |= COMEDI_CB_EOA;
438         }
439         comedi_event(dev, s);
440         async->events = 0;
441         return IRQ_HANDLED;
442 }
443
444 static int das800_attach(struct comedi_device * dev, comedi_devconfig * it)
445 {
446         comedi_subdevice *s;
447         unsigned long iobase = it->options[0];
448         unsigned int irq = it->options[1];
449         unsigned long irq_flags;
450         int board;
451
452         printk("comedi%d: das800: io 0x%lx", dev->minor, iobase);
453         if (irq) {
454                 printk(", irq %u", irq);
455         }
456         printk("\n");
457
458         /* allocate and initialize dev->private */
459         if (alloc_private(dev, sizeof(das800_private)) < 0)
460                 return -ENOMEM;
461
462         if (iobase == 0) {
463                 printk("io base address required for das800\n");
464                 return -EINVAL;
465         }
466
467         /* check if io addresses are available */
468         if (!request_region(iobase, DAS800_SIZE, "das800")) {
469                 printk("I/O port conflict\n");
470                 return -EIO;
471         }
472         dev->iobase = iobase;
473
474         board = das800_probe(dev);
475         if (board < 0) {
476                 printk("unable to determine board type\n");
477                 return -ENODEV;
478         }
479         dev->board_ptr = das800_boards + board;
480
481         /* grab our IRQ */
482         if (irq == 1 || irq > 7) {
483                 printk("irq out of range\n");
484                 return -EINVAL;
485         }
486         if (irq) {
487                 if (comedi_request_irq(irq, das800_interrupt, 0, "das800", dev)) {
488                         printk("unable to allocate irq %u\n", irq);
489                         return -EINVAL;
490                 }
491         }
492         dev->irq = irq;
493
494         dev->board_name = thisboard->name;
495
496         if (alloc_subdevices(dev, 3) < 0)
497                 return -ENOMEM;
498
499         /* analog input subdevice */
500         s = dev->subdevices + 0;
501         dev->read_subdev = s;
502         s->type = COMEDI_SUBD_AI;
503         s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
504         s->n_chan = 8;
505         s->len_chanlist = 8;
506         s->maxdata = (1 << thisboard->resolution) - 1;
507         s->range_table = thisboard->ai_range;
508         s->do_cmd = das800_ai_do_cmd;
509         s->do_cmdtest = das800_ai_do_cmdtest;
510         s->insn_read = das800_ai_rinsn;
511         s->cancel = das800_cancel;
512
513         /* di */
514         s = dev->subdevices + 1;
515         s->type = COMEDI_SUBD_DI;
516         s->subdev_flags = SDF_READABLE;
517         s->n_chan = 3;
518         s->maxdata = 1;
519         s->range_table = &range_digital;
520         s->insn_bits = das800_di_rbits;
521
522         /* do */
523         s = dev->subdevices + 2;
524         s->type = COMEDI_SUBD_DO;
525         s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
526         s->n_chan = 4;
527         s->maxdata = 1;
528         s->range_table = &range_digital;
529         s->insn_bits = das800_do_wbits;
530
531         disable_das800(dev);
532
533         /* initialize digital out channels */
534         comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
535         outb(CONTROL1, dev->iobase + DAS800_GAIN);      /* select dev->iobase + 2 to be control register 1 */
536         outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
537         comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
538
539         return 0;
540 };
541
542 static int das800_detach(struct comedi_device * dev)
543 {
544         printk("comedi%d: das800: remove\n", dev->minor);
545
546         /* only free stuff if it has been allocated by _attach */
547         if (dev->iobase)
548                 release_region(dev->iobase, DAS800_SIZE);
549         if (dev->irq)
550                 comedi_free_irq(dev->irq, dev);
551         return 0;
552 };
553
554 static int das800_cancel(struct comedi_device * dev, comedi_subdevice * s)
555 {
556         devpriv->forever = 0;
557         devpriv->count = 0;
558         disable_das800(dev);
559         return 0;
560 }
561
562 /* enable_das800 makes the card start taking hardware triggered conversions */
563 static void enable_das800(struct comedi_device * dev)
564 {
565         unsigned long irq_flags;
566         comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
567         // enable fifo-half full interrupts for cio-das802/16
568         if (thisboard->resolution == 16)
569                 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
570         outb(CONV_CONTROL, dev->iobase + DAS800_GAIN);  /* select dev->iobase + 2 to be conversion control register */
571         outb(CONV_HCEN, dev->iobase + DAS800_CONV_CONTROL);     /* enable hardware triggering */
572         outb(CONTROL1, dev->iobase + DAS800_GAIN);      /* select dev->iobase + 2 to be control register 1 */
573         outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);  /* enable card's interrupt */
574         comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
575 }
576
577 /* disable_das800 stops hardware triggered conversions */
578 static void disable_das800(struct comedi_device * dev)
579 {
580         unsigned long irq_flags;
581         comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
582         outb(CONV_CONTROL, dev->iobase + DAS800_GAIN);  /* select dev->iobase + 2 to be conversion control register */
583         outb(0x0, dev->iobase + DAS800_CONV_CONTROL);   /* disable hardware triggering of conversions */
584         comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
585 }
586
587 static int das800_ai_do_cmdtest(struct comedi_device * dev, comedi_subdevice * s,
588         comedi_cmd * cmd)
589 {
590         int err = 0;
591         int tmp;
592         int gain, startChan;
593         int i;
594
595         /* step 1: make sure trigger sources are trivially valid */
596
597         tmp = cmd->start_src;
598         cmd->start_src &= TRIG_NOW | TRIG_EXT;
599         if (!cmd->start_src || tmp != cmd->start_src)
600                 err++;
601
602         tmp = cmd->scan_begin_src;
603         cmd->scan_begin_src &= TRIG_FOLLOW;
604         if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
605                 err++;
606
607         tmp = cmd->convert_src;
608         cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
609         if (!cmd->convert_src || tmp != cmd->convert_src)
610                 err++;
611
612         tmp = cmd->scan_end_src;
613         cmd->scan_end_src &= TRIG_COUNT;
614         if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
615                 err++;
616
617         tmp = cmd->stop_src;
618         cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
619         if (!cmd->stop_src || tmp != cmd->stop_src)
620                 err++;
621
622         if (err)
623                 return 1;
624
625         /* step 2: make sure trigger sources are unique and mutually compatible */
626
627         if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
628                 err++;
629         if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
630                 err++;
631         if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
632                 err++;
633
634         if (err)
635                 return 2;
636
637         /* step 3: make sure arguments are trivially compatible */
638
639         if (cmd->start_arg != 0) {
640                 cmd->start_arg = 0;
641                 err++;
642         }
643         if (cmd->convert_src == TRIG_TIMER) {
644                 if (cmd->convert_arg < thisboard->ai_speed) {
645                         cmd->convert_arg = thisboard->ai_speed;
646                         err++;
647                 }
648         }
649         if (!cmd->chanlist_len) {
650                 cmd->chanlist_len = 1;
651                 err++;
652         }
653         if (cmd->scan_end_arg != cmd->chanlist_len) {
654                 cmd->scan_end_arg = cmd->chanlist_len;
655                 err++;
656         }
657         if (cmd->stop_src == TRIG_COUNT) {
658                 if (!cmd->stop_arg) {
659                         cmd->stop_arg = 1;
660                         err++;
661                 }
662         } else {                /* TRIG_NONE */
663                 if (cmd->stop_arg != 0) {
664                         cmd->stop_arg = 0;
665                         err++;
666                 }
667         }
668
669         if (err)
670                 return 3;
671
672         /* step 4: fix up any arguments */
673
674         if (cmd->convert_src == TRIG_TIMER) {
675                 tmp = cmd->convert_arg;
676                 /* calculate counter values that give desired timing */
677                 i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
678                         &(devpriv->divisor2), &(cmd->convert_arg),
679                         cmd->flags & TRIG_ROUND_MASK);
680                 if (tmp != cmd->convert_arg)
681                         err++;
682         }
683
684         if (err)
685                 return 4;
686
687         // check channel/gain list against card's limitations
688         if (cmd->chanlist) {
689                 gain = CR_RANGE(cmd->chanlist[0]);
690                 startChan = CR_CHAN(cmd->chanlist[0]);
691                 for (i = 1; i < cmd->chanlist_len; i++) {
692                         if (CR_CHAN(cmd->chanlist[i]) !=
693                                 (startChan + i) % N_CHAN_AI) {
694                                 comedi_error(dev,
695                                         "entries in chanlist must be consecutive channels, counting upwards\n");
696                                 err++;
697                         }
698                         if (CR_RANGE(cmd->chanlist[i]) != gain) {
699                                 comedi_error(dev,
700                                         "entries in chanlist must all have the same gain\n");
701                                 err++;
702                         }
703                 }
704         }
705
706         if (err)
707                 return 5;
708
709         return 0;
710 }
711
712 static int das800_ai_do_cmd(struct comedi_device * dev, comedi_subdevice * s)
713 {
714         int startChan, endChan, scan, gain;
715         int conv_bits;
716         unsigned long irq_flags;
717         comedi_async *async = s->async;
718
719         if (!dev->irq) {
720                 comedi_error(dev,
721                         "no irq assigned for das-800, cannot do hardware conversions");
722                 return -1;
723         }
724
725         disable_das800(dev);
726
727         /* set channel scan limits */
728         startChan = CR_CHAN(async->cmd.chanlist[0]);
729         endChan = (startChan + async->cmd.chanlist_len - 1) % 8;
730         scan = (endChan << 3) | startChan;
731
732         comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
733         outb(SCAN_LIMITS, dev->iobase + DAS800_GAIN);   /* select base address + 2 to be scan limits register */
734         outb(scan, dev->iobase + DAS800_SCAN_LIMITS);   /* set scan limits */
735         comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
736
737         /* set gain */
738         gain = CR_RANGE(async->cmd.chanlist[0]);
739         if (thisboard->resolution == 12 && gain > 0)
740                 gain += 0x7;
741         gain &= 0xf;
742         outb(gain, dev->iobase + DAS800_GAIN);
743
744         switch (async->cmd.stop_src) {
745         case TRIG_COUNT:
746                 devpriv->count = async->cmd.stop_arg * async->cmd.chanlist_len;
747                 devpriv->forever = 0;
748                 break;
749         case TRIG_NONE:
750                 devpriv->forever = 1;
751                 devpriv->count = 0;
752                 break;
753         default:
754                 break;
755         }
756
757         /* enable auto channel scan, send interrupts on end of conversion
758          * and set clock source to internal or external
759          */
760         conv_bits = 0;
761         conv_bits |= EACS | IEOC;
762         if (async->cmd.start_src == TRIG_EXT)
763                 conv_bits |= DTEN;
764         switch (async->cmd.convert_src) {
765         case TRIG_TIMER:
766                 conv_bits |= CASC | ITE;
767                 /* set conversion frequency */
768                 i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
769                         &(devpriv->divisor2), &(async->cmd.convert_arg),
770                         async->cmd.flags & TRIG_ROUND_MASK);
771                 if (das800_set_frequency(dev) < 0) {
772                         comedi_error(dev, "Error setting up counters");
773                         return -1;
774                 }
775                 break;
776         case TRIG_EXT:
777                 break;
778         default:
779                 break;
780         }
781
782         comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
783         outb(CONV_CONTROL, dev->iobase + DAS800_GAIN);  /* select dev->iobase + 2 to be conversion control register */
784         outb(conv_bits, dev->iobase + DAS800_CONV_CONTROL);
785         comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
786         async->events = 0;
787         enable_das800(dev);
788         return 0;
789 }
790
791 static int das800_ai_rinsn(struct comedi_device * dev, comedi_subdevice * s,
792         comedi_insn * insn, unsigned int * data)
793 {
794         int i, n;
795         int chan;
796         int range;
797         int lsb, msb;
798         int timeout = 1000;
799         unsigned long irq_flags;
800
801         disable_das800(dev);    /* disable hardware conversions (enables software conversions) */
802
803         /* set multiplexer */
804         chan = CR_CHAN(insn->chanspec);
805
806         comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
807         outb(CONTROL1, dev->iobase + DAS800_GAIN);      /* select dev->iobase + 2 to be control register 1 */
808         outb(chan | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
809         comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
810
811         /* set gain / range */
812         range = CR_RANGE(insn->chanspec);
813         if (thisboard->resolution == 12 && range)
814                 range += 0x7;
815         range &= 0xf;
816         outb(range, dev->iobase + DAS800_GAIN);
817
818         comedi_udelay(5);
819
820         for (n = 0; n < insn->n; n++) {
821                 /* trigger conversion */
822                 outb_p(0, dev->iobase + DAS800_MSB);
823
824                 for (i = 0; i < timeout; i++) {
825                         if (!(inb(dev->iobase + DAS800_STATUS) & BUSY))
826                                 break;
827                 }
828                 if (i == timeout) {
829                         comedi_error(dev, "timeout");
830                         return -ETIME;
831                 }
832                 lsb = inb(dev->iobase + DAS800_LSB);
833                 msb = inb(dev->iobase + DAS800_MSB);
834                 if (thisboard->resolution == 12) {
835                         data[n] = (lsb >> 4) & 0xff;
836                         data[n] |= (msb << 4);
837                 } else {
838                         data[n] = (msb << 8) | lsb;
839                 }
840         }
841
842         return n;
843 }
844
845 static int das800_di_rbits(struct comedi_device * dev, comedi_subdevice * s,
846         comedi_insn * insn, unsigned int * data)
847 {
848         unsigned int bits;
849
850         bits = inb(dev->iobase + DAS800_STATUS) >> 4;
851         bits &= 0x7;
852         data[1] = bits;
853         data[0] = 0;
854
855         return 2;
856 }
857
858 static int das800_do_wbits(struct comedi_device * dev, comedi_subdevice * s,
859         comedi_insn * insn, unsigned int * data)
860 {
861         int wbits;
862         unsigned long irq_flags;
863
864         // only set bits that have been masked
865         data[0] &= 0xf;
866         wbits = devpriv->do_bits >> 4;
867         wbits &= ~data[0];
868         wbits |= data[0] & data[1];
869         devpriv->do_bits = wbits << 4;
870
871         comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
872         outb(CONTROL1, dev->iobase + DAS800_GAIN);      /* select dev->iobase + 2 to be control register 1 */
873         outb(devpriv->do_bits | CONTROL1_INTE, dev->iobase + DAS800_CONTROL1);
874         comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
875
876         data[1] = wbits;
877
878         return 2;
879 }
880
881 /* loads counters with divisor1, divisor2 from private structure */
882 static int das800_set_frequency(struct comedi_device * dev)
883 {
884         int err = 0;
885
886         if (i8254_load(dev->iobase + DAS800_8254, 0, 1, devpriv->divisor1, 2))
887                 err++;
888         if (i8254_load(dev->iobase + DAS800_8254, 0, 2, devpriv->divisor2, 2))
889                 err++;
890         if (err)
891                 return -1;
892
893         return 0;
894 }