dt282x.c revision 845d131e2b363717d8ac8db2c6b4417de8cf10b5
1/*
2   comedi/drivers/dt282x.c
3   Hardware driver for Data Translation DT2821 series
4
5   COMEDI - Linux Control and Measurement Device Interface
6   Copyright (C) 1997-8 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/*
24Driver: dt282x
25Description: Data Translation DT2821 series (including DT-EZ)
26Author: ds
27Devices: [Data Translation] DT2821 (dt2821),
28  DT2821-F-16SE (dt2821-f), DT2821-F-8DI (dt2821-f),
29  DT2821-G-16SE (dt2821-f), DT2821-G-8DI (dt2821-g),
30  DT2823 (dt2823),
31  DT2824-PGH (dt2824-pgh), DT2824-PGL (dt2824-pgl), DT2825 (dt2825),
32  DT2827 (dt2827), DT2828 (dt2828), DT21-EZ (dt21-ez), DT23-EZ (dt23-ez),
33  DT24-EZ (dt24-ez), DT24-EZ-PGL (dt24-ez-pgl)
34Status: complete
35Updated: Wed, 22 Aug 2001 17:11:34 -0700
36
37Configuration options:
38  [0] - I/O port base address
39  [1] - IRQ
40  [2] - DMA 1
41  [3] - DMA 2
42  [4] - AI jumpered for 0=single ended, 1=differential
43  [5] - AI jumpered for 0=straight binary, 1=2's complement
44  [6] - AO 0 jumpered for 0=straight binary, 1=2's complement
45  [7] - AO 1 jumpered for 0=straight binary, 1=2's complement
46  [8] - AI jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5]
47  [9] - AO 0 jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5],
48	4=[-2.5,2.5]
49  [10]- A0 1 jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5],
50	4=[-2.5,2.5]
51
52Notes:
53  - AO commands might be broken.
54  - If you try to run a command on both the AI and AO subdevices
55    simultaneously, bad things will happen.  The driver needs to
56    be fixed to check for this situation and return an error.
57*/
58
59#include "../comedidev.h"
60
61#include <linux/gfp.h>
62#include <linux/ioport.h>
63#include <linux/interrupt.h>
64#include <linux/io.h>
65#include <asm/dma.h>
66#include "comedi_fc.h"
67
68#define DEBUG
69
70#define DT2821_TIMEOUT		100	/* 500 us */
71#define DT2821_SIZE 0x10
72
73/*
74 *    Registers in the DT282x
75 */
76
77#define DT2821_ADCSR	0x00	/* A/D Control/Status             */
78#define DT2821_CHANCSR	0x02	/* Channel Control/Status */
79#define DT2821_ADDAT	0x04	/* A/D data                       */
80#define DT2821_DACSR	0x06	/* D/A Control/Status             */
81#define DT2821_DADAT	0x08	/* D/A data                       */
82#define DT2821_DIODAT	0x0a	/* digital data                   */
83#define DT2821_SUPCSR	0x0c	/* Supervisor Control/Status      */
84#define DT2821_TMRCTR	0x0e	/* Timer/Counter          */
85
86/*
87 *  At power up, some registers are in a well-known state.  The
88 *  masks and values are as follows:
89 */
90
91#define DT2821_ADCSR_MASK 0xfff0
92#define DT2821_ADCSR_VAL 0x7c00
93
94#define DT2821_CHANCSR_MASK 0xf0f0
95#define DT2821_CHANCSR_VAL 0x70f0
96
97#define DT2821_DACSR_MASK 0x7c93
98#define DT2821_DACSR_VAL 0x7c90
99
100#define DT2821_SUPCSR_MASK 0xf8ff
101#define DT2821_SUPCSR_VAL 0x0000
102
103#define DT2821_TMRCTR_MASK 0xff00
104#define DT2821_TMRCTR_VAL 0xf000
105
106/*
107 *    Bit fields of each register
108 */
109
110/* ADCSR */
111
112#define DT2821_ADERR	0x8000	/* (R)   1 for A/D error  */
113#define DT2821_ADCLK	0x0200	/* (R/W) A/D clock enable */
114		/*      0x7c00           read as 1's            */
115#define DT2821_MUXBUSY	0x0100	/* (R)   multiplexer busy */
116#define DT2821_ADDONE	0x0080	/* (R)   A/D done         */
117#define DT2821_IADDONE	0x0040	/* (R/W) interrupt on A/D done    */
118		/*      0x0030           gain select            */
119		/*      0x000f           channel select         */
120
121/* CHANCSR */
122
123#define DT2821_LLE	0x8000	/* (R/W) Load List Enable */
124		/*      0x7000           read as 1's            */
125		/*      0x0f00     (R)   present address        */
126		/*      0x00f0           read as 1's            */
127		/*      0x000f     (R)   number of entries - 1  */
128
129/* DACSR */
130
131#define DT2821_DAERR	0x8000	/* (R)   D/A error                */
132#define DT2821_YSEL	0x0200	/* (R/W) DAC 1 select             */
133#define DT2821_SSEL	0x0100	/* (R/W) single channel select    */
134#define DT2821_DACRDY	0x0080	/* (R)   DAC ready                */
135#define DT2821_IDARDY	0x0040	/* (R/W) interrupt on DAC ready   */
136#define DT2821_DACLK	0x0020	/* (R/W) D/A clock enable */
137#define DT2821_HBOE	0x0002	/* (R/W) DIO high byte output enable      */
138#define DT2821_LBOE	0x0001	/* (R/W) DIO low byte output enable       */
139
140/* SUPCSR */
141
142#define DT2821_DMAD	0x8000	/* (R)   DMA done                 */
143#define DT2821_ERRINTEN	0x4000	/* (R/W) interrupt on error               */
144#define DT2821_CLRDMADNE 0x2000	/* (W)   clear DMA done                   */
145#define DT2821_DDMA	0x1000	/* (R/W) dual DMA                 */
146#define DT2821_DS1	0x0800	/* (R/W) DMA select 1                     */
147#define DT2821_DS0	0x0400	/* (R/W) DMA select 0                     */
148#define DT2821_BUFFB	0x0200	/* (R/W) buffer B selected                */
149#define DT2821_SCDN	0x0100	/* (R)   scan done                        */
150#define DT2821_DACON	0x0080	/* (W)   DAC single conversion            */
151#define DT2821_ADCINIT	0x0040	/* (W)   A/D initialize                   */
152#define DT2821_DACINIT	0x0020	/* (W)   D/A initialize                   */
153#define DT2821_PRLD	0x0010	/* (W)   preload multiplexer              */
154#define DT2821_STRIG	0x0008	/* (W)   software trigger         */
155#define DT2821_XTRIG	0x0004	/* (R/W) external trigger enable  */
156#define DT2821_XCLK	0x0002	/* (R/W) external clock enable            */
157#define DT2821_BDINIT	0x0001	/* (W)   initialize board         */
158
159static const struct comedi_lrange range_dt282x_ai_lo_bipolar = {
160	4, {
161		RANGE(-10, 10),
162		RANGE(-5, 5),
163		RANGE(-2.5, 2.5),
164		RANGE(-1.25, 1.25)
165	}
166};
167
168static const struct comedi_lrange range_dt282x_ai_lo_unipolar = {
169	4, {
170		RANGE(0, 10),
171		RANGE(0, 5),
172		RANGE(0, 2.5),
173		RANGE(0, 1.25)
174	}
175};
176
177static const struct comedi_lrange range_dt282x_ai_5_bipolar = {
178	4, {
179		RANGE(-5, 5),
180		RANGE(-2.5, 2.5),
181		RANGE(-1.25, 1.25),
182		RANGE(-0.625, 0.625)
183	}
184};
185
186static const struct comedi_lrange range_dt282x_ai_5_unipolar = {
187	4, {
188		RANGE(0, 5),
189		RANGE(0, 2.5),
190		RANGE(0, 1.25),
191		RANGE(0, 0.625),
192	}
193};
194
195static const struct comedi_lrange range_dt282x_ai_hi_bipolar = {
196	4, {
197		RANGE(-10, 10),
198		RANGE(-1, 1),
199		RANGE(-0.1, 0.1),
200		RANGE(-0.02, 0.02)
201	}
202};
203
204static const struct comedi_lrange range_dt282x_ai_hi_unipolar = {
205	4, {
206		RANGE(0, 10),
207		RANGE(0, 1),
208		RANGE(0, 0.1),
209		RANGE(0, 0.02)
210	}
211};
212
213struct dt282x_board {
214	const char *name;
215	int adbits;
216	int adchan_se;
217	int adchan_di;
218	int ai_speed;
219	int ispgl;
220	int dachan;
221	int dabits;
222};
223
224static const struct dt282x_board boardtypes[] = {
225	{.name = "dt2821",
226	 .adbits = 12,
227	 .adchan_se = 16,
228	 .adchan_di = 8,
229	 .ai_speed = 20000,
230	 .ispgl = 0,
231	 .dachan = 2,
232	 .dabits = 12,
233	 },
234	{.name = "dt2821-f",
235	 .adbits = 12,
236	 .adchan_se = 16,
237	 .adchan_di = 8,
238	 .ai_speed = 6500,
239	 .ispgl = 0,
240	 .dachan = 2,
241	 .dabits = 12,
242	 },
243	{.name = "dt2821-g",
244	 .adbits = 12,
245	 .adchan_se = 16,
246	 .adchan_di = 8,
247	 .ai_speed = 4000,
248	 .ispgl = 0,
249	 .dachan = 2,
250	 .dabits = 12,
251	 },
252	{.name = "dt2823",
253	 .adbits = 16,
254	 .adchan_se = 0,
255	 .adchan_di = 4,
256	 .ai_speed = 10000,
257	 .ispgl = 0,
258	 .dachan = 2,
259	 .dabits = 16,
260	 },
261	{.name = "dt2824-pgh",
262	 .adbits = 12,
263	 .adchan_se = 16,
264	 .adchan_di = 8,
265	 .ai_speed = 20000,
266	 .ispgl = 0,
267	 .dachan = 0,
268	 .dabits = 0,
269	 },
270	{.name = "dt2824-pgl",
271	 .adbits = 12,
272	 .adchan_se = 16,
273	 .adchan_di = 8,
274	 .ai_speed = 20000,
275	 .ispgl = 1,
276	 .dachan = 0,
277	 .dabits = 0,
278	 },
279	{.name = "dt2825",
280	 .adbits = 12,
281	 .adchan_se = 16,
282	 .adchan_di = 8,
283	 .ai_speed = 20000,
284	 .ispgl = 1,
285	 .dachan = 2,
286	 .dabits = 12,
287	 },
288	{.name = "dt2827",
289	 .adbits = 16,
290	 .adchan_se = 0,
291	 .adchan_di = 4,
292	 .ai_speed = 10000,
293	 .ispgl = 0,
294	 .dachan = 2,
295	 .dabits = 12,
296	 },
297	{.name = "dt2828",
298	 .adbits = 12,
299	 .adchan_se = 4,
300	 .adchan_di = 0,
301	 .ai_speed = 10000,
302	 .ispgl = 0,
303	 .dachan = 2,
304	 .dabits = 12,
305	 },
306	{.name = "dt2829",
307	 .adbits = 16,
308	 .adchan_se = 8,
309	 .adchan_di = 0,
310	 .ai_speed = 33250,
311	 .ispgl = 0,
312	 .dachan = 2,
313	 .dabits = 16,
314	 },
315	{.name = "dt21-ez",
316	 .adbits = 12,
317	 .adchan_se = 16,
318	 .adchan_di = 8,
319	 .ai_speed = 10000,
320	 .ispgl = 0,
321	 .dachan = 2,
322	 .dabits = 12,
323	 },
324	{.name = "dt23-ez",
325	 .adbits = 16,
326	 .adchan_se = 16,
327	 .adchan_di = 8,
328	 .ai_speed = 10000,
329	 .ispgl = 0,
330	 .dachan = 0,
331	 .dabits = 0,
332	 },
333	{.name = "dt24-ez",
334	 .adbits = 12,
335	 .adchan_se = 16,
336	 .adchan_di = 8,
337	 .ai_speed = 10000,
338	 .ispgl = 0,
339	 .dachan = 0,
340	 .dabits = 0,
341	 },
342	{.name = "dt24-ez-pgl",
343	 .adbits = 12,
344	 .adchan_se = 16,
345	 .adchan_di = 8,
346	 .ai_speed = 10000,
347	 .ispgl = 1,
348	 .dachan = 0,
349	 .dabits = 0,
350	 },
351};
352
353#define n_boardtypes (sizeof(boardtypes)/sizeof(struct dt282x_board))
354#define this_board ((const struct dt282x_board *)dev->board_ptr)
355
356struct dt282x_private {
357	int ad_2scomp;		/* we have 2's comp jumper set  */
358	int da0_2scomp;		/* same, for DAC0               */
359	int da1_2scomp;		/* same, for DAC1               */
360
361	const struct comedi_lrange *darangelist[2];
362
363	short ao[2];
364
365	volatile int dacsr;	/* software copies of registers */
366	volatile int adcsr;
367	volatile int supcsr;
368
369	volatile int ntrig;
370	volatile int nread;
371
372	struct {
373		int chan;
374		short *buf;	/* DMA buffer */
375		volatile int size;	/* size of current transfer */
376	} dma[2];
377	int dma_maxsize;	/* max size of DMA transfer (in bytes) */
378	int usedma;		/* driver uses DMA              */
379	volatile int current_dma_index;
380	int dma_dir;
381};
382
383#define devpriv ((struct dt282x_private *)dev->private)
384#define boardtype (*(const struct dt282x_board *)dev->board_ptr)
385
386/*
387 *    Some useless abstractions
388 */
389#define chan_to_DAC(a)	((a)&1)
390#define update_dacsr(a)	outw(devpriv->dacsr|(a), dev->iobase+DT2821_DACSR)
391#define update_adcsr(a)	outw(devpriv->adcsr|(a), dev->iobase+DT2821_ADCSR)
392#define mux_busy() (inw(dev->iobase+DT2821_ADCSR)&DT2821_MUXBUSY)
393#define ad_done() (inw(dev->iobase+DT2821_ADCSR)&DT2821_ADDONE)
394#define update_supcsr(a) outw(devpriv->supcsr|(a), dev->iobase+DT2821_SUPCSR)
395
396/*
397 *    danger! macro abuse... a is the expression to wait on, and b is
398 *      the statement(s) to execute if it doesn't happen.
399 */
400#define wait_for(a, b)						\
401	do {							\
402		int _i;						\
403		for (_i = 0; _i < DT2821_TIMEOUT; _i++) {	\
404			if (a) {				\
405				_i = 0;				\
406				break;				\
407			}					\
408			udelay(5);				\
409		}						\
410		if (_i)						\
411			b					\
412	} while (0)
413
414static int dt282x_attach(struct comedi_device *dev,
415			 struct comedi_devconfig *it);
416static int dt282x_detach(struct comedi_device *dev);
417static struct comedi_driver driver_dt282x = {
418	.driver_name = "dt282x",
419	.module = THIS_MODULE,
420	.attach = dt282x_attach,
421	.detach = dt282x_detach,
422	.board_name = &boardtypes[0].name,
423	.num_names = n_boardtypes,
424	.offset = sizeof(struct dt282x_board),
425};
426
427static int __init driver_dt282x_init_module(void)
428{
429	return comedi_driver_register(&driver_dt282x);
430}
431
432static void __exit driver_dt282x_cleanup_module(void)
433{
434	comedi_driver_unregister(&driver_dt282x);
435}
436
437module_init(driver_dt282x_init_module);
438module_exit(driver_dt282x_cleanup_module);
439
440static void free_resources(struct comedi_device *dev);
441static int prep_ai_dma(struct comedi_device *dev, int chan, int size);
442static int prep_ao_dma(struct comedi_device *dev, int chan, int size);
443static int dt282x_ai_cancel(struct comedi_device *dev,
444			    struct comedi_subdevice *s);
445static int dt282x_ao_cancel(struct comedi_device *dev,
446			    struct comedi_subdevice *s);
447static int dt282x_ns_to_timer(int *nanosec, int round_mode);
448static void dt282x_disable_dma(struct comedi_device *dev);
449
450static int dt282x_grab_dma(struct comedi_device *dev, int dma1, int dma2);
451
452static void dt282x_munge(struct comedi_device *dev, short *buf,
453			 unsigned int nbytes)
454{
455	unsigned int i;
456	unsigned short mask = (1 << boardtype.adbits) - 1;
457	unsigned short sign = 1 << (boardtype.adbits - 1);
458	int n;
459
460	if (devpriv->ad_2scomp)
461		sign = 1 << (boardtype.adbits - 1);
462	else
463		sign = 0;
464
465	if (nbytes % 2)
466		comedi_error(dev, "bug! odd number of bytes from dma xfer");
467	n = nbytes / 2;
468	for (i = 0; i < n; i++)
469		buf[i] = (buf[i] & mask) ^ sign;
470}
471
472static void dt282x_ao_dma_interrupt(struct comedi_device *dev)
473{
474	void *ptr;
475	int size;
476	int i;
477	struct comedi_subdevice *s = dev->subdevices + 1;
478
479	update_supcsr(DT2821_CLRDMADNE);
480
481	if (!s->async->prealloc_buf) {
482		printk(KERN_ERR "async->data disappeared.  dang!\n");
483		return;
484	}
485
486	i = devpriv->current_dma_index;
487	ptr = devpriv->dma[i].buf;
488
489	disable_dma(devpriv->dma[i].chan);
490
491	devpriv->current_dma_index = 1 - i;
492
493	size = cfc_read_array_from_buffer(s, ptr, devpriv->dma_maxsize);
494	if (size == 0) {
495		printk(KERN_ERR "dt282x: AO underrun\n");
496		dt282x_ao_cancel(dev, s);
497		s->async->events |= COMEDI_CB_OVERFLOW;
498		return;
499	}
500	prep_ao_dma(dev, i, size);
501	return;
502}
503
504static void dt282x_ai_dma_interrupt(struct comedi_device *dev)
505{
506	void *ptr;
507	int size;
508	int i;
509	int ret;
510	struct comedi_subdevice *s = dev->subdevices;
511
512	update_supcsr(DT2821_CLRDMADNE);
513
514	if (!s->async->prealloc_buf) {
515		printk(KERN_ERR "async->data disappeared.  dang!\n");
516		return;
517	}
518
519	i = devpriv->current_dma_index;
520	ptr = devpriv->dma[i].buf;
521	size = devpriv->dma[i].size;
522
523	disable_dma(devpriv->dma[i].chan);
524
525	devpriv->current_dma_index = 1 - i;
526
527	dt282x_munge(dev, ptr, size);
528	ret = cfc_write_array_to_buffer(s, ptr, size);
529	if (ret != size) {
530		dt282x_ai_cancel(dev, s);
531		return;
532	}
533	devpriv->nread -= size / 2;
534
535	if (devpriv->nread < 0) {
536		printk(KERN_INFO "dt282x: off by one\n");
537		devpriv->nread = 0;
538	}
539	if (!devpriv->nread) {
540		dt282x_ai_cancel(dev, s);
541		s->async->events |= COMEDI_CB_EOA;
542		return;
543	}
544#if 0
545	/* clear the dual dma flag, making this the last dma segment */
546	/* XXX probably wrong */
547	if (!devpriv->ntrig) {
548		devpriv->supcsr &= ~(DT2821_DDMA);
549		update_supcsr(0);
550	}
551#endif
552	/* restart the channel */
553	prep_ai_dma(dev, i, 0);
554}
555
556static int prep_ai_dma(struct comedi_device *dev, int dma_index, int n)
557{
558	int dma_chan;
559	unsigned long dma_ptr;
560	unsigned long flags;
561
562	if (!devpriv->ntrig)
563		return 0;
564
565	if (n == 0)
566		n = devpriv->dma_maxsize;
567	if (n > devpriv->ntrig * 2)
568		n = devpriv->ntrig * 2;
569	devpriv->ntrig -= n / 2;
570
571	devpriv->dma[dma_index].size = n;
572	dma_chan = devpriv->dma[dma_index].chan;
573	dma_ptr = virt_to_bus(devpriv->dma[dma_index].buf);
574
575	set_dma_mode(dma_chan, DMA_MODE_READ);
576	flags = claim_dma_lock();
577	clear_dma_ff(dma_chan);
578	set_dma_addr(dma_chan, dma_ptr);
579	set_dma_count(dma_chan, n);
580	release_dma_lock(flags);
581
582	enable_dma(dma_chan);
583
584	return n;
585}
586
587static int prep_ao_dma(struct comedi_device *dev, int dma_index, int n)
588{
589	int dma_chan;
590	unsigned long dma_ptr;
591	unsigned long flags;
592
593	devpriv->dma[dma_index].size = n;
594	dma_chan = devpriv->dma[dma_index].chan;
595	dma_ptr = virt_to_bus(devpriv->dma[dma_index].buf);
596
597	set_dma_mode(dma_chan, DMA_MODE_WRITE);
598	flags = claim_dma_lock();
599	clear_dma_ff(dma_chan);
600	set_dma_addr(dma_chan, dma_ptr);
601	set_dma_count(dma_chan, n);
602	release_dma_lock(flags);
603
604	enable_dma(dma_chan);
605
606	return n;
607}
608
609static irqreturn_t dt282x_interrupt(int irq, void *d)
610{
611	struct comedi_device *dev = d;
612	struct comedi_subdevice *s;
613	struct comedi_subdevice *s_ao;
614	unsigned int supcsr, adcsr, dacsr;
615	int handled = 0;
616
617	if (!dev->attached) {
618		comedi_error(dev, "spurious interrupt");
619		return IRQ_HANDLED;
620	}
621
622	s = dev->subdevices + 0;
623	s_ao = dev->subdevices + 1;
624	adcsr = inw(dev->iobase + DT2821_ADCSR);
625	dacsr = inw(dev->iobase + DT2821_DACSR);
626	supcsr = inw(dev->iobase + DT2821_SUPCSR);
627	if (supcsr & DT2821_DMAD) {
628		if (devpriv->dma_dir == DMA_MODE_READ)
629			dt282x_ai_dma_interrupt(dev);
630		else
631			dt282x_ao_dma_interrupt(dev);
632		handled = 1;
633	}
634	if (adcsr & DT2821_ADERR) {
635		if (devpriv->nread != 0) {
636			comedi_error(dev, "A/D error");
637			dt282x_ai_cancel(dev, s);
638			s->async->events |= COMEDI_CB_ERROR;
639		}
640		handled = 1;
641	}
642	if (dacsr & DT2821_DAERR) {
643#if 0
644		static int warn = 5;
645		if (--warn <= 0) {
646			disable_irq(dev->irq);
647			printk(KERN_INFO "disabling irq\n");
648		}
649#endif
650		comedi_error(dev, "D/A error");
651		dt282x_ao_cancel(dev, s_ao);
652		s->async->events |= COMEDI_CB_ERROR;
653		handled = 1;
654	}
655#if 0
656	if (adcsr & DT2821_ADDONE) {
657		int ret;
658		short data;
659
660		data = (short)inw(dev->iobase + DT2821_ADDAT);
661		data &= (1 << boardtype.adbits) - 1;
662
663		if (devpriv->ad_2scomp)
664			data ^= 1 << (boardtype.adbits - 1);
665		ret = comedi_buf_put(s->async, data);
666
667		if (ret == 0)
668			s->async->events |= COMEDI_CB_OVERFLOW;
669
670		devpriv->nread--;
671		if (!devpriv->nread) {
672			s->async->events |= COMEDI_CB_EOA;
673		} else {
674			if (supcsr & DT2821_SCDN)
675				update_supcsr(DT2821_STRIG);
676		}
677		handled = 1;
678	}
679#endif
680	comedi_event(dev, s);
681	/* printk("adcsr=0x%02x dacsr-0x%02x supcsr=0x%02x\n",
682		adcsr, dacsr, supcsr); */
683	return IRQ_RETVAL(handled);
684}
685
686static void dt282x_load_changain(struct comedi_device *dev, int n,
687				 unsigned int *chanlist)
688{
689	unsigned int i;
690	unsigned int chan, range;
691
692	outw(DT2821_LLE | (n - 1), dev->iobase + DT2821_CHANCSR);
693	for (i = 0; i < n; i++) {
694		chan = CR_CHAN(chanlist[i]);
695		range = CR_RANGE(chanlist[i]);
696		update_adcsr((range << 4) | (chan));
697	}
698	outw(n - 1, dev->iobase + DT2821_CHANCSR);
699}
700
701/*
702 *    Performs a single A/D conversion.
703 *      - Put channel/gain into channel-gain list
704 *      - preload multiplexer
705 *      - trigger conversion and wait for it to finish
706 */
707static int dt282x_ai_insn_read(struct comedi_device *dev,
708			       struct comedi_subdevice *s,
709			       struct comedi_insn *insn, unsigned int *data)
710{
711	int i;
712
713	/* XXX should we really be enabling the ad clock here? */
714	devpriv->adcsr = DT2821_ADCLK;
715	update_adcsr(0);
716
717	dt282x_load_changain(dev, 1, &insn->chanspec);
718
719	update_supcsr(DT2821_PRLD);
720	wait_for(!mux_busy(), comedi_error(dev, "timeout\n"); return -ETIME;);
721
722	for (i = 0; i < insn->n; i++) {
723		update_supcsr(DT2821_STRIG);
724		wait_for(ad_done(), comedi_error(dev, "timeout\n");
725			 return -ETIME;);
726
727		data[i] =
728		    inw(dev->iobase +
729			DT2821_ADDAT) & ((1 << boardtype.adbits) - 1);
730		if (devpriv->ad_2scomp)
731			data[i] ^= (1 << (boardtype.adbits - 1));
732	}
733
734	return i;
735}
736
737static int dt282x_ai_cmdtest(struct comedi_device *dev,
738			     struct comedi_subdevice *s, struct comedi_cmd *cmd)
739{
740	int err = 0;
741	int tmp;
742
743	/* step 1: make sure trigger sources are trivially valid */
744
745	tmp = cmd->start_src;
746	cmd->start_src &= TRIG_NOW;
747	if (!cmd->start_src || tmp != cmd->start_src)
748		err++;
749
750	tmp = cmd->scan_begin_src;
751	cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_EXT;
752	if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
753		err++;
754
755	tmp = cmd->convert_src;
756	cmd->convert_src &= TRIG_TIMER;
757	if (!cmd->convert_src || tmp != cmd->convert_src)
758		err++;
759
760	tmp = cmd->scan_end_src;
761	cmd->scan_end_src &= TRIG_COUNT;
762	if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
763		err++;
764
765	tmp = cmd->stop_src;
766	cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
767	if (!cmd->stop_src || tmp != cmd->stop_src)
768		err++;
769
770	if (err)
771		return 1;
772
773	/*
774	 * step 2: make sure trigger sources are unique
775	 * and mutually compatible
776	 */
777
778	/* note that mutual compatibility is not an issue here */
779	if (cmd->scan_begin_src != TRIG_FOLLOW &&
780	    cmd->scan_begin_src != TRIG_EXT)
781		err++;
782	if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
783		err++;
784
785	if (err)
786		return 2;
787
788	/* step 3: make sure arguments are trivially compatible */
789
790	if (cmd->start_arg != 0) {
791		cmd->start_arg = 0;
792		err++;
793	}
794	if (cmd->scan_begin_src == TRIG_FOLLOW) {
795		/* internal trigger */
796		if (cmd->scan_begin_arg != 0) {
797			cmd->scan_begin_arg = 0;
798			err++;
799		}
800	} else {
801		/* external trigger */
802		/* should be level/edge, hi/lo specification here */
803		if (cmd->scan_begin_arg != 0) {
804			cmd->scan_begin_arg = 0;
805			err++;
806		}
807	}
808	if (cmd->convert_arg < 4000) {
809		/* XXX board dependent */
810		cmd->convert_arg = 4000;
811		err++;
812	}
813#define SLOWEST_TIMER	(250*(1<<15)*255)
814	if (cmd->convert_arg > SLOWEST_TIMER) {
815		cmd->convert_arg = SLOWEST_TIMER;
816		err++;
817	}
818	if (cmd->convert_arg < this_board->ai_speed) {
819		cmd->convert_arg = this_board->ai_speed;
820		err++;
821	}
822	if (cmd->scan_end_arg != cmd->chanlist_len) {
823		cmd->scan_end_arg = cmd->chanlist_len;
824		err++;
825	}
826	if (cmd->stop_src == TRIG_COUNT) {
827		/* any count is allowed */
828	} else {
829		/* TRIG_NONE */
830		if (cmd->stop_arg != 0) {
831			cmd->stop_arg = 0;
832			err++;
833		}
834	}
835
836	if (err)
837		return 3;
838
839	/* step 4: fix up any arguments */
840
841	tmp = cmd->convert_arg;
842	dt282x_ns_to_timer(&cmd->convert_arg, cmd->flags & TRIG_ROUND_MASK);
843	if (tmp != cmd->convert_arg)
844		err++;
845
846	if (err)
847		return 4;
848
849	return 0;
850}
851
852static int dt282x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
853{
854	struct comedi_cmd *cmd = &s->async->cmd;
855	int timer;
856
857	if (devpriv->usedma == 0) {
858		comedi_error(dev,
859			     "driver requires 2 dma channels"
860						" to execute command");
861		return -EIO;
862	}
863
864	dt282x_disable_dma(dev);
865
866	if (cmd->convert_arg < this_board->ai_speed)
867		cmd->convert_arg = this_board->ai_speed;
868	timer = dt282x_ns_to_timer(&cmd->convert_arg, TRIG_ROUND_NEAREST);
869	outw(timer, dev->iobase + DT2821_TMRCTR);
870
871	if (cmd->scan_begin_src == TRIG_FOLLOW) {
872		/* internal trigger */
873		devpriv->supcsr = DT2821_ERRINTEN | DT2821_DS0;
874	} else {
875		/* external trigger */
876		devpriv->supcsr = DT2821_ERRINTEN | DT2821_DS0 | DT2821_DS1;
877	}
878	update_supcsr(DT2821_CLRDMADNE | DT2821_BUFFB | DT2821_ADCINIT);
879
880	devpriv->ntrig = cmd->stop_arg * cmd->scan_end_arg;
881	devpriv->nread = devpriv->ntrig;
882
883	devpriv->dma_dir = DMA_MODE_READ;
884	devpriv->current_dma_index = 0;
885	prep_ai_dma(dev, 0, 0);
886	if (devpriv->ntrig) {
887		prep_ai_dma(dev, 1, 0);
888		devpriv->supcsr |= DT2821_DDMA;
889		update_supcsr(0);
890	}
891
892	devpriv->adcsr = 0;
893
894	dt282x_load_changain(dev, cmd->chanlist_len, cmd->chanlist);
895
896	devpriv->adcsr = DT2821_ADCLK | DT2821_IADDONE;
897	update_adcsr(0);
898
899	update_supcsr(DT2821_PRLD);
900	wait_for(!mux_busy(), comedi_error(dev, "timeout\n"); return -ETIME;);
901
902	if (cmd->scan_begin_src == TRIG_FOLLOW) {
903		update_supcsr(DT2821_STRIG);
904	} else {
905		devpriv->supcsr |= DT2821_XTRIG;
906		update_supcsr(0);
907	}
908
909	return 0;
910}
911
912static void dt282x_disable_dma(struct comedi_device *dev)
913{
914	if (devpriv->usedma) {
915		disable_dma(devpriv->dma[0].chan);
916		disable_dma(devpriv->dma[1].chan);
917	}
918}
919
920static int dt282x_ai_cancel(struct comedi_device *dev,
921			    struct comedi_subdevice *s)
922{
923	dt282x_disable_dma(dev);
924
925	devpriv->adcsr = 0;
926	update_adcsr(0);
927
928	devpriv->supcsr = 0;
929	update_supcsr(DT2821_ADCINIT);
930
931	return 0;
932}
933
934static int dt282x_ns_to_timer(int *nanosec, int round_mode)
935{
936	int prescale, base, divider;
937
938	for (prescale = 0; prescale < 16; prescale++) {
939		if (prescale == 1)
940			continue;
941		base = 250 * (1 << prescale);
942		switch (round_mode) {
943		case TRIG_ROUND_NEAREST:
944		default:
945			divider = (*nanosec + base / 2) / base;
946			break;
947		case TRIG_ROUND_DOWN:
948			divider = (*nanosec) / base;
949			break;
950		case TRIG_ROUND_UP:
951			divider = (*nanosec + base - 1) / base;
952			break;
953		}
954		if (divider < 256) {
955			*nanosec = divider * base;
956			return (prescale << 8) | (255 - divider);
957		}
958	}
959	base = 250 * (1 << 15);
960	divider = 255;
961	*nanosec = divider * base;
962	return (15 << 8) | (255 - divider);
963}
964
965/*
966 *    Analog output routine.  Selects single channel conversion,
967 *      selects correct channel, converts from 2's compliment to
968 *      offset binary if necessary, loads the data into the DAC
969 *      data register, and performs the conversion.
970 */
971static int dt282x_ao_insn_read(struct comedi_device *dev,
972			       struct comedi_subdevice *s,
973			       struct comedi_insn *insn, unsigned int *data)
974{
975	data[0] = devpriv->ao[CR_CHAN(insn->chanspec)];
976
977	return 1;
978}
979
980static int dt282x_ao_insn_write(struct comedi_device *dev,
981				struct comedi_subdevice *s,
982				struct comedi_insn *insn, unsigned int *data)
983{
984	short d;
985	unsigned int chan;
986
987	chan = CR_CHAN(insn->chanspec);
988	d = data[0];
989	d &= (1 << boardtype.dabits) - 1;
990	devpriv->ao[chan] = d;
991
992	devpriv->dacsr |= DT2821_SSEL;
993
994	if (chan) {
995		/* select channel */
996		devpriv->dacsr |= DT2821_YSEL;
997		if (devpriv->da0_2scomp)
998			d ^= (1 << (boardtype.dabits - 1));
999	} else {
1000		devpriv->dacsr &= ~DT2821_YSEL;
1001		if (devpriv->da1_2scomp)
1002			d ^= (1 << (boardtype.dabits - 1));
1003	}
1004
1005	update_dacsr(0);
1006
1007	outw(d, dev->iobase + DT2821_DADAT);
1008
1009	update_supcsr(DT2821_DACON);
1010
1011	return 1;
1012}
1013
1014static int dt282x_ao_cmdtest(struct comedi_device *dev,
1015			     struct comedi_subdevice *s, struct comedi_cmd *cmd)
1016{
1017	int err = 0;
1018	int tmp;
1019
1020	/* step 1: make sure trigger sources are trivially valid */
1021
1022	tmp = cmd->start_src;
1023	cmd->start_src &= TRIG_INT;
1024	if (!cmd->start_src || tmp != cmd->start_src)
1025		err++;
1026
1027	tmp = cmd->scan_begin_src;
1028	cmd->scan_begin_src &= TRIG_TIMER;
1029	if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
1030		err++;
1031
1032	tmp = cmd->convert_src;
1033	cmd->convert_src &= TRIG_NOW;
1034	if (!cmd->convert_src || tmp != cmd->convert_src)
1035		err++;
1036
1037	tmp = cmd->scan_end_src;
1038	cmd->scan_end_src &= TRIG_COUNT;
1039	if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
1040		err++;
1041
1042	tmp = cmd->stop_src;
1043	cmd->stop_src &= TRIG_NONE;
1044	if (!cmd->stop_src || tmp != cmd->stop_src)
1045		err++;
1046
1047	if (err)
1048		return 1;
1049
1050	/*
1051	 * step 2: make sure trigger sources are unique
1052	 * and mutually compatible
1053	 */
1054
1055	/* note that mutual compatibility is not an issue here */
1056	if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
1057		err++;
1058
1059	if (err)
1060		return 2;
1061
1062	/* step 3: make sure arguments are trivially compatible */
1063
1064	if (cmd->start_arg != 0) {
1065		cmd->start_arg = 0;
1066		err++;
1067	}
1068	if (cmd->scan_begin_arg < 5000 /* XXX unknown */) {
1069		cmd->scan_begin_arg = 5000;
1070		err++;
1071	}
1072	if (cmd->convert_arg != 0) {
1073		cmd->convert_arg = 0;
1074		err++;
1075	}
1076	if (cmd->scan_end_arg > 2) {
1077		/* XXX chanlist stuff? */
1078		cmd->scan_end_arg = 2;
1079		err++;
1080	}
1081	if (cmd->stop_src == TRIG_COUNT) {
1082		/* any count is allowed */
1083	} else {
1084		/* TRIG_NONE */
1085		if (cmd->stop_arg != 0) {
1086			cmd->stop_arg = 0;
1087			err++;
1088		}
1089	}
1090
1091	if (err)
1092		return 3;
1093
1094	/* step 4: fix up any arguments */
1095
1096	tmp = cmd->scan_begin_arg;
1097	dt282x_ns_to_timer(&cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK);
1098	if (tmp != cmd->scan_begin_arg)
1099		err++;
1100
1101	if (err)
1102		return 4;
1103
1104	return 0;
1105
1106}
1107
1108static int dt282x_ao_inttrig(struct comedi_device *dev,
1109			     struct comedi_subdevice *s, unsigned int x)
1110{
1111	int size;
1112
1113	if (x != 0)
1114		return -EINVAL;
1115
1116	size = cfc_read_array_from_buffer(s, devpriv->dma[0].buf,
1117					  devpriv->dma_maxsize);
1118	if (size == 0) {
1119		printk(KERN_ERR "dt282x: AO underrun\n");
1120		return -EPIPE;
1121	}
1122	prep_ao_dma(dev, 0, size);
1123
1124	size = cfc_read_array_from_buffer(s, devpriv->dma[1].buf,
1125					  devpriv->dma_maxsize);
1126	if (size == 0) {
1127		printk(KERN_ERR "dt282x: AO underrun\n");
1128		return -EPIPE;
1129	}
1130	prep_ao_dma(dev, 1, size);
1131
1132	update_supcsr(DT2821_STRIG);
1133	s->async->inttrig = NULL;
1134
1135	return 1;
1136}
1137
1138static int dt282x_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
1139{
1140	int timer;
1141	struct comedi_cmd *cmd = &s->async->cmd;
1142
1143	if (devpriv->usedma == 0) {
1144		comedi_error(dev,
1145			     "driver requires 2 dma channels"
1146						" to execute command");
1147		return -EIO;
1148	}
1149
1150	dt282x_disable_dma(dev);
1151
1152	devpriv->supcsr = DT2821_ERRINTEN | DT2821_DS1 | DT2821_DDMA;
1153	update_supcsr(DT2821_CLRDMADNE | DT2821_BUFFB | DT2821_DACINIT);
1154
1155	devpriv->ntrig = cmd->stop_arg * cmd->chanlist_len;
1156	devpriv->nread = devpriv->ntrig;
1157
1158	devpriv->dma_dir = DMA_MODE_WRITE;
1159	devpriv->current_dma_index = 0;
1160
1161	timer = dt282x_ns_to_timer(&cmd->scan_begin_arg, TRIG_ROUND_NEAREST);
1162	outw(timer, dev->iobase + DT2821_TMRCTR);
1163
1164	devpriv->dacsr = DT2821_SSEL | DT2821_DACLK | DT2821_IDARDY;
1165	update_dacsr(0);
1166
1167	s->async->inttrig = dt282x_ao_inttrig;
1168
1169	return 0;
1170}
1171
1172static int dt282x_ao_cancel(struct comedi_device *dev,
1173			    struct comedi_subdevice *s)
1174{
1175	dt282x_disable_dma(dev);
1176
1177	devpriv->dacsr = 0;
1178	update_dacsr(0);
1179
1180	devpriv->supcsr = 0;
1181	update_supcsr(DT2821_DACINIT);
1182
1183	return 0;
1184}
1185
1186static int dt282x_dio_insn_bits(struct comedi_device *dev,
1187				struct comedi_subdevice *s,
1188				struct comedi_insn *insn, unsigned int *data)
1189{
1190	if (data[0]) {
1191		s->state &= ~data[0];
1192		s->state |= (data[0] & data[1]);
1193
1194		outw(s->state, dev->iobase + DT2821_DIODAT);
1195	}
1196	data[1] = inw(dev->iobase + DT2821_DIODAT);
1197
1198	return 2;
1199}
1200
1201static int dt282x_dio_insn_config(struct comedi_device *dev,
1202				  struct comedi_subdevice *s,
1203				  struct comedi_insn *insn, unsigned int *data)
1204{
1205	int mask;
1206
1207	mask = (CR_CHAN(insn->chanspec) < 8) ? 0x00ff : 0xff00;
1208	if (data[0])
1209		s->io_bits |= mask;
1210	else
1211		s->io_bits &= ~mask;
1212
1213	if (s->io_bits & 0x00ff)
1214		devpriv->dacsr |= DT2821_LBOE;
1215	else
1216		devpriv->dacsr &= ~DT2821_LBOE;
1217	if (s->io_bits & 0xff00)
1218		devpriv->dacsr |= DT2821_HBOE;
1219	else
1220		devpriv->dacsr &= ~DT2821_HBOE;
1221
1222	outw(devpriv->dacsr, dev->iobase + DT2821_DACSR);
1223
1224	return 1;
1225}
1226
1227static const struct comedi_lrange *const ai_range_table[] = {
1228	&range_dt282x_ai_lo_bipolar,
1229	&range_dt282x_ai_lo_unipolar,
1230	&range_dt282x_ai_5_bipolar,
1231	&range_dt282x_ai_5_unipolar
1232};
1233
1234static const struct comedi_lrange *const ai_range_pgl_table[] = {
1235	&range_dt282x_ai_hi_bipolar,
1236	&range_dt282x_ai_hi_unipolar
1237};
1238
1239static const struct comedi_lrange *opt_ai_range_lkup(int ispgl, int x)
1240{
1241	if (ispgl) {
1242		if (x < 0 || x >= 2)
1243			x = 0;
1244		return ai_range_pgl_table[x];
1245	} else {
1246		if (x < 0 || x >= 4)
1247			x = 0;
1248		return ai_range_table[x];
1249	}
1250}
1251
1252static const struct comedi_lrange *const ao_range_table[] = {
1253	&range_bipolar10,
1254	&range_unipolar10,
1255	&range_bipolar5,
1256	&range_unipolar5,
1257	&range_bipolar2_5
1258};
1259
1260static const struct comedi_lrange *opt_ao_range_lkup(int x)
1261{
1262	if (x < 0 || x >= 5)
1263		x = 0;
1264	return ao_range_table[x];
1265}
1266
1267enum {  /* i/o base, irq, dma channels */
1268	opt_iobase = 0, opt_irq, opt_dma1, opt_dma2,
1269	opt_diff,		/* differential */
1270	opt_ai_twos, opt_ao0_twos, opt_ao1_twos,	/* twos comp */
1271	opt_ai_range, opt_ao0_range, opt_ao1_range,	/* range */
1272};
1273
1274/*
1275   options:
1276   0	i/o base
1277   1	irq
1278   2	dma1
1279   3	dma2
1280   4	0=single ended, 1=differential
1281   5	ai 0=straight binary, 1=2's comp
1282   6	ao0 0=straight binary, 1=2's comp
1283   7	ao1 0=straight binary, 1=2's comp
1284   8	ai 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V
1285   9	ao0 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V, 4=±2.5 V
1286   10	ao1 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V, 4=±2.5 V
1287 */
1288static int dt282x_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1289{
1290	int i, irq;
1291	int ret;
1292	struct comedi_subdevice *s;
1293	unsigned long iobase;
1294
1295	dev->board_name = this_board->name;
1296
1297	iobase = it->options[opt_iobase];
1298	if (!iobase)
1299		iobase = 0x240;
1300
1301	printk(KERN_INFO "comedi%d: dt282x: 0x%04lx", dev->minor, iobase);
1302	if (!request_region(iobase, DT2821_SIZE, "dt282x")) {
1303		printk(KERN_INFO " I/O port conflict\n");
1304		return -EBUSY;
1305	}
1306	dev->iobase = iobase;
1307
1308	outw(DT2821_BDINIT, dev->iobase + DT2821_SUPCSR);
1309	i = inw(dev->iobase + DT2821_ADCSR);
1310#ifdef DEBUG
1311	printk(KERN_DEBUG " fingerprint=%x,%x,%x,%x,%x",
1312	       inw(dev->iobase + DT2821_ADCSR),
1313	       inw(dev->iobase + DT2821_CHANCSR),
1314	       inw(dev->iobase + DT2821_DACSR),
1315	       inw(dev->iobase + DT2821_SUPCSR),
1316	       inw(dev->iobase + DT2821_TMRCTR));
1317#endif
1318
1319	if (((inw(dev->iobase + DT2821_ADCSR) & DT2821_ADCSR_MASK)
1320	     != DT2821_ADCSR_VAL) ||
1321	    ((inw(dev->iobase + DT2821_CHANCSR) & DT2821_CHANCSR_MASK)
1322	     != DT2821_CHANCSR_VAL) ||
1323	    ((inw(dev->iobase + DT2821_DACSR) & DT2821_DACSR_MASK)
1324	     != DT2821_DACSR_VAL) ||
1325	    ((inw(dev->iobase + DT2821_SUPCSR) & DT2821_SUPCSR_MASK)
1326	     != DT2821_SUPCSR_VAL) ||
1327	    ((inw(dev->iobase + DT2821_TMRCTR) & DT2821_TMRCTR_MASK)
1328	     != DT2821_TMRCTR_VAL)) {
1329		printk(KERN_ERR " board not found");
1330		return -EIO;
1331	}
1332	/* should do board test */
1333
1334	irq = it->options[opt_irq];
1335#if 0
1336	if (irq < 0) {
1337		unsigned long flags;
1338		int irqs;
1339
1340		save_flags(flags);
1341		sti();
1342		irqs = probe_irq_on();
1343
1344		/* trigger interrupt */
1345
1346		udelay(100);
1347
1348		irq = probe_irq_off(irqs);
1349		restore_flags(flags);
1350		if (0 /* error */)
1351			printk(KERN_ERR " error probing irq (bad)");
1352	}
1353#endif
1354	if (irq > 0) {
1355		printk(KERN_INFO " ( irq = %d )", irq);
1356		ret = request_irq(irq, dt282x_interrupt, 0, "dt282x", dev);
1357		if (ret < 0) {
1358			printk(KERN_ERR " failed to get irq\n");
1359			return -EIO;
1360		}
1361		dev->irq = irq;
1362	} else if (irq == 0) {
1363		printk(KERN_INFO " (no irq)");
1364	} else {
1365#if 0
1366		printk(KERN_INFO " (probe returned multiple irqs--bad)");
1367#else
1368		printk(KERN_INFO " (irq probe not implemented)");
1369#endif
1370	}
1371
1372	ret = alloc_private(dev, sizeof(struct dt282x_private));
1373	if (ret < 0)
1374		return ret;
1375
1376	ret = dt282x_grab_dma(dev, it->options[opt_dma1],
1377			      it->options[opt_dma2]);
1378	if (ret < 0)
1379		return ret;
1380
1381	ret = alloc_subdevices(dev, 3);
1382	if (ret < 0)
1383		return ret;
1384
1385	s = dev->subdevices + 0;
1386
1387	dev->read_subdev = s;
1388	/* ai subdevice */
1389	s->type = COMEDI_SUBD_AI;
1390	s->subdev_flags = SDF_READABLE | SDF_CMD_READ |
1391	    ((it->options[opt_diff]) ? SDF_DIFF : SDF_COMMON);
1392	s->n_chan =
1393	    (it->options[opt_diff]) ? boardtype.adchan_di : boardtype.adchan_se;
1394	s->insn_read = dt282x_ai_insn_read;
1395	s->do_cmdtest = dt282x_ai_cmdtest;
1396	s->do_cmd = dt282x_ai_cmd;
1397	s->cancel = dt282x_ai_cancel;
1398	s->maxdata = (1 << boardtype.adbits) - 1;
1399	s->len_chanlist = 16;
1400	s->range_table =
1401	    opt_ai_range_lkup(boardtype.ispgl, it->options[opt_ai_range]);
1402	devpriv->ad_2scomp = it->options[opt_ai_twos];
1403
1404	s++;
1405
1406	s->n_chan = boardtype.dachan;
1407	if (s->n_chan) {
1408		/* ao subsystem */
1409		s->type = COMEDI_SUBD_AO;
1410		dev->write_subdev = s;
1411		s->subdev_flags = SDF_WRITABLE | SDF_CMD_WRITE;
1412		s->insn_read = dt282x_ao_insn_read;
1413		s->insn_write = dt282x_ao_insn_write;
1414		s->do_cmdtest = dt282x_ao_cmdtest;
1415		s->do_cmd = dt282x_ao_cmd;
1416		s->cancel = dt282x_ao_cancel;
1417		s->maxdata = (1 << boardtype.dabits) - 1;
1418		s->len_chanlist = 2;
1419		s->range_table_list = devpriv->darangelist;
1420		devpriv->darangelist[0] =
1421		    opt_ao_range_lkup(it->options[opt_ao0_range]);
1422		devpriv->darangelist[1] =
1423		    opt_ao_range_lkup(it->options[opt_ao1_range]);
1424		devpriv->da0_2scomp = it->options[opt_ao0_twos];
1425		devpriv->da1_2scomp = it->options[opt_ao1_twos];
1426	} else {
1427		s->type = COMEDI_SUBD_UNUSED;
1428	}
1429
1430	s++;
1431	/* dio subsystem */
1432	s->type = COMEDI_SUBD_DIO;
1433	s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
1434	s->n_chan = 16;
1435	s->insn_bits = dt282x_dio_insn_bits;
1436	s->insn_config = dt282x_dio_insn_config;
1437	s->maxdata = 1;
1438	s->range_table = &range_digital;
1439
1440	printk(KERN_INFO "\n");
1441
1442	return 0;
1443}
1444
1445static void free_resources(struct comedi_device *dev)
1446{
1447	if (dev->irq)
1448		free_irq(dev->irq, dev);
1449	if (dev->iobase)
1450		release_region(dev->iobase, DT2821_SIZE);
1451	if (dev->private) {
1452		if (devpriv->dma[0].chan)
1453			free_dma(devpriv->dma[0].chan);
1454		if (devpriv->dma[1].chan)
1455			free_dma(devpriv->dma[1].chan);
1456		if (devpriv->dma[0].buf)
1457			free_page((unsigned long)devpriv->dma[0].buf);
1458		if (devpriv->dma[1].buf)
1459			free_page((unsigned long)devpriv->dma[1].buf);
1460	}
1461}
1462
1463static int dt282x_detach(struct comedi_device *dev)
1464{
1465	printk(KERN_INFO "comedi%d: dt282x: remove\n", dev->minor);
1466
1467	free_resources(dev);
1468
1469	return 0;
1470}
1471
1472static int dt282x_grab_dma(struct comedi_device *dev, int dma1, int dma2)
1473{
1474	int ret;
1475
1476	devpriv->usedma = 0;
1477
1478	if (!dma1 && !dma2) {
1479		printk(KERN_ERR " (no dma)");
1480		return 0;
1481	}
1482
1483	if (dma1 == dma2 || dma1 < 5 || dma2 < 5 || dma1 > 7 || dma2 > 7)
1484		return -EINVAL;
1485
1486	if (dma2 < dma1) {
1487		int i;
1488		i = dma1;
1489		dma1 = dma2;
1490		dma2 = i;
1491	}
1492
1493	ret = request_dma(dma1, "dt282x A");
1494	if (ret)
1495		return -EBUSY;
1496	devpriv->dma[0].chan = dma1;
1497
1498	ret = request_dma(dma2, "dt282x B");
1499	if (ret)
1500		return -EBUSY;
1501	devpriv->dma[1].chan = dma2;
1502
1503	devpriv->dma_maxsize = PAGE_SIZE;
1504	devpriv->dma[0].buf = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
1505	devpriv->dma[1].buf = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
1506	if (!devpriv->dma[0].buf || !devpriv->dma[1].buf) {
1507		printk(KERN_ERR " can't get DMA memory");
1508		return -ENOMEM;
1509	}
1510
1511	printk(KERN_INFO " (dma=%d,%d)", dma1, dma2);
1512
1513	devpriv->usedma = 1;
1514
1515	return 0;
1516}
1517
1518MODULE_AUTHOR("Comedi http://www.comedi.org");
1519MODULE_DESCRIPTION("Comedi low-level driver");
1520MODULE_LICENSE("GPL");
1521