linux/drivers/staging/comedi/drivers/dt282x.c
<<
>>
Prefs
   1/*
   2 * dt282x.c
   3 * Comedi 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
  19/*
  20 * Driver: dt282x
  21 * Description: Data Translation DT2821 series (including DT-EZ)
  22 * Author: ds
  23 * Devices: [Data Translation] DT2821 (dt2821), DT2821-F-16SE (dt2821-f),
  24 *   DT2821-F-8DI (dt2821-f), DT2821-G-16SE (dt2821-g),
  25 *   DT2821-G-8DI (dt2821-g), DT2823 (dt2823), DT2824-PGH (dt2824-pgh),
  26 *   DT2824-PGL (dt2824-pgl), DT2825 (dt2825), DT2827 (dt2827),
  27 *   DT2828 (dt2828), DT2928 (dt2829), DT21-EZ (dt21-ez), DT23-EZ (dt23-ez),
  28 *   DT24-EZ (dt24-ez), DT24-EZ-PGL (dt24-ez-pgl)
  29 * Status: complete
  30 * Updated: Wed, 22 Aug 2001 17:11:34 -0700
  31 *
  32 * Configuration options:
  33 *   [0] - I/O port base address
  34 *   [1] - IRQ (optional, required for async command support)
  35 *   [2] - DMA 1 (optional, required for async command support)
  36 *   [3] - DMA 2 (optional, required for async command support)
  37 *   [4] - AI jumpered for 0=single ended, 1=differential
  38 *   [5] - AI jumpered for 0=straight binary, 1=2's complement
  39 *   [6] - AO 0 data format (deprecated, see below)
  40 *   [7] - AO 1 data format (deprecated, see below)
  41 *   [8] - AI jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5]
  42 *   [9] - AO channel 0 range (deprecated, see below)
  43 *   [10]- AO channel 1 range (deprecated, see below)
  44 *
  45 * Notes:
  46 *   - AO commands might be broken.
  47 *   - If you try to run a command on both the AI and AO subdevices
  48 *     simultaneously, bad things will happen.  The driver needs to
  49 *     be fixed to check for this situation and return an error.
  50 *   - AO range is not programmable. The AO subdevice has a range_table
  51 *     containing all the possible analog output ranges. Use the range
  52 *     that matches your board configuration to convert between data
  53 *     values and physical units. The format of the data written to the
  54 *     board is handled automatically based on the unipolar/bipolar
  55 *     range that is selected.
  56 */
  57
  58#include <linux/module.h>
  59#include <linux/delay.h>
  60#include <linux/gfp.h>
  61#include <linux/interrupt.h>
  62#include <linux/io.h>
  63
  64#include "../comedidev.h"
  65
  66#include "comedi_isadma.h"
  67
  68/*
  69 * Register map
  70 */
  71#define DT2821_ADCSR_REG                0x00
  72#define DT2821_ADCSR_ADERR              BIT(15)
  73#define DT2821_ADCSR_ADCLK              BIT(9)
  74#define DT2821_ADCSR_MUXBUSY            BIT(8)
  75#define DT2821_ADCSR_ADDONE             BIT(7)
  76#define DT2821_ADCSR_IADDONE            BIT(6)
  77#define DT2821_ADCSR_GS(x)              (((x) & 0x3) << 4)
  78#define DT2821_ADCSR_CHAN(x)            (((x) & 0xf) << 0)
  79#define DT2821_CHANCSR_REG              0x02
  80#define DT2821_CHANCSR_LLE              BIT(15)
  81#define DT2821_CHANCSR_TO_PRESLA(x)     (((x) >> 8) & 0xf)
  82#define DT2821_CHANCSR_NUMB(x)          ((((x) - 1) & 0xf) << 0)
  83#define DT2821_ADDAT_REG                0x04
  84#define DT2821_DACSR_REG                0x06
  85#define DT2821_DACSR_DAERR              BIT(15)
  86#define DT2821_DACSR_YSEL(x)            ((x) << 9)
  87#define DT2821_DACSR_SSEL               BIT(8)
  88#define DT2821_DACSR_DACRDY             BIT(7)
  89#define DT2821_DACSR_IDARDY             BIT(6)
  90#define DT2821_DACSR_DACLK              BIT(5)
  91#define DT2821_DACSR_HBOE               BIT(1)
  92#define DT2821_DACSR_LBOE               BIT(0)
  93#define DT2821_DADAT_REG                0x08
  94#define DT2821_DIODAT_REG               0x0a
  95#define DT2821_SUPCSR_REG               0x0c
  96#define DT2821_SUPCSR_DMAD              BIT(15)
  97#define DT2821_SUPCSR_ERRINTEN          BIT(14)
  98#define DT2821_SUPCSR_CLRDMADNE         BIT(13)
  99#define DT2821_SUPCSR_DDMA              BIT(12)
 100#define DT2821_SUPCSR_DS(x)             (((x) & 0x3) << 10)
 101#define DT2821_SUPCSR_DS_PIO            DT2821_SUPCSR_DS(0)
 102#define DT2821_SUPCSR_DS_AD_CLK         DT2821_SUPCSR_DS(1)
 103#define DT2821_SUPCSR_DS_DA_CLK         DT2821_SUPCSR_DS(2)
 104#define DT2821_SUPCSR_DS_AD_TRIG        DT2821_SUPCSR_DS(3)
 105#define DT2821_SUPCSR_BUFFB             BIT(9)
 106#define DT2821_SUPCSR_SCDN              BIT(8)
 107#define DT2821_SUPCSR_DACON             BIT(7)
 108#define DT2821_SUPCSR_ADCINIT           BIT(6)
 109#define DT2821_SUPCSR_DACINIT           BIT(5)
 110#define DT2821_SUPCSR_PRLD              BIT(4)
 111#define DT2821_SUPCSR_STRIG             BIT(3)
 112#define DT2821_SUPCSR_XTRIG             BIT(2)
 113#define DT2821_SUPCSR_XCLK              BIT(1)
 114#define DT2821_SUPCSR_BDINIT            BIT(0)
 115#define DT2821_TMRCTR_REG               0x0e
 116#define DT2821_TMRCTR_PRESCALE(x)       (((x) & 0xf) << 8)
 117#define DT2821_TMRCTR_DIVIDER(x)        ((255 - ((x) & 0xff)) << 0)
 118
 119/* Pacer Clock */
 120#define DT2821_OSC_BASE         250     /* 4 MHz (in nanoseconds) */
 121#define DT2821_PRESCALE(x)      BIT(x)
 122#define DT2821_PRESCALE_MAX     15
 123#define DT2821_DIVIDER_MAX      255
 124#define DT2821_OSC_MAX          (DT2821_OSC_BASE *                      \
 125                                 DT2821_PRESCALE(DT2821_PRESCALE_MAX) * \
 126                                 DT2821_DIVIDER_MAX)
 127
 128static const struct comedi_lrange range_dt282x_ai_lo_bipolar = {
 129        4, {
 130                BIP_RANGE(10),
 131                BIP_RANGE(5),
 132                BIP_RANGE(2.5),
 133                BIP_RANGE(1.25)
 134        }
 135};
 136
 137static const struct comedi_lrange range_dt282x_ai_lo_unipolar = {
 138        4, {
 139                UNI_RANGE(10),
 140                UNI_RANGE(5),
 141                UNI_RANGE(2.5),
 142                UNI_RANGE(1.25)
 143        }
 144};
 145
 146static const struct comedi_lrange range_dt282x_ai_5_bipolar = {
 147        4, {
 148                BIP_RANGE(5),
 149                BIP_RANGE(2.5),
 150                BIP_RANGE(1.25),
 151                BIP_RANGE(0.625)
 152        }
 153};
 154
 155static const struct comedi_lrange range_dt282x_ai_5_unipolar = {
 156        4, {
 157                UNI_RANGE(5),
 158                UNI_RANGE(2.5),
 159                UNI_RANGE(1.25),
 160                UNI_RANGE(0.625)
 161        }
 162};
 163
 164static const struct comedi_lrange range_dt282x_ai_hi_bipolar = {
 165        4, {
 166                BIP_RANGE(10),
 167                BIP_RANGE(1),
 168                BIP_RANGE(0.1),
 169                BIP_RANGE(0.02)
 170        }
 171};
 172
 173static const struct comedi_lrange range_dt282x_ai_hi_unipolar = {
 174        4, {
 175                UNI_RANGE(10),
 176                UNI_RANGE(1),
 177                UNI_RANGE(0.1),
 178                UNI_RANGE(0.02)
 179        }
 180};
 181
 182/*
 183 * The Analog Output range is set per-channel using jumpers on the board.
 184 * All of these ranges may not be available on some DT2821 series boards.
 185 * The default jumper setting has both channels set for +/-10V output.
 186 */
 187static const struct comedi_lrange dt282x_ao_range = {
 188        5, {
 189                BIP_RANGE(10),
 190                BIP_RANGE(5),
 191                BIP_RANGE(2.5),
 192                UNI_RANGE(10),
 193                UNI_RANGE(5),
 194        }
 195};
 196
 197struct dt282x_board {
 198        const char *name;
 199        unsigned int ai_maxdata;
 200        int adchan_se;
 201        int adchan_di;
 202        int ai_speed;
 203        int ispgl;
 204        int dachan;
 205        unsigned int ao_maxdata;
 206};
 207
 208static const struct dt282x_board boardtypes[] = {
 209        {
 210                .name           = "dt2821",
 211                .ai_maxdata     = 0x0fff,
 212                .adchan_se      = 16,
 213                .adchan_di      = 8,
 214                .ai_speed       = 20000,
 215                .dachan         = 2,
 216                .ao_maxdata     = 0x0fff,
 217        }, {
 218                .name           = "dt2821-f",
 219                .ai_maxdata     = 0x0fff,
 220                .adchan_se      = 16,
 221                .adchan_di      = 8,
 222                .ai_speed       = 6500,
 223                .dachan         = 2,
 224                .ao_maxdata     = 0x0fff,
 225        }, {
 226                .name           = "dt2821-g",
 227                .ai_maxdata     = 0x0fff,
 228                .adchan_se      = 16,
 229                .adchan_di      = 8,
 230                .ai_speed       = 4000,
 231                .dachan         = 2,
 232                .ao_maxdata     = 0x0fff,
 233        }, {
 234                .name           = "dt2823",
 235                .ai_maxdata     = 0xffff,
 236                .adchan_di      = 4,
 237                .ai_speed       = 10000,
 238                .dachan         = 2,
 239                .ao_maxdata     = 0xffff,
 240        }, {
 241                .name           = "dt2824-pgh",
 242                .ai_maxdata     = 0x0fff,
 243                .adchan_se      = 16,
 244                .adchan_di      = 8,
 245                .ai_speed       = 20000,
 246        }, {
 247                .name           = "dt2824-pgl",
 248                .ai_maxdata     = 0x0fff,
 249                .adchan_se      = 16,
 250                .adchan_di      = 8,
 251                .ai_speed       = 20000,
 252                .ispgl          = 1,
 253        }, {
 254                .name           = "dt2825",
 255                .ai_maxdata     = 0x0fff,
 256                .adchan_se      = 16,
 257                .adchan_di      = 8,
 258                .ai_speed       = 20000,
 259                .ispgl          = 1,
 260                .dachan         = 2,
 261                .ao_maxdata     = 0x0fff,
 262        }, {
 263                .name           = "dt2827",
 264                .ai_maxdata     = 0xffff,
 265                .adchan_di      = 4,
 266                .ai_speed       = 10000,
 267                .dachan         = 2,
 268                .ao_maxdata     = 0x0fff,
 269        }, {
 270                .name           = "dt2828",
 271                .ai_maxdata     = 0x0fff,
 272                .adchan_se      = 4,
 273                .ai_speed       = 10000,
 274                .dachan         = 2,
 275                .ao_maxdata     = 0x0fff,
 276        }, {
 277                .name           = "dt2829",
 278                .ai_maxdata     = 0xffff,
 279                .adchan_se      = 8,
 280                .ai_speed       = 33250,
 281                .dachan         = 2,
 282                .ao_maxdata     = 0xffff,
 283        }, {
 284                .name           = "dt21-ez",
 285                .ai_maxdata     = 0x0fff,
 286                .adchan_se      = 16,
 287                .adchan_di      = 8,
 288                .ai_speed       = 10000,
 289                .dachan         = 2,
 290                .ao_maxdata     = 0x0fff,
 291        }, {
 292                .name           = "dt23-ez",
 293                .ai_maxdata     = 0xffff,
 294                .adchan_se      = 16,
 295                .adchan_di      = 8,
 296                .ai_speed       = 10000,
 297        }, {
 298                .name           = "dt24-ez",
 299                .ai_maxdata     = 0x0fff,
 300                .adchan_se      = 16,
 301                .adchan_di      = 8,
 302                .ai_speed       = 10000,
 303        }, {
 304                .name           = "dt24-ez-pgl",
 305                .ai_maxdata     = 0x0fff,
 306                .adchan_se      = 16,
 307                .adchan_di      = 8,
 308                .ai_speed       = 10000,
 309                .ispgl          = 1,
 310        },
 311};
 312
 313struct dt282x_private {
 314        struct comedi_isadma *dma;
 315        unsigned int ad_2scomp:1;
 316        unsigned int divisor;
 317        int dacsr;      /* software copies of registers */
 318        int adcsr;
 319        int supcsr;
 320        int ntrig;
 321        int nread;
 322        int dma_dir;
 323};
 324
 325static int dt282x_prep_ai_dma(struct comedi_device *dev, int dma_index, int n)
 326{
 327        struct dt282x_private *devpriv = dev->private;
 328        struct comedi_isadma *dma = devpriv->dma;
 329        struct comedi_isadma_desc *desc = &dma->desc[dma_index];
 330
 331        if (!devpriv->ntrig)
 332                return 0;
 333
 334        if (n == 0)
 335                n = desc->maxsize;
 336        if (n > devpriv->ntrig * 2)
 337                n = devpriv->ntrig * 2;
 338        devpriv->ntrig -= n / 2;
 339
 340        desc->size = n;
 341        comedi_isadma_set_mode(desc, devpriv->dma_dir);
 342
 343        comedi_isadma_program(desc);
 344
 345        return n;
 346}
 347
 348static int dt282x_prep_ao_dma(struct comedi_device *dev, int dma_index, int n)
 349{
 350        struct dt282x_private *devpriv = dev->private;
 351        struct comedi_isadma *dma = devpriv->dma;
 352        struct comedi_isadma_desc *desc = &dma->desc[dma_index];
 353
 354        desc->size = n;
 355        comedi_isadma_set_mode(desc, devpriv->dma_dir);
 356
 357        comedi_isadma_program(desc);
 358
 359        return n;
 360}
 361
 362static void dt282x_disable_dma(struct comedi_device *dev)
 363{
 364        struct dt282x_private *devpriv = dev->private;
 365        struct comedi_isadma *dma = devpriv->dma;
 366        struct comedi_isadma_desc *desc;
 367        int i;
 368
 369        for (i = 0; i < 2; i++) {
 370                desc = &dma->desc[i];
 371                comedi_isadma_disable(desc->chan);
 372        }
 373}
 374
 375static unsigned int dt282x_ns_to_timer(unsigned int *ns, unsigned int flags)
 376{
 377        unsigned int prescale, base, divider;
 378
 379        for (prescale = 0; prescale <= DT2821_PRESCALE_MAX; prescale++) {
 380                if (prescale == 1)      /* 0 and 1 are both divide by 1 */
 381                        continue;
 382                base = DT2821_OSC_BASE * DT2821_PRESCALE(prescale);
 383                switch (flags & CMDF_ROUND_MASK) {
 384                case CMDF_ROUND_NEAREST:
 385                default:
 386                        divider = DIV_ROUND_CLOSEST(*ns, base);
 387                        break;
 388                case CMDF_ROUND_DOWN:
 389                        divider = (*ns) / base;
 390                        break;
 391                case CMDF_ROUND_UP:
 392                        divider = DIV_ROUND_UP(*ns, base);
 393                        break;
 394                }
 395                if (divider <= DT2821_DIVIDER_MAX)
 396                        break;
 397        }
 398        if (divider > DT2821_DIVIDER_MAX) {
 399                prescale = DT2821_PRESCALE_MAX;
 400                divider = DT2821_DIVIDER_MAX;
 401                base = DT2821_OSC_BASE * DT2821_PRESCALE(prescale);
 402        }
 403        *ns = divider * base;
 404        return DT2821_TMRCTR_PRESCALE(prescale) |
 405               DT2821_TMRCTR_DIVIDER(divider);
 406}
 407
 408static void dt282x_munge(struct comedi_device *dev,
 409                         struct comedi_subdevice *s,
 410                         unsigned short *buf,
 411                         unsigned int nbytes)
 412{
 413        struct dt282x_private *devpriv = dev->private;
 414        unsigned int val;
 415        int i;
 416
 417        if (nbytes % 2)
 418                dev_err(dev->class_dev,
 419                        "bug! odd number of bytes from dma xfer\n");
 420
 421        for (i = 0; i < nbytes / 2; i++) {
 422                val = buf[i];
 423                val &= s->maxdata;
 424                if (devpriv->ad_2scomp)
 425                        val = comedi_offset_munge(s, val);
 426
 427                buf[i] = val;
 428        }
 429}
 430
 431static unsigned int dt282x_ao_setup_dma(struct comedi_device *dev,
 432                                        struct comedi_subdevice *s,
 433                                        int cur_dma)
 434{
 435        struct dt282x_private *devpriv = dev->private;
 436        struct comedi_isadma *dma = devpriv->dma;
 437        struct comedi_isadma_desc *desc = &dma->desc[cur_dma];
 438        unsigned int nsamples = comedi_bytes_to_samples(s, desc->maxsize);
 439        unsigned int nbytes;
 440
 441        nbytes = comedi_buf_read_samples(s, desc->virt_addr, nsamples);
 442        if (nbytes)
 443                dt282x_prep_ao_dma(dev, cur_dma, nbytes);
 444        else
 445                dev_err(dev->class_dev, "AO underrun\n");
 446
 447        return nbytes;
 448}
 449
 450static void dt282x_ao_dma_interrupt(struct comedi_device *dev,
 451                                    struct comedi_subdevice *s)
 452{
 453        struct dt282x_private *devpriv = dev->private;
 454        struct comedi_isadma *dma = devpriv->dma;
 455        struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
 456
 457        outw(devpriv->supcsr | DT2821_SUPCSR_CLRDMADNE,
 458             dev->iobase + DT2821_SUPCSR_REG);
 459
 460        comedi_isadma_disable(desc->chan);
 461
 462        if (!dt282x_ao_setup_dma(dev, s, dma->cur_dma))
 463                s->async->events |= COMEDI_CB_OVERFLOW;
 464
 465        dma->cur_dma = 1 - dma->cur_dma;
 466}
 467
 468static void dt282x_ai_dma_interrupt(struct comedi_device *dev,
 469                                    struct comedi_subdevice *s)
 470{
 471        struct dt282x_private *devpriv = dev->private;
 472        struct comedi_isadma *dma = devpriv->dma;
 473        struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
 474        unsigned int nsamples = comedi_bytes_to_samples(s, desc->size);
 475        int ret;
 476
 477        outw(devpriv->supcsr | DT2821_SUPCSR_CLRDMADNE,
 478             dev->iobase + DT2821_SUPCSR_REG);
 479
 480        comedi_isadma_disable(desc->chan);
 481
 482        dt282x_munge(dev, s, desc->virt_addr, desc->size);
 483        ret = comedi_buf_write_samples(s, desc->virt_addr, nsamples);
 484        if (ret != desc->size)
 485                return;
 486
 487        devpriv->nread -= nsamples;
 488        if (devpriv->nread < 0) {
 489                dev_info(dev->class_dev, "nread off by one\n");
 490                devpriv->nread = 0;
 491        }
 492        if (!devpriv->nread) {
 493                s->async->events |= COMEDI_CB_EOA;
 494                return;
 495        }
 496#if 0
 497        /* clear the dual dma flag, making this the last dma segment */
 498        /* XXX probably wrong */
 499        if (!devpriv->ntrig) {
 500                devpriv->supcsr &= ~DT2821_SUPCSR_DDMA;
 501                outw(devpriv->supcsr, dev->iobase + DT2821_SUPCSR_REG);
 502        }
 503#endif
 504        /* restart the channel */
 505        dt282x_prep_ai_dma(dev, dma->cur_dma, 0);
 506
 507        dma->cur_dma = 1 - dma->cur_dma;
 508}
 509
 510static irqreturn_t dt282x_interrupt(int irq, void *d)
 511{
 512        struct comedi_device *dev = d;
 513        struct dt282x_private *devpriv = dev->private;
 514        struct comedi_subdevice *s = dev->read_subdev;
 515        struct comedi_subdevice *s_ao = dev->write_subdev;
 516        unsigned int supcsr, adcsr, dacsr;
 517        int handled = 0;
 518
 519        if (!dev->attached) {
 520                dev_err(dev->class_dev, "spurious interrupt\n");
 521                return IRQ_HANDLED;
 522        }
 523
 524        adcsr = inw(dev->iobase + DT2821_ADCSR_REG);
 525        dacsr = inw(dev->iobase + DT2821_DACSR_REG);
 526        supcsr = inw(dev->iobase + DT2821_SUPCSR_REG);
 527        if (supcsr & DT2821_SUPCSR_DMAD) {
 528                if (devpriv->dma_dir == COMEDI_ISADMA_READ)
 529                        dt282x_ai_dma_interrupt(dev, s);
 530                else
 531                        dt282x_ao_dma_interrupt(dev, s_ao);
 532                handled = 1;
 533        }
 534        if (adcsr & DT2821_ADCSR_ADERR) {
 535                if (devpriv->nread != 0) {
 536                        dev_err(dev->class_dev, "A/D error\n");
 537                        s->async->events |= COMEDI_CB_ERROR;
 538                }
 539                handled = 1;
 540        }
 541        if (dacsr & DT2821_DACSR_DAERR) {
 542                dev_err(dev->class_dev, "D/A error\n");
 543                s_ao->async->events |= COMEDI_CB_ERROR;
 544                handled = 1;
 545        }
 546#if 0
 547        if (adcsr & DT2821_ADCSR_ADDONE) {
 548                unsigned short data;
 549
 550                data = inw(dev->iobase + DT2821_ADDAT_REG);
 551                data &= s->maxdata;
 552                if (devpriv->ad_2scomp)
 553                        data = comedi_offset_munge(s, data);
 554
 555                comedi_buf_write_samples(s, &data, 1);
 556
 557                devpriv->nread--;
 558                if (!devpriv->nread) {
 559                        s->async->events |= COMEDI_CB_EOA;
 560                } else {
 561                        if (supcsr & DT2821_SUPCSR_SCDN)
 562                                outw(devpriv->supcsr | DT2821_SUPCSR_STRIG,
 563                                     dev->iobase + DT2821_SUPCSR_REG);
 564                }
 565                handled = 1;
 566        }
 567#endif
 568        comedi_handle_events(dev, s);
 569        comedi_handle_events(dev, s_ao);
 570
 571        return IRQ_RETVAL(handled);
 572}
 573
 574static void dt282x_load_changain(struct comedi_device *dev, int n,
 575                                 unsigned int *chanlist)
 576{
 577        struct dt282x_private *devpriv = dev->private;
 578        int i;
 579
 580        outw(DT2821_CHANCSR_LLE | DT2821_CHANCSR_NUMB(n),
 581             dev->iobase + DT2821_CHANCSR_REG);
 582        for (i = 0; i < n; i++) {
 583                unsigned int chan = CR_CHAN(chanlist[i]);
 584                unsigned int range = CR_RANGE(chanlist[i]);
 585
 586                outw(devpriv->adcsr |
 587                     DT2821_ADCSR_GS(range) |
 588                     DT2821_ADCSR_CHAN(chan),
 589                     dev->iobase + DT2821_ADCSR_REG);
 590        }
 591        outw(DT2821_CHANCSR_NUMB(n), dev->iobase + DT2821_CHANCSR_REG);
 592}
 593
 594static int dt282x_ai_timeout(struct comedi_device *dev,
 595                             struct comedi_subdevice *s,
 596                             struct comedi_insn *insn,
 597                             unsigned long context)
 598{
 599        unsigned int status;
 600
 601        status = inw(dev->iobase + DT2821_ADCSR_REG);
 602        switch (context) {
 603        case DT2821_ADCSR_MUXBUSY:
 604                if ((status & DT2821_ADCSR_MUXBUSY) == 0)
 605                        return 0;
 606                break;
 607        case DT2821_ADCSR_ADDONE:
 608                if (status & DT2821_ADCSR_ADDONE)
 609                        return 0;
 610                break;
 611        default:
 612                return -EINVAL;
 613        }
 614        return -EBUSY;
 615}
 616
 617/*
 618 *    Performs a single A/D conversion.
 619 *      - Put channel/gain into channel-gain list
 620 *      - preload multiplexer
 621 *      - trigger conversion and wait for it to finish
 622 */
 623static int dt282x_ai_insn_read(struct comedi_device *dev,
 624                               struct comedi_subdevice *s,
 625                               struct comedi_insn *insn,
 626                               unsigned int *data)
 627{
 628        struct dt282x_private *devpriv = dev->private;
 629        unsigned int val;
 630        int ret;
 631        int i;
 632
 633        /* XXX should we really be enabling the ad clock here? */
 634        devpriv->adcsr = DT2821_ADCSR_ADCLK;
 635        outw(devpriv->adcsr, dev->iobase + DT2821_ADCSR_REG);
 636
 637        dt282x_load_changain(dev, 1, &insn->chanspec);
 638
 639        outw(devpriv->supcsr | DT2821_SUPCSR_PRLD,
 640             dev->iobase + DT2821_SUPCSR_REG);
 641        ret = comedi_timeout(dev, s, insn,
 642                             dt282x_ai_timeout, DT2821_ADCSR_MUXBUSY);
 643        if (ret)
 644                return ret;
 645
 646        for (i = 0; i < insn->n; i++) {
 647                outw(devpriv->supcsr | DT2821_SUPCSR_STRIG,
 648                     dev->iobase + DT2821_SUPCSR_REG);
 649
 650                ret = comedi_timeout(dev, s, insn,
 651                                     dt282x_ai_timeout, DT2821_ADCSR_ADDONE);
 652                if (ret)
 653                        return ret;
 654
 655                val = inw(dev->iobase + DT2821_ADDAT_REG);
 656                val &= s->maxdata;
 657                if (devpriv->ad_2scomp)
 658                        val = comedi_offset_munge(s, val);
 659
 660                data[i] = val;
 661        }
 662
 663        return i;
 664}
 665
 666static int dt282x_ai_cmdtest(struct comedi_device *dev,
 667                             struct comedi_subdevice *s,
 668                             struct comedi_cmd *cmd)
 669{
 670        const struct dt282x_board *board = dev->board_ptr;
 671        struct dt282x_private *devpriv = dev->private;
 672        int err = 0;
 673        unsigned int arg;
 674
 675        /* Step 1 : check if triggers are trivially valid */
 676
 677        err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
 678        err |= comedi_check_trigger_src(&cmd->scan_begin_src,
 679                                        TRIG_FOLLOW | TRIG_EXT);
 680        err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
 681        err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 682        err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
 683
 684        if (err)
 685                return 1;
 686
 687        /* Step 2a : make sure trigger sources are unique */
 688
 689        err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
 690        err |= comedi_check_trigger_is_unique(cmd->stop_src);
 691
 692        /* Step 2b : and mutually compatible */
 693
 694        if (err)
 695                return 2;
 696
 697        /* Step 3: check if arguments are trivially valid */
 698
 699        err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 700        err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 701        err |= comedi_check_trigger_arg_max(&cmd->convert_arg, DT2821_OSC_MAX);
 702        err |= comedi_check_trigger_arg_min(&cmd->convert_arg, board->ai_speed);
 703        err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 704                                           cmd->chanlist_len);
 705
 706        if (cmd->stop_src == TRIG_COUNT)
 707                err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
 708        else    /* TRIG_EXT | TRIG_NONE */
 709                err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 710
 711        if (err)
 712                return 3;
 713
 714        /* step 4: fix up any arguments */
 715
 716        arg = cmd->convert_arg;
 717        devpriv->divisor = dt282x_ns_to_timer(&arg, cmd->flags);
 718        err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
 719
 720        if (err)
 721                return 4;
 722
 723        return 0;
 724}
 725
 726static int dt282x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 727{
 728        struct dt282x_private *devpriv = dev->private;
 729        struct comedi_isadma *dma = devpriv->dma;
 730        struct comedi_cmd *cmd = &s->async->cmd;
 731        int ret;
 732
 733        dt282x_disable_dma(dev);
 734
 735        outw(devpriv->divisor, dev->iobase + DT2821_TMRCTR_REG);
 736
 737        devpriv->supcsr = DT2821_SUPCSR_ERRINTEN;
 738        if (cmd->scan_begin_src == TRIG_FOLLOW)
 739                devpriv->supcsr = DT2821_SUPCSR_DS_AD_CLK;
 740        else
 741                devpriv->supcsr = DT2821_SUPCSR_DS_AD_TRIG;
 742        outw(devpriv->supcsr |
 743             DT2821_SUPCSR_CLRDMADNE |
 744             DT2821_SUPCSR_BUFFB |
 745             DT2821_SUPCSR_ADCINIT,
 746             dev->iobase + DT2821_SUPCSR_REG);
 747
 748        devpriv->ntrig = cmd->stop_arg * cmd->scan_end_arg;
 749        devpriv->nread = devpriv->ntrig;
 750
 751        devpriv->dma_dir = COMEDI_ISADMA_READ;
 752        dma->cur_dma = 0;
 753        dt282x_prep_ai_dma(dev, 0, 0);
 754        if (devpriv->ntrig) {
 755                dt282x_prep_ai_dma(dev, 1, 0);
 756                devpriv->supcsr |= DT2821_SUPCSR_DDMA;
 757                outw(devpriv->supcsr, dev->iobase + DT2821_SUPCSR_REG);
 758        }
 759
 760        devpriv->adcsr = 0;
 761
 762        dt282x_load_changain(dev, cmd->chanlist_len, cmd->chanlist);
 763
 764        devpriv->adcsr = DT2821_ADCSR_ADCLK | DT2821_ADCSR_IADDONE;
 765        outw(devpriv->adcsr, dev->iobase + DT2821_ADCSR_REG);
 766
 767        outw(devpriv->supcsr | DT2821_SUPCSR_PRLD,
 768             dev->iobase + DT2821_SUPCSR_REG);
 769        ret = comedi_timeout(dev, s, NULL,
 770                             dt282x_ai_timeout, DT2821_ADCSR_MUXBUSY);
 771        if (ret)
 772                return ret;
 773
 774        if (cmd->scan_begin_src == TRIG_FOLLOW) {
 775                outw(devpriv->supcsr | DT2821_SUPCSR_STRIG,
 776                     dev->iobase + DT2821_SUPCSR_REG);
 777        } else {
 778                devpriv->supcsr |= DT2821_SUPCSR_XTRIG;
 779                outw(devpriv->supcsr, dev->iobase + DT2821_SUPCSR_REG);
 780        }
 781
 782        return 0;
 783}
 784
 785static int dt282x_ai_cancel(struct comedi_device *dev,
 786                            struct comedi_subdevice *s)
 787{
 788        struct dt282x_private *devpriv = dev->private;
 789
 790        dt282x_disable_dma(dev);
 791
 792        devpriv->adcsr = 0;
 793        outw(devpriv->adcsr, dev->iobase + DT2821_ADCSR_REG);
 794
 795        devpriv->supcsr = 0;
 796        outw(devpriv->supcsr | DT2821_SUPCSR_ADCINIT,
 797             dev->iobase + DT2821_SUPCSR_REG);
 798
 799        return 0;
 800}
 801
 802static int dt282x_ao_insn_write(struct comedi_device *dev,
 803                                struct comedi_subdevice *s,
 804                                struct comedi_insn *insn,
 805                                unsigned int *data)
 806{
 807        struct dt282x_private *devpriv = dev->private;
 808        unsigned int chan = CR_CHAN(insn->chanspec);
 809        unsigned int range = CR_RANGE(insn->chanspec);
 810        int i;
 811
 812        devpriv->dacsr |= DT2821_DACSR_SSEL | DT2821_DACSR_YSEL(chan);
 813
 814        for (i = 0; i < insn->n; i++) {
 815                unsigned int val = data[i];
 816
 817                s->readback[chan] = val;
 818
 819                if (comedi_range_is_bipolar(s, range))
 820                        val = comedi_offset_munge(s, val);
 821
 822                outw(devpriv->dacsr, dev->iobase + DT2821_DACSR_REG);
 823
 824                outw(val, dev->iobase + DT2821_DADAT_REG);
 825
 826                outw(devpriv->supcsr | DT2821_SUPCSR_DACON,
 827                     dev->iobase + DT2821_SUPCSR_REG);
 828        }
 829
 830        return insn->n;
 831}
 832
 833static int dt282x_ao_cmdtest(struct comedi_device *dev,
 834                             struct comedi_subdevice *s,
 835                             struct comedi_cmd *cmd)
 836{
 837        struct dt282x_private *devpriv = dev->private;
 838        int err = 0;
 839        unsigned int arg;
 840
 841        /* Step 1 : check if triggers are trivially valid */
 842
 843        err |= comedi_check_trigger_src(&cmd->start_src, TRIG_INT);
 844        err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
 845        err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
 846        err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 847        err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
 848
 849        if (err)
 850                return 1;
 851
 852        /* Step 2a : make sure trigger sources are unique */
 853
 854        err |= comedi_check_trigger_is_unique(cmd->stop_src);
 855
 856        /* Step 2b : and mutually compatible */
 857
 858        if (err)
 859                return 2;
 860
 861        /* Step 3: check if arguments are trivially valid */
 862
 863        err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 864        err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, 5000);
 865        err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
 866        err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 867                                           cmd->chanlist_len);
 868
 869        if (cmd->stop_src == TRIG_COUNT)
 870                err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
 871        else    /* TRIG_EXT | TRIG_NONE */
 872                err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 873
 874        if (err)
 875                return 3;
 876
 877        /* step 4: fix up any arguments */
 878
 879        arg = cmd->scan_begin_arg;
 880        devpriv->divisor = dt282x_ns_to_timer(&arg, cmd->flags);
 881        err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
 882
 883        if (err)
 884                return 4;
 885
 886        return 0;
 887}
 888
 889static int dt282x_ao_inttrig(struct comedi_device *dev,
 890                             struct comedi_subdevice *s,
 891                             unsigned int trig_num)
 892{
 893        struct dt282x_private *devpriv = dev->private;
 894        struct comedi_cmd *cmd = &s->async->cmd;
 895
 896        if (trig_num != cmd->start_src)
 897                return -EINVAL;
 898
 899        if (!dt282x_ao_setup_dma(dev, s, 0))
 900                return -EPIPE;
 901
 902        if (!dt282x_ao_setup_dma(dev, s, 1))
 903                return -EPIPE;
 904
 905        outw(devpriv->supcsr | DT2821_SUPCSR_STRIG,
 906             dev->iobase + DT2821_SUPCSR_REG);
 907        s->async->inttrig = NULL;
 908
 909        return 1;
 910}
 911
 912static int dt282x_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 913{
 914        struct dt282x_private *devpriv = dev->private;
 915        struct comedi_isadma *dma = devpriv->dma;
 916        struct comedi_cmd *cmd = &s->async->cmd;
 917
 918        dt282x_disable_dma(dev);
 919
 920        devpriv->supcsr = DT2821_SUPCSR_ERRINTEN |
 921                          DT2821_SUPCSR_DS_DA_CLK |
 922                          DT2821_SUPCSR_DDMA;
 923        outw(devpriv->supcsr |
 924             DT2821_SUPCSR_CLRDMADNE |
 925             DT2821_SUPCSR_BUFFB |
 926             DT2821_SUPCSR_DACINIT,
 927             dev->iobase + DT2821_SUPCSR_REG);
 928
 929        devpriv->ntrig = cmd->stop_arg * cmd->chanlist_len;
 930        devpriv->nread = devpriv->ntrig;
 931
 932        devpriv->dma_dir = COMEDI_ISADMA_WRITE;
 933        dma->cur_dma = 0;
 934
 935        outw(devpriv->divisor, dev->iobase + DT2821_TMRCTR_REG);
 936
 937        /* clear all bits but the DIO direction bits */
 938        devpriv->dacsr &= (DT2821_DACSR_LBOE | DT2821_DACSR_HBOE);
 939
 940        devpriv->dacsr |= (DT2821_DACSR_SSEL |
 941                           DT2821_DACSR_DACLK |
 942                           DT2821_DACSR_IDARDY);
 943        outw(devpriv->dacsr, dev->iobase + DT2821_DACSR_REG);
 944
 945        s->async->inttrig = dt282x_ao_inttrig;
 946
 947        return 0;
 948}
 949
 950static int dt282x_ao_cancel(struct comedi_device *dev,
 951                            struct comedi_subdevice *s)
 952{
 953        struct dt282x_private *devpriv = dev->private;
 954
 955        dt282x_disable_dma(dev);
 956
 957        /* clear all bits but the DIO direction bits */
 958        devpriv->dacsr &= (DT2821_DACSR_LBOE | DT2821_DACSR_HBOE);
 959
 960        outw(devpriv->dacsr, dev->iobase + DT2821_DACSR_REG);
 961
 962        devpriv->supcsr = 0;
 963        outw(devpriv->supcsr | DT2821_SUPCSR_DACINIT,
 964             dev->iobase + DT2821_SUPCSR_REG);
 965
 966        return 0;
 967}
 968
 969static int dt282x_dio_insn_bits(struct comedi_device *dev,
 970                                struct comedi_subdevice *s,
 971                                struct comedi_insn *insn,
 972                                unsigned int *data)
 973{
 974        if (comedi_dio_update_state(s, data))
 975                outw(s->state, dev->iobase + DT2821_DIODAT_REG);
 976
 977        data[1] = inw(dev->iobase + DT2821_DIODAT_REG);
 978
 979        return insn->n;
 980}
 981
 982static int dt282x_dio_insn_config(struct comedi_device *dev,
 983                                  struct comedi_subdevice *s,
 984                                  struct comedi_insn *insn,
 985                                  unsigned int *data)
 986{
 987        struct dt282x_private *devpriv = dev->private;
 988        unsigned int chan = CR_CHAN(insn->chanspec);
 989        unsigned int mask;
 990        int ret;
 991
 992        if (chan < 8)
 993                mask = 0x00ff;
 994        else
 995                mask = 0xff00;
 996
 997        ret = comedi_dio_insn_config(dev, s, insn, data, mask);
 998        if (ret)
 999                return ret;
1000
1001        devpriv->dacsr &= ~(DT2821_DACSR_LBOE | DT2821_DACSR_HBOE);
1002        if (s->io_bits & 0x00ff)
1003                devpriv->dacsr |= DT2821_DACSR_LBOE;
1004        if (s->io_bits & 0xff00)
1005                devpriv->dacsr |= DT2821_DACSR_HBOE;
1006
1007        outw(devpriv->dacsr, dev->iobase + DT2821_DACSR_REG);
1008
1009        return insn->n;
1010}
1011
1012static const struct comedi_lrange *const ai_range_table[] = {
1013        &range_dt282x_ai_lo_bipolar,
1014        &range_dt282x_ai_lo_unipolar,
1015        &range_dt282x_ai_5_bipolar,
1016        &range_dt282x_ai_5_unipolar
1017};
1018
1019static const struct comedi_lrange *const ai_range_pgl_table[] = {
1020        &range_dt282x_ai_hi_bipolar,
1021        &range_dt282x_ai_hi_unipolar
1022};
1023
1024static const struct comedi_lrange *opt_ai_range_lkup(int ispgl, int x)
1025{
1026        if (ispgl) {
1027                if (x < 0 || x >= 2)
1028                        x = 0;
1029                return ai_range_pgl_table[x];
1030        }
1031
1032        if (x < 0 || x >= 4)
1033                x = 0;
1034        return ai_range_table[x];
1035}
1036
1037static void dt282x_alloc_dma(struct comedi_device *dev,
1038                             struct comedi_devconfig *it)
1039{
1040        struct dt282x_private *devpriv = dev->private;
1041        unsigned int irq_num = it->options[1];
1042        unsigned int dma_chan[2];
1043
1044        if (it->options[2] < it->options[3]) {
1045                dma_chan[0] = it->options[2];
1046                dma_chan[1] = it->options[3];
1047        } else {
1048                dma_chan[0] = it->options[3];
1049                dma_chan[1] = it->options[2];
1050        }
1051
1052        if (!irq_num || dma_chan[0] == dma_chan[1] ||
1053            dma_chan[0] < 5 || dma_chan[0] > 7 ||
1054            dma_chan[1] < 5 || dma_chan[1] > 7)
1055                return;
1056
1057        if (request_irq(irq_num, dt282x_interrupt, 0, dev->board_name, dev))
1058                return;
1059
1060        /* DMA uses two 4K buffers with separate DMA channels */
1061        devpriv->dma = comedi_isadma_alloc(dev, 2, dma_chan[0], dma_chan[1],
1062                                           PAGE_SIZE, 0);
1063        if (!devpriv->dma)
1064                free_irq(irq_num, dev);
1065}
1066
1067static void dt282x_free_dma(struct comedi_device *dev)
1068{
1069        struct dt282x_private *devpriv = dev->private;
1070
1071        if (devpriv)
1072                comedi_isadma_free(devpriv->dma);
1073}
1074
1075static int dt282x_initialize(struct comedi_device *dev)
1076{
1077        /* Initialize board */
1078        outw(DT2821_SUPCSR_BDINIT, dev->iobase + DT2821_SUPCSR_REG);
1079        inw(dev->iobase + DT2821_ADCSR_REG);
1080
1081        /*
1082         * At power up, some registers are in a well-known state.
1083         * Check them to see if a DT2821 series board is present.
1084         */
1085        if (((inw(dev->iobase + DT2821_ADCSR_REG) & 0xfff0) != 0x7c00) ||
1086            ((inw(dev->iobase + DT2821_CHANCSR_REG) & 0xf0f0) != 0x70f0) ||
1087            ((inw(dev->iobase + DT2821_DACSR_REG) & 0x7c93) != 0x7c90) ||
1088            ((inw(dev->iobase + DT2821_SUPCSR_REG) & 0xf8ff) != 0x0000) ||
1089            ((inw(dev->iobase + DT2821_TMRCTR_REG) & 0xff00) != 0xf000)) {
1090                dev_err(dev->class_dev, "board not found\n");
1091                return -EIO;
1092        }
1093        return 0;
1094}
1095
1096static int dt282x_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1097{
1098        const struct dt282x_board *board = dev->board_ptr;
1099        struct dt282x_private *devpriv;
1100        struct comedi_subdevice *s;
1101        int ret;
1102
1103        ret = comedi_request_region(dev, it->options[0], 0x10);
1104        if (ret)
1105                return ret;
1106
1107        ret = dt282x_initialize(dev);
1108        if (ret)
1109                return ret;
1110
1111        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
1112        if (!devpriv)
1113                return -ENOMEM;
1114
1115        /* an IRQ and 2 DMA channels are required for async command support */
1116        dt282x_alloc_dma(dev, it);
1117
1118        ret = comedi_alloc_subdevices(dev, 3);
1119        if (ret)
1120                return ret;
1121
1122        /* Analog Input subdevice */
1123        s = &dev->subdevices[0];
1124        s->type         = COMEDI_SUBD_AI;
1125        s->subdev_flags = SDF_READABLE;
1126        if ((it->options[4] && board->adchan_di) || board->adchan_se == 0) {
1127                s->subdev_flags |= SDF_DIFF;
1128                s->n_chan       = board->adchan_di;
1129        } else {
1130                s->subdev_flags |= SDF_COMMON;
1131                s->n_chan       = board->adchan_se;
1132        }
1133        s->maxdata      = board->ai_maxdata;
1134
1135        s->range_table = opt_ai_range_lkup(board->ispgl, it->options[8]);
1136        devpriv->ad_2scomp = it->options[5] ? 1 : 0;
1137
1138        s->insn_read    = dt282x_ai_insn_read;
1139        if (dev->irq) {
1140                dev->read_subdev = s;
1141                s->subdev_flags |= SDF_CMD_READ;
1142                s->len_chanlist = s->n_chan;
1143                s->do_cmdtest   = dt282x_ai_cmdtest;
1144                s->do_cmd       = dt282x_ai_cmd;
1145                s->cancel       = dt282x_ai_cancel;
1146        }
1147
1148        /* Analog Output subdevice */
1149        s = &dev->subdevices[1];
1150        if (board->dachan) {
1151                s->type         = COMEDI_SUBD_AO;
1152                s->subdev_flags = SDF_WRITABLE;
1153                s->n_chan       = board->dachan;
1154                s->maxdata      = board->ao_maxdata;
1155                /* ranges are per-channel, set by jumpers on the board */
1156                s->range_table  = &dt282x_ao_range;
1157                s->insn_write   = dt282x_ao_insn_write;
1158                if (dev->irq) {
1159                        dev->write_subdev = s;
1160                        s->subdev_flags |= SDF_CMD_WRITE;
1161                        s->len_chanlist = s->n_chan;
1162                        s->do_cmdtest   = dt282x_ao_cmdtest;
1163                        s->do_cmd       = dt282x_ao_cmd;
1164                        s->cancel       = dt282x_ao_cancel;
1165                }
1166
1167                ret = comedi_alloc_subdev_readback(s);
1168                if (ret)
1169                        return ret;
1170        } else {
1171                s->type         = COMEDI_SUBD_UNUSED;
1172        }
1173
1174        /* Digital I/O subdevice */
1175        s = &dev->subdevices[2];
1176        s->type         = COMEDI_SUBD_DIO;
1177        s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
1178        s->n_chan       = 16;
1179        s->maxdata      = 1;
1180        s->range_table  = &range_digital;
1181        s->insn_bits    = dt282x_dio_insn_bits;
1182        s->insn_config  = dt282x_dio_insn_config;
1183
1184        return 0;
1185}
1186
1187static void dt282x_detach(struct comedi_device *dev)
1188{
1189        dt282x_free_dma(dev);
1190        comedi_legacy_detach(dev);
1191}
1192
1193static struct comedi_driver dt282x_driver = {
1194        .driver_name    = "dt282x",
1195        .module         = THIS_MODULE,
1196        .attach         = dt282x_attach,
1197        .detach         = dt282x_detach,
1198        .board_name     = &boardtypes[0].name,
1199        .num_names      = ARRAY_SIZE(boardtypes),
1200        .offset         = sizeof(struct dt282x_board),
1201};
1202module_comedi_driver(dt282x_driver);
1203
1204MODULE_AUTHOR("Comedi http://www.comedi.org");
1205MODULE_DESCRIPTION("Comedi driver for Data Translation DT2821 series");
1206MODULE_LICENSE("GPL");
1207