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