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