Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dt282x.c
Go to the documentation of this file.
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 <[email protected]>
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;
218  int ai_speed;
219  int ispgl;
220  int dachan;
221  int dabits;
222 };
223 
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[0];
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 : check if triggers are trivially valid */
586 
587  err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
588  err |= cfc_check_trigger_src(&cmd->scan_begin_src,
590  err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
591  err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
592  err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
593 
594  if (err)
595  return 1;
596 
597  /* Step 2a : make sure trigger sources are unique */
598 
599  err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
600  err |= cfc_check_trigger_is_unique(cmd->stop_src);
601 
602  /* Step 2b : and mutually compatible */
603 
604  if (err)
605  return 2;
606 
607  /* step 3: make sure arguments are trivially compatible */
608 
609  if (cmd->start_arg != 0) {
610  cmd->start_arg = 0;
611  err++;
612  }
613  if (cmd->scan_begin_src == TRIG_FOLLOW) {
614  /* internal trigger */
615  if (cmd->scan_begin_arg != 0) {
616  cmd->scan_begin_arg = 0;
617  err++;
618  }
619  } else {
620  /* external trigger */
621  /* should be level/edge, hi/lo specification here */
622  if (cmd->scan_begin_arg != 0) {
623  cmd->scan_begin_arg = 0;
624  err++;
625  }
626  }
627  if (cmd->convert_arg < 4000) {
628  /* XXX board dependent */
629  cmd->convert_arg = 4000;
630  err++;
631  }
632 #define SLOWEST_TIMER (250*(1<<15)*255)
633  if (cmd->convert_arg > SLOWEST_TIMER) {
634  cmd->convert_arg = SLOWEST_TIMER;
635  err++;
636  }
637  if (cmd->convert_arg < board->ai_speed) {
638  cmd->convert_arg = board->ai_speed;
639  err++;
640  }
641  if (cmd->scan_end_arg != cmd->chanlist_len) {
642  cmd->scan_end_arg = cmd->chanlist_len;
643  err++;
644  }
645  if (cmd->stop_src == TRIG_COUNT) {
646  /* any count is allowed */
647  } else {
648  /* TRIG_NONE */
649  if (cmd->stop_arg != 0) {
650  cmd->stop_arg = 0;
651  err++;
652  }
653  }
654 
655  if (err)
656  return 3;
657 
658  /* step 4: fix up any arguments */
659 
660  tmp = cmd->convert_arg;
661  dt282x_ns_to_timer(&cmd->convert_arg, cmd->flags & TRIG_ROUND_MASK);
662  if (tmp != cmd->convert_arg)
663  err++;
664 
665  if (err)
666  return 4;
667 
668  return 0;
669 }
670 
671 static int dt282x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
672 {
673  const struct dt282x_board *board = comedi_board(dev);
674  struct comedi_cmd *cmd = &s->async->cmd;
675  int timer;
676 
677  if (devpriv->usedma == 0) {
678  comedi_error(dev,
679  "driver requires 2 dma channels"
680  " to execute command");
681  return -EIO;
682  }
683 
684  dt282x_disable_dma(dev);
685 
686  if (cmd->convert_arg < board->ai_speed)
687  cmd->convert_arg = board->ai_speed;
688  timer = dt282x_ns_to_timer(&cmd->convert_arg, TRIG_ROUND_NEAREST);
689  outw(timer, dev->iobase + DT2821_TMRCTR);
690 
691  if (cmd->scan_begin_src == TRIG_FOLLOW) {
692  /* internal trigger */
693  devpriv->supcsr = DT2821_ERRINTEN | DT2821_DS0;
694  } else {
695  /* external trigger */
697  }
699  dev->iobase + DT2821_SUPCSR);
700 
701  devpriv->ntrig = cmd->stop_arg * cmd->scan_end_arg;
702  devpriv->nread = devpriv->ntrig;
703 
704  devpriv->dma_dir = DMA_MODE_READ;
705  devpriv->current_dma_index = 0;
706  prep_ai_dma(dev, 0, 0);
707  if (devpriv->ntrig) {
708  prep_ai_dma(dev, 1, 0);
709  devpriv->supcsr |= DT2821_DDMA;
710  outw(devpriv->supcsr, dev->iobase + DT2821_SUPCSR);
711  }
712 
713  devpriv->adcsr = 0;
714 
715  dt282x_load_changain(dev, cmd->chanlist_len, cmd->chanlist);
716 
718  outw(devpriv->adcsr, dev->iobase + DT2821_ADCSR);
719 
720  outw(devpriv->supcsr | DT2821_PRLD, dev->iobase + DT2821_SUPCSR);
721  wait_for(!mux_busy(), comedi_error(dev, "timeout\n"); return -ETIME;);
722 
723  if (cmd->scan_begin_src == TRIG_FOLLOW) {
724  outw(devpriv->supcsr | DT2821_STRIG,
725  dev->iobase + DT2821_SUPCSR);
726  } else {
727  devpriv->supcsr |= DT2821_XTRIG;
728  outw(devpriv->supcsr, dev->iobase + DT2821_SUPCSR);
729  }
730 
731  return 0;
732 }
733 
734 static void dt282x_disable_dma(struct comedi_device *dev)
735 {
736  if (devpriv->usedma) {
737  disable_dma(devpriv->dma[0].chan);
738  disable_dma(devpriv->dma[1].chan);
739  }
740 }
741 
742 static int dt282x_ai_cancel(struct comedi_device *dev,
743  struct comedi_subdevice *s)
744 {
745  dt282x_disable_dma(dev);
746 
747  devpriv->adcsr = 0;
748  outw(devpriv->adcsr, dev->iobase + DT2821_ADCSR);
749 
750  devpriv->supcsr = 0;
751  outw(devpriv->supcsr | DT2821_ADCINIT, dev->iobase + DT2821_SUPCSR);
752 
753  return 0;
754 }
755 
756 static int dt282x_ns_to_timer(int *nanosec, int round_mode)
757 {
758  int prescale, base, divider;
759 
760  for (prescale = 0; prescale < 16; prescale++) {
761  if (prescale == 1)
762  continue;
763  base = 250 * (1 << prescale);
764  switch (round_mode) {
765  case TRIG_ROUND_NEAREST:
766  default:
767  divider = (*nanosec + base / 2) / base;
768  break;
769  case TRIG_ROUND_DOWN:
770  divider = (*nanosec) / base;
771  break;
772  case TRIG_ROUND_UP:
773  divider = (*nanosec + base - 1) / base;
774  break;
775  }
776  if (divider < 256) {
777  *nanosec = divider * base;
778  return (prescale << 8) | (255 - divider);
779  }
780  }
781  base = 250 * (1 << 15);
782  divider = 255;
783  *nanosec = divider * base;
784  return (15 << 8) | (255 - divider);
785 }
786 
787 /*
788  * Analog output routine. Selects single channel conversion,
789  * selects correct channel, converts from 2's compliment to
790  * offset binary if necessary, loads the data into the DAC
791  * data register, and performs the conversion.
792  */
793 static int dt282x_ao_insn_read(struct comedi_device *dev,
794  struct comedi_subdevice *s,
795  struct comedi_insn *insn, unsigned int *data)
796 {
797  data[0] = devpriv->ao[CR_CHAN(insn->chanspec)];
798 
799  return 1;
800 }
801 
802 static int dt282x_ao_insn_write(struct comedi_device *dev,
803  struct comedi_subdevice *s,
804  struct comedi_insn *insn, unsigned int *data)
805 {
806  short d;
807  unsigned int chan;
808 
809  chan = CR_CHAN(insn->chanspec);
810  d = data[0];
811  d &= (1 << boardtype.dabits) - 1;
812  devpriv->ao[chan] = d;
813 
814  devpriv->dacsr |= DT2821_SSEL;
815 
816  if (chan) {
817  /* select channel */
818  devpriv->dacsr |= DT2821_YSEL;
819  if (devpriv->da0_2scomp)
820  d ^= (1 << (boardtype.dabits - 1));
821  } else {
822  devpriv->dacsr &= ~DT2821_YSEL;
823  if (devpriv->da1_2scomp)
824  d ^= (1 << (boardtype.dabits - 1));
825  }
826 
827  outw(devpriv->dacsr, dev->iobase + DT2821_DACSR);
828 
829  outw(d, dev->iobase + DT2821_DADAT);
830 
831  outw(devpriv->supcsr | DT2821_DACON, dev->iobase + DT2821_SUPCSR);
832 
833  return 1;
834 }
835 
836 static int dt282x_ao_cmdtest(struct comedi_device *dev,
837  struct comedi_subdevice *s, struct comedi_cmd *cmd)
838 {
839  int err = 0;
840  int tmp;
841 
842  /* Step 1 : check if triggers are trivially valid */
843 
844  err |= cfc_check_trigger_src(&cmd->start_src, TRIG_INT);
845  err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
846  err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
847  err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
848  err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
849 
850  if (err)
851  return 1;
852 
853  /* Step 2a : make sure trigger sources are unique */
854 
855  err |= cfc_check_trigger_is_unique(cmd->stop_src);
856 
857  /* Step 2b : and mutually compatible */
858 
859  if (err)
860  return 2;
861 
862  /* step 3: make sure arguments are trivially compatible */
863 
864  if (cmd->start_arg != 0) {
865  cmd->start_arg = 0;
866  err++;
867  }
868  if (cmd->scan_begin_arg < 5000 /* XXX unknown */) {
869  cmd->scan_begin_arg = 5000;
870  err++;
871  }
872  if (cmd->convert_arg != 0) {
873  cmd->convert_arg = 0;
874  err++;
875  }
876  if (cmd->scan_end_arg > 2) {
877  /* XXX chanlist stuff? */
878  cmd->scan_end_arg = 2;
879  err++;
880  }
881  if (cmd->stop_src == TRIG_COUNT) {
882  /* any count is allowed */
883  } else {
884  /* TRIG_NONE */
885  if (cmd->stop_arg != 0) {
886  cmd->stop_arg = 0;
887  err++;
888  }
889  }
890 
891  if (err)
892  return 3;
893 
894  /* step 4: fix up any arguments */
895 
896  tmp = cmd->scan_begin_arg;
897  dt282x_ns_to_timer(&cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK);
898  if (tmp != cmd->scan_begin_arg)
899  err++;
900 
901  if (err)
902  return 4;
903 
904  return 0;
905 
906 }
907 
908 static int dt282x_ao_inttrig(struct comedi_device *dev,
909  struct comedi_subdevice *s, unsigned int x)
910 {
911  int size;
912 
913  if (x != 0)
914  return -EINVAL;
915 
916  size = cfc_read_array_from_buffer(s, devpriv->dma[0].buf,
917  devpriv->dma_maxsize);
918  if (size == 0) {
919  printk(KERN_ERR "dt282x: AO underrun\n");
920  return -EPIPE;
921  }
922  prep_ao_dma(dev, 0, size);
923 
924  size = cfc_read_array_from_buffer(s, devpriv->dma[1].buf,
925  devpriv->dma_maxsize);
926  if (size == 0) {
927  printk(KERN_ERR "dt282x: AO underrun\n");
928  return -EPIPE;
929  }
930  prep_ao_dma(dev, 1, size);
931 
932  outw(devpriv->supcsr | DT2821_STRIG, dev->iobase + DT2821_SUPCSR);
933  s->async->inttrig = NULL;
934 
935  return 1;
936 }
937 
938 static int dt282x_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
939 {
940  int timer;
941  struct comedi_cmd *cmd = &s->async->cmd;
942 
943  if (devpriv->usedma == 0) {
944  comedi_error(dev,
945  "driver requires 2 dma channels"
946  " to execute command");
947  return -EIO;
948  }
949 
950  dt282x_disable_dma(dev);
951 
954  dev->iobase + DT2821_SUPCSR);
955 
956  devpriv->ntrig = cmd->stop_arg * cmd->chanlist_len;
957  devpriv->nread = devpriv->ntrig;
958 
959  devpriv->dma_dir = DMA_MODE_WRITE;
960  devpriv->current_dma_index = 0;
961 
962  timer = dt282x_ns_to_timer(&cmd->scan_begin_arg, TRIG_ROUND_NEAREST);
963  outw(timer, dev->iobase + DT2821_TMRCTR);
964 
966  outw(devpriv->dacsr, dev->iobase + DT2821_DACSR);
967 
968  s->async->inttrig = dt282x_ao_inttrig;
969 
970  return 0;
971 }
972 
973 static int dt282x_ao_cancel(struct comedi_device *dev,
974  struct comedi_subdevice *s)
975 {
976  dt282x_disable_dma(dev);
977 
978  devpriv->dacsr = 0;
979  outw(devpriv->dacsr, dev->iobase + DT2821_DACSR);
980 
981  devpriv->supcsr = 0;
982  outw(devpriv->supcsr | DT2821_DACINIT, dev->iobase + DT2821_SUPCSR);
983 
984  return 0;
985 }
986 
987 static int dt282x_dio_insn_bits(struct comedi_device *dev,
988  struct comedi_subdevice *s,
989  struct comedi_insn *insn, unsigned int *data)
990 {
991  if (data[0]) {
992  s->state &= ~data[0];
993  s->state |= (data[0] & data[1]);
994 
995  outw(s->state, dev->iobase + DT2821_DIODAT);
996  }
997  data[1] = inw(dev->iobase + DT2821_DIODAT);
998 
999  return insn->n;
1000 }
1001 
1002 static int dt282x_dio_insn_config(struct comedi_device *dev,
1003  struct comedi_subdevice *s,
1004  struct comedi_insn *insn, unsigned int *data)
1005 {
1006  int mask;
1007 
1008  mask = (CR_CHAN(insn->chanspec) < 8) ? 0x00ff : 0xff00;
1009  if (data[0])
1010  s->io_bits |= mask;
1011  else
1012  s->io_bits &= ~mask;
1013 
1014  if (s->io_bits & 0x00ff)
1015  devpriv->dacsr |= DT2821_LBOE;
1016  else
1017  devpriv->dacsr &= ~DT2821_LBOE;
1018  if (s->io_bits & 0xff00)
1019  devpriv->dacsr |= DT2821_HBOE;
1020  else
1021  devpriv->dacsr &= ~DT2821_HBOE;
1022 
1023  outw(devpriv->dacsr, dev->iobase + DT2821_DACSR);
1024 
1025  return 1;
1026 }
1027 
1028 static const struct comedi_lrange *const ai_range_table[] = {
1029  &range_dt282x_ai_lo_bipolar,
1030  &range_dt282x_ai_lo_unipolar,
1031  &range_dt282x_ai_5_bipolar,
1032  &range_dt282x_ai_5_unipolar
1033 };
1034 
1035 static const struct comedi_lrange *const ai_range_pgl_table[] = {
1036  &range_dt282x_ai_hi_bipolar,
1037  &range_dt282x_ai_hi_unipolar
1038 };
1039 
1040 static const struct comedi_lrange *opt_ai_range_lkup(int ispgl, int x)
1041 {
1042  if (ispgl) {
1043  if (x < 0 || x >= 2)
1044  x = 0;
1045  return ai_range_pgl_table[x];
1046  } else {
1047  if (x < 0 || x >= 4)
1048  x = 0;
1049  return ai_range_table[x];
1050  }
1051 }
1052 
1053 static const struct comedi_lrange *const ao_range_table[] = {
1054  &range_bipolar10,
1056  &range_bipolar5,
1057  &range_unipolar5,
1059 };
1060 
1061 static const struct comedi_lrange *opt_ao_range_lkup(int x)
1062 {
1063  if (x < 0 || x >= 5)
1064  x = 0;
1065  return ao_range_table[x];
1066 }
1067 
1068 enum { /* i/o base, irq, dma channels */
1070  opt_diff, /* differential */
1073 };
1074 
1075 static int dt282x_grab_dma(struct comedi_device *dev, int dma1, int dma2)
1076 {
1077  int ret;
1078 
1079  devpriv->usedma = 0;
1080 
1081  if (!dma1 && !dma2) {
1082  printk(KERN_ERR " (no dma)");
1083  return 0;
1084  }
1085 
1086  if (dma1 == dma2 || dma1 < 5 || dma2 < 5 || dma1 > 7 || dma2 > 7)
1087  return -EINVAL;
1088 
1089  if (dma2 < dma1) {
1090  int i;
1091  i = dma1;
1092  dma1 = dma2;
1093  dma2 = i;
1094  }
1095 
1096  ret = request_dma(dma1, "dt282x A");
1097  if (ret)
1098  return -EBUSY;
1099  devpriv->dma[0].chan = dma1;
1100 
1101  ret = request_dma(dma2, "dt282x B");
1102  if (ret)
1103  return -EBUSY;
1104  devpriv->dma[1].chan = dma2;
1105 
1106  devpriv->dma_maxsize = PAGE_SIZE;
1107  devpriv->dma[0].buf = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
1108  devpriv->dma[1].buf = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
1109  if (!devpriv->dma[0].buf || !devpriv->dma[1].buf) {
1110  printk(KERN_ERR " can't get DMA memory");
1111  return -ENOMEM;
1112  }
1113 
1114  printk(KERN_INFO " (dma=%d,%d)", dma1, dma2);
1115 
1116  devpriv->usedma = 1;
1117 
1118  return 0;
1119 }
1120 
1121 /*
1122  options:
1123  0 i/o base
1124  1 irq
1125  2 dma1
1126  3 dma2
1127  4 0=single ended, 1=differential
1128  5 ai 0=straight binary, 1=2's comp
1129  6 ao0 0=straight binary, 1=2's comp
1130  7 ao1 0=straight binary, 1=2's comp
1131  8 ai 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V
1132  9 ao0 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V, 4=±2.5 V
1133  10 ao1 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V, 4=±2.5 V
1134  */
1135 static int dt282x_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1136 {
1137  const struct dt282x_board *board = comedi_board(dev);
1138  int i, irq;
1139  int ret;
1140  struct comedi_subdevice *s;
1141  unsigned long iobase;
1142 
1143  dev->board_name = board->name;
1144 
1145  iobase = it->options[opt_iobase];
1146  if (!iobase)
1147  iobase = 0x240;
1148 
1149  printk(KERN_INFO "comedi%d: dt282x: 0x%04lx", dev->minor, iobase);
1150  if (!request_region(iobase, DT2821_SIZE, "dt282x")) {
1151  printk(KERN_INFO " I/O port conflict\n");
1152  return -EBUSY;
1153  }
1154  dev->iobase = iobase;
1155 
1157  i = inw(dev->iobase + DT2821_ADCSR);
1158 #ifdef DEBUG
1159  printk(KERN_DEBUG " fingerprint=%x,%x,%x,%x,%x",
1160  inw(dev->iobase + DT2821_ADCSR),
1161  inw(dev->iobase + DT2821_CHANCSR),
1162  inw(dev->iobase + DT2821_DACSR),
1163  inw(dev->iobase + DT2821_SUPCSR),
1164  inw(dev->iobase + DT2821_TMRCTR));
1165 #endif
1166 
1167  if (((inw(dev->iobase + DT2821_ADCSR) & DT2821_ADCSR_MASK)
1168  != DT2821_ADCSR_VAL) ||
1170  != DT2821_CHANCSR_VAL) ||
1172  != DT2821_DACSR_VAL) ||
1174  != DT2821_SUPCSR_VAL) ||
1176  != DT2821_TMRCTR_VAL)) {
1177  printk(KERN_ERR " board not found");
1178  return -EIO;
1179  }
1180  /* should do board test */
1181 
1182  irq = it->options[opt_irq];
1183 #if 0
1184  if (irq < 0) {
1185  unsigned long flags;
1186  int irqs;
1187 
1188  save_flags(flags);
1189  sti();
1190  irqs = probe_irq_on();
1191 
1192  /* trigger interrupt */
1193 
1194  udelay(100);
1195 
1196  irq = probe_irq_off(irqs);
1197  restore_flags(flags);
1198  if (0 /* error */)
1199  printk(KERN_ERR " error probing irq (bad)");
1200  }
1201 #endif
1202  if (irq > 0) {
1203  printk(KERN_INFO " ( irq = %d )", irq);
1204  ret = request_irq(irq, dt282x_interrupt, 0, "dt282x", dev);
1205  if (ret < 0) {
1206  printk(KERN_ERR " failed to get irq\n");
1207  return -EIO;
1208  }
1209  dev->irq = irq;
1210  } else if (irq == 0) {
1211  printk(KERN_INFO " (no irq)");
1212  } else {
1213 #if 0
1214  printk(KERN_INFO " (probe returned multiple irqs--bad)");
1215 #else
1216  printk(KERN_INFO " (irq probe not implemented)");
1217 #endif
1218  }
1219 
1220  ret = alloc_private(dev, sizeof(struct dt282x_private));
1221  if (ret < 0)
1222  return ret;
1223 
1224  ret = dt282x_grab_dma(dev, it->options[opt_dma1],
1225  it->options[opt_dma2]);
1226  if (ret < 0)
1227  return ret;
1228 
1229  ret = comedi_alloc_subdevices(dev, 3);
1230  if (ret)
1231  return ret;
1232 
1233  s = &dev->subdevices[0];
1234 
1235  dev->read_subdev = s;
1236  /* ai subdevice */
1237  s->type = COMEDI_SUBD_AI;
1239  ((it->options[opt_diff]) ? SDF_DIFF : SDF_COMMON);
1240  s->n_chan =
1241  (it->options[opt_diff]) ? boardtype.adchan_di : boardtype.adchan_se;
1242  s->insn_read = dt282x_ai_insn_read;
1243  s->do_cmdtest = dt282x_ai_cmdtest;
1244  s->do_cmd = dt282x_ai_cmd;
1245  s->cancel = dt282x_ai_cancel;
1246  s->maxdata = (1 << boardtype.adbits) - 1;
1247  s->len_chanlist = 16;
1248  s->range_table =
1249  opt_ai_range_lkup(boardtype.ispgl, it->options[opt_ai_range]);
1250  devpriv->ad_2scomp = it->options[opt_ai_twos];
1251 
1252  s = &dev->subdevices[1];
1253 
1254  s->n_chan = boardtype.dachan;
1255  if (s->n_chan) {
1256  /* ao subsystem */
1257  s->type = COMEDI_SUBD_AO;
1258  dev->write_subdev = s;
1260  s->insn_read = dt282x_ao_insn_read;
1261  s->insn_write = dt282x_ao_insn_write;
1262  s->do_cmdtest = dt282x_ao_cmdtest;
1263  s->do_cmd = dt282x_ao_cmd;
1264  s->cancel = dt282x_ao_cancel;
1265  s->maxdata = (1 << boardtype.dabits) - 1;
1266  s->len_chanlist = 2;
1267  s->range_table_list = devpriv->darangelist;
1268  devpriv->darangelist[0] =
1269  opt_ao_range_lkup(it->options[opt_ao0_range]);
1270  devpriv->darangelist[1] =
1271  opt_ao_range_lkup(it->options[opt_ao1_range]);
1272  devpriv->da0_2scomp = it->options[opt_ao0_twos];
1273  devpriv->da1_2scomp = it->options[opt_ao1_twos];
1274  } else {
1275  s->type = COMEDI_SUBD_UNUSED;
1276  }
1277 
1278  s = &dev->subdevices[2];
1279  /* dio subsystem */
1280  s->type = COMEDI_SUBD_DIO;
1282  s->n_chan = 16;
1283  s->insn_bits = dt282x_dio_insn_bits;
1284  s->insn_config = dt282x_dio_insn_config;
1285  s->maxdata = 1;
1286  s->range_table = &range_digital;
1287 
1288  printk(KERN_INFO "\n");
1289 
1290  return 0;
1291 }
1292 
1293 static void dt282x_detach(struct comedi_device *dev)
1294 {
1295  if (dev->irq)
1296  free_irq(dev->irq, dev);
1297  if (dev->iobase)
1299  if (dev->private) {
1300  if (devpriv->dma[0].chan)
1301  free_dma(devpriv->dma[0].chan);
1302  if (devpriv->dma[1].chan)
1303  free_dma(devpriv->dma[1].chan);
1304  if (devpriv->dma[0].buf)
1305  free_page((unsigned long)devpriv->dma[0].buf);
1306  if (devpriv->dma[1].buf)
1307  free_page((unsigned long)devpriv->dma[1].buf);
1308  }
1309 }
1310 
1311 static const struct dt282x_board boardtypes[] = {
1312  {
1313  .name = "dt2821",
1314  .adbits = 12,
1315  .adchan_se = 16,
1316  .adchan_di = 8,
1317  .ai_speed = 20000,
1318  .ispgl = 0,
1319  .dachan = 2,
1320  .dabits = 12,
1321  }, {
1322  .name = "dt2821-f",
1323  .adbits = 12,
1324  .adchan_se = 16,
1325  .adchan_di = 8,
1326  .ai_speed = 6500,
1327  .ispgl = 0,
1328  .dachan = 2,
1329  .dabits = 12,
1330  }, {
1331  .name = "dt2821-g",
1332  .adbits = 12,
1333  .adchan_se = 16,
1334  .adchan_di = 8,
1335  .ai_speed = 4000,
1336  .ispgl = 0,
1337  .dachan = 2,
1338  .dabits = 12,
1339  }, {
1340  .name = "dt2823",
1341  .adbits = 16,
1342  .adchan_se = 0,
1343  .adchan_di = 4,
1344  .ai_speed = 10000,
1345  .ispgl = 0,
1346  .dachan = 2,
1347  .dabits = 16,
1348  }, {
1349  .name = "dt2824-pgh",
1350  .adbits = 12,
1351  .adchan_se = 16,
1352  .adchan_di = 8,
1353  .ai_speed = 20000,
1354  .ispgl = 0,
1355  .dachan = 0,
1356  .dabits = 0,
1357  }, {
1358  .name = "dt2824-pgl",
1359  .adbits = 12,
1360  .adchan_se = 16,
1361  .adchan_di = 8,
1362  .ai_speed = 20000,
1363  .ispgl = 1,
1364  .dachan = 0,
1365  .dabits = 0,
1366  }, {
1367  .name = "dt2825",
1368  .adbits = 12,
1369  .adchan_se = 16,
1370  .adchan_di = 8,
1371  .ai_speed = 20000,
1372  .ispgl = 1,
1373  .dachan = 2,
1374  .dabits = 12,
1375  }, {
1376  .name = "dt2827",
1377  .adbits = 16,
1378  .adchan_se = 0,
1379  .adchan_di = 4,
1380  .ai_speed = 10000,
1381  .ispgl = 0,
1382  .dachan = 2,
1383  .dabits = 12,
1384  }, {
1385  .name = "dt2828",
1386  .adbits = 12,
1387  .adchan_se = 4,
1388  .adchan_di = 0,
1389  .ai_speed = 10000,
1390  .ispgl = 0,
1391  .dachan = 2,
1392  .dabits = 12,
1393  }, {
1394  .name = "dt2829",
1395  .adbits = 16,
1396  .adchan_se = 8,
1397  .adchan_di = 0,
1398  .ai_speed = 33250,
1399  .ispgl = 0,
1400  .dachan = 2,
1401  .dabits = 16,
1402  }, {
1403  .name = "dt21-ez",
1404  .adbits = 12,
1405  .adchan_se = 16,
1406  .adchan_di = 8,
1407  .ai_speed = 10000,
1408  .ispgl = 0,
1409  .dachan = 2,
1410  .dabits = 12,
1411  }, {
1412  .name = "dt23-ez",
1413  .adbits = 16,
1414  .adchan_se = 16,
1415  .adchan_di = 8,
1416  .ai_speed = 10000,
1417  .ispgl = 0,
1418  .dachan = 0,
1419  .dabits = 0,
1420  }, {
1421  .name = "dt24-ez",
1422  .adbits = 12,
1423  .adchan_se = 16,
1424  .adchan_di = 8,
1425  .ai_speed = 10000,
1426  .ispgl = 0,
1427  .dachan = 0,
1428  .dabits = 0,
1429  }, {
1430  .name = "dt24-ez-pgl",
1431  .adbits = 12,
1432  .adchan_se = 16,
1433  .adchan_di = 8,
1434  .ai_speed = 10000,
1435  .ispgl = 1,
1436  .dachan = 0,
1437  .dabits = 0,
1438  },
1439 };
1440 
1441 static struct comedi_driver dt282x_driver = {
1442  .driver_name = "dt282x",
1443  .module = THIS_MODULE,
1444  .attach = dt282x_attach,
1445  .detach = dt282x_detach,
1446  .board_name = &boardtypes[0].name,
1447  .num_names = ARRAY_SIZE(boardtypes),
1448  .offset = sizeof(struct dt282x_board),
1449 };
1450 module_comedi_driver(dt282x_driver);
1451 
1452 MODULE_AUTHOR("Comedi http://www.comedi.org");
1453 MODULE_DESCRIPTION("Comedi low-level driver");
1454 MODULE_LICENSE("GPL");