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