linux/drivers/staging/comedi/drivers/das800.c
<<
>>
Prefs
   1/*
   2    comedi/drivers/das800.c
   3    Driver for Keitley das800 series boards and compatibles
   4    Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net>
   5
   6    COMEDI - Linux Control and Measurement Device Interface
   7    Copyright (C) 2000 David A. Schleef <ds@schleef.org>
   8
   9    This program is free software; you can redistribute it and/or modify
  10    it under the terms of the GNU General Public License as published by
  11    the Free Software Foundation; either version 2 of the License, or
  12    (at your option) any later version.
  13
  14    This program is distributed in the hope that it will be useful,
  15    but WITHOUT ANY WARRANTY; without even the implied warranty of
  16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17    GNU General Public License for more details.
  18*/
  19/*
  20Driver: das800
  21Description: Keithley Metrabyte DAS800 (& compatibles)
  22Author: Frank Mori Hess <fmhess@users.sourceforge.net>
  23Devices: [Keithley Metrabyte] DAS-800 (das-800), DAS-801 (das-801),
  24  DAS-802 (das-802),
  25  [Measurement Computing] CIO-DAS800 (cio-das800),
  26  CIO-DAS801 (cio-das801), CIO-DAS802 (cio-das802),
  27  CIO-DAS802/16 (cio-das802/16)
  28Status: works, cio-das802/16 untested - email me if you have tested it
  29
  30Configuration options:
  31  [0] - I/O port base address
  32  [1] - IRQ (optional, required for timed or externally triggered conversions)
  33
  34Notes:
  35        IRQ can be omitted, although the cmd interface will not work without it.
  36
  37        All entries in the channel/gain list must use the same gain and be
  38        consecutive channels counting upwards in channel number (these are
  39        hardware limitations.)
  40
  41        I've never tested the gain setting stuff since I only have a
  42        DAS-800 board with fixed gain.
  43
  44        The cio-das802/16 does not have a fifo-empty status bit!  Therefore
  45        only fifo-half-full transfers are possible with this card.
  46
  47cmd triggers supported:
  48        start_src:      TRIG_NOW | TRIG_EXT
  49        scan_begin_src: TRIG_FOLLOW
  50        scan_end_src:   TRIG_COUNT
  51        convert_src:    TRIG_TIMER | TRIG_EXT
  52        stop_src:       TRIG_NONE | TRIG_COUNT
  53*/
  54
  55#include <linux/module.h>
  56#include <linux/interrupt.h>
  57#include <linux/delay.h>
  58
  59#include "../comedidev.h"
  60
  61#include "comedi_8254.h"
  62
  63#define N_CHAN_AI             8 /*  number of analog input channels */
  64
  65/* Registers for the das800 */
  66
  67#define DAS800_LSB            0
  68#define   FIFO_EMPTY            0x1
  69#define   FIFO_OVF              0x2
  70#define DAS800_MSB            1
  71#define DAS800_CONTROL1       2
  72#define   CONTROL1_INTE         0x8
  73#define DAS800_CONV_CONTROL   2
  74#define   ITE                   0x1
  75#define   CASC                  0x2
  76#define   DTEN                  0x4
  77#define   IEOC                  0x8
  78#define   EACS                  0x10
  79#define   CONV_HCEN             0x80
  80#define DAS800_SCAN_LIMITS    2
  81#define DAS800_STATUS         2
  82#define   IRQ                   0x8
  83#define   BUSY                  0x80
  84#define DAS800_GAIN           3
  85#define   CIO_FFOV              0x8   /* cio-das802/16 fifo overflow */
  86#define   CIO_ENHF              0x90  /* cio-das802/16 fifo half full int ena */
  87#define   CONTROL1              0x80
  88#define   CONV_CONTROL          0xa0
  89#define   SCAN_LIMITS           0xc0
  90#define   ID                    0xe0
  91#define DAS800_8254           4
  92#define DAS800_STATUS2        7
  93#define   STATUS2_HCEN          0x80
  94#define   STATUS2_INTE          0X20
  95#define DAS800_ID             7
  96
  97#define DAS802_16_HALF_FIFO_SZ  128
  98
  99struct das800_board {
 100        const char *name;
 101        int ai_speed;
 102        const struct comedi_lrange *ai_range;
 103        int resolution;
 104};
 105
 106static const struct comedi_lrange range_das801_ai = {
 107        9, {
 108                BIP_RANGE(5),
 109                BIP_RANGE(10),
 110                UNI_RANGE(10),
 111                BIP_RANGE(0.5),
 112                UNI_RANGE(1),
 113                BIP_RANGE(0.05),
 114                UNI_RANGE(0.1),
 115                BIP_RANGE(0.01),
 116                UNI_RANGE(0.02)
 117        }
 118};
 119
 120static const struct comedi_lrange range_cio_das801_ai = {
 121        9, {
 122                BIP_RANGE(5),
 123                BIP_RANGE(10),
 124                UNI_RANGE(10),
 125                BIP_RANGE(0.5),
 126                UNI_RANGE(1),
 127                BIP_RANGE(0.05),
 128                UNI_RANGE(0.1),
 129                BIP_RANGE(0.005),
 130                UNI_RANGE(0.01)
 131        }
 132};
 133
 134static const struct comedi_lrange range_das802_ai = {
 135        9, {
 136                BIP_RANGE(5),
 137                BIP_RANGE(10),
 138                UNI_RANGE(10),
 139                BIP_RANGE(2.5),
 140                UNI_RANGE(5),
 141                BIP_RANGE(1.25),
 142                UNI_RANGE(2.5),
 143                BIP_RANGE(0.625),
 144                UNI_RANGE(1.25)
 145        }
 146};
 147
 148static const struct comedi_lrange range_das80216_ai = {
 149        8, {
 150                BIP_RANGE(10),
 151                UNI_RANGE(10),
 152                BIP_RANGE(5),
 153                UNI_RANGE(5),
 154                BIP_RANGE(2.5),
 155                UNI_RANGE(2.5),
 156                BIP_RANGE(1.25),
 157                UNI_RANGE(1.25)
 158        }
 159};
 160
 161enum das800_boardinfo {
 162        BOARD_DAS800,
 163        BOARD_CIODAS800,
 164        BOARD_DAS801,
 165        BOARD_CIODAS801,
 166        BOARD_DAS802,
 167        BOARD_CIODAS802,
 168        BOARD_CIODAS80216,
 169};
 170
 171static const struct das800_board das800_boards[] = {
 172        [BOARD_DAS800] = {
 173                .name           = "das-800",
 174                .ai_speed       = 25000,
 175                .ai_range       = &range_bipolar5,
 176                .resolution     = 12,
 177        },
 178        [BOARD_CIODAS800] = {
 179                .name           = "cio-das800",
 180                .ai_speed       = 20000,
 181                .ai_range       = &range_bipolar5,
 182                .resolution     = 12,
 183        },
 184        [BOARD_DAS801] = {
 185                .name           = "das-801",
 186                .ai_speed       = 25000,
 187                .ai_range       = &range_das801_ai,
 188                .resolution     = 12,
 189        },
 190        [BOARD_CIODAS801] = {
 191                .name           = "cio-das801",
 192                .ai_speed       = 20000,
 193                .ai_range       = &range_cio_das801_ai,
 194                .resolution     = 12,
 195        },
 196        [BOARD_DAS802] = {
 197                .name           = "das-802",
 198                .ai_speed       = 25000,
 199                .ai_range       = &range_das802_ai,
 200                .resolution     = 12,
 201        },
 202        [BOARD_CIODAS802] = {
 203                .name           = "cio-das802",
 204                .ai_speed       = 20000,
 205                .ai_range       = &range_das802_ai,
 206                .resolution     = 12,
 207        },
 208        [BOARD_CIODAS80216] = {
 209                .name           = "cio-das802/16",
 210                .ai_speed       = 10000,
 211                .ai_range       = &range_das80216_ai,
 212                .resolution     = 16,
 213        },
 214};
 215
 216struct das800_private {
 217        unsigned int do_bits;   /* digital output bits */
 218};
 219
 220static void das800_ind_write(struct comedi_device *dev,
 221                             unsigned val, unsigned reg)
 222{
 223        /*
 224         * Select dev->iobase + 2 to be desired register
 225         * then write to that register.
 226         */
 227        outb(reg, dev->iobase + DAS800_GAIN);
 228        outb(val, dev->iobase + 2);
 229}
 230
 231static unsigned das800_ind_read(struct comedi_device *dev, unsigned reg)
 232{
 233        /*
 234         * Select dev->iobase + 7 to be desired register
 235         * then read from that register.
 236         */
 237        outb(reg, dev->iobase + DAS800_GAIN);
 238        return inb(dev->iobase + 7);
 239}
 240
 241static void das800_enable(struct comedi_device *dev)
 242{
 243        const struct das800_board *board = dev->board_ptr;
 244        struct das800_private *devpriv = dev->private;
 245        unsigned long irq_flags;
 246
 247        spin_lock_irqsave(&dev->spinlock, irq_flags);
 248        /*  enable fifo-half full interrupts for cio-das802/16 */
 249        if (board->resolution == 16)
 250                outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
 251        /* enable hardware triggering */
 252        das800_ind_write(dev, CONV_HCEN, CONV_CONTROL);
 253        /* enable card's interrupt */
 254        das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
 255        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 256}
 257
 258static void das800_disable(struct comedi_device *dev)
 259{
 260        unsigned long irq_flags;
 261
 262        spin_lock_irqsave(&dev->spinlock, irq_flags);
 263        /* disable hardware triggering of conversions */
 264        das800_ind_write(dev, 0x0, CONV_CONTROL);
 265        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 266}
 267
 268static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
 269{
 270        das800_disable(dev);
 271        return 0;
 272}
 273
 274static int das800_ai_check_chanlist(struct comedi_device *dev,
 275                                    struct comedi_subdevice *s,
 276                                    struct comedi_cmd *cmd)
 277{
 278        unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
 279        unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
 280        int i;
 281
 282        for (i = 1; i < cmd->chanlist_len; i++) {
 283                unsigned int chan = CR_CHAN(cmd->chanlist[i]);
 284                unsigned int range = CR_RANGE(cmd->chanlist[i]);
 285
 286                if (chan != (chan0 + i) % s->n_chan) {
 287                        dev_dbg(dev->class_dev,
 288                                "chanlist must be consecutive, counting upwards\n");
 289                        return -EINVAL;
 290                }
 291
 292                if (range != range0) {
 293                        dev_dbg(dev->class_dev,
 294                                "chanlist must all have the same gain\n");
 295                        return -EINVAL;
 296                }
 297        }
 298
 299        return 0;
 300}
 301
 302static int das800_ai_do_cmdtest(struct comedi_device *dev,
 303                                struct comedi_subdevice *s,
 304                                struct comedi_cmd *cmd)
 305{
 306        const struct das800_board *board = dev->board_ptr;
 307        int err = 0;
 308
 309        /* Step 1 : check if triggers are trivially valid */
 310
 311        err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
 312        err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
 313        err |= comedi_check_trigger_src(&cmd->convert_src,
 314                                        TRIG_TIMER | TRIG_EXT);
 315        err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 316        err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
 317
 318        if (err)
 319                return 1;
 320
 321        /* Step 2a : make sure trigger sources are unique */
 322
 323        err |= comedi_check_trigger_is_unique(cmd->start_src);
 324        err |= comedi_check_trigger_is_unique(cmd->convert_src);
 325        err |= comedi_check_trigger_is_unique(cmd->stop_src);
 326
 327        /* Step 2b : and mutually compatible */
 328
 329        if (err)
 330                return 2;
 331
 332        /* Step 3: check if arguments are trivially valid */
 333
 334        err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 335
 336        if (cmd->convert_src == TRIG_TIMER) {
 337                err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
 338                                                    board->ai_speed);
 339        }
 340
 341        err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
 342        err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 343                                           cmd->chanlist_len);
 344
 345        if (cmd->stop_src == TRIG_COUNT)
 346                err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
 347        else    /* TRIG_NONE */
 348                err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 349
 350        if (err)
 351                return 3;
 352
 353        /* step 4: fix up any arguments */
 354
 355        if (cmd->convert_src == TRIG_TIMER) {
 356                unsigned int arg = cmd->convert_arg;
 357
 358                comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
 359                err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
 360        }
 361
 362        if (err)
 363                return 4;
 364
 365        /* Step 5: check channel list if it exists */
 366        if (cmd->chanlist && cmd->chanlist_len > 0)
 367                err |= das800_ai_check_chanlist(dev, s, cmd);
 368
 369        if (err)
 370                return 5;
 371
 372        return 0;
 373}
 374
 375static int das800_ai_do_cmd(struct comedi_device *dev,
 376                            struct comedi_subdevice *s)
 377{
 378        const struct das800_board *board = dev->board_ptr;
 379        struct comedi_async *async = s->async;
 380        struct comedi_cmd *cmd = &async->cmd;
 381        unsigned int gain = CR_RANGE(cmd->chanlist[0]);
 382        unsigned int start_chan = CR_CHAN(cmd->chanlist[0]);
 383        unsigned int end_chan = (start_chan + cmd->chanlist_len - 1) % 8;
 384        unsigned int scan_chans = (end_chan << 3) | start_chan;
 385        int conv_bits;
 386        unsigned long irq_flags;
 387
 388        das800_disable(dev);
 389
 390        spin_lock_irqsave(&dev->spinlock, irq_flags);
 391        /* set scan limits */
 392        das800_ind_write(dev, scan_chans, SCAN_LIMITS);
 393        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 394
 395        /* set gain */
 396        if (board->resolution == 12 && gain > 0)
 397                gain += 0x7;
 398        gain &= 0xf;
 399        outb(gain, dev->iobase + DAS800_GAIN);
 400
 401        /* enable auto channel scan, send interrupts on end of conversion
 402         * and set clock source to internal or external
 403         */
 404        conv_bits = 0;
 405        conv_bits |= EACS | IEOC;
 406        if (cmd->start_src == TRIG_EXT)
 407                conv_bits |= DTEN;
 408        if (cmd->convert_src == TRIG_TIMER) {
 409                conv_bits |= CASC | ITE;
 410                comedi_8254_update_divisors(dev->pacer);
 411                comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
 412        }
 413
 414        spin_lock_irqsave(&dev->spinlock, irq_flags);
 415        das800_ind_write(dev, conv_bits, CONV_CONTROL);
 416        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 417
 418        das800_enable(dev);
 419        return 0;
 420}
 421
 422static unsigned int das800_ai_get_sample(struct comedi_device *dev)
 423{
 424        unsigned int lsb = inb(dev->iobase + DAS800_LSB);
 425        unsigned int msb = inb(dev->iobase + DAS800_MSB);
 426
 427        return (msb << 8) | lsb;
 428}
 429
 430static irqreturn_t das800_interrupt(int irq, void *d)
 431{
 432        struct comedi_device *dev = d;
 433        struct das800_private *devpriv = dev->private;
 434        struct comedi_subdevice *s = dev->read_subdev;
 435        struct comedi_async *async;
 436        struct comedi_cmd *cmd;
 437        unsigned long irq_flags;
 438        unsigned int status;
 439        unsigned int val;
 440        bool fifo_empty;
 441        bool fifo_overflow;
 442        int i;
 443
 444        status = inb(dev->iobase + DAS800_STATUS);
 445        if (!(status & IRQ))
 446                return IRQ_NONE;
 447        if (!dev->attached)
 448                return IRQ_HANDLED;
 449
 450        async = s->async;
 451        cmd = &async->cmd;
 452
 453        spin_lock_irqsave(&dev->spinlock, irq_flags);
 454        status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
 455        /*
 456         * Don't release spinlock yet since we want to make sure
 457         * no one else disables hardware conversions.
 458         */
 459
 460        /* if hardware conversions are not enabled, then quit */
 461        if (status == 0) {
 462                spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 463                return IRQ_HANDLED;
 464        }
 465
 466        for (i = 0; i < DAS802_16_HALF_FIFO_SZ; i++) {
 467                val = das800_ai_get_sample(dev);
 468                if (s->maxdata == 0x0fff) {
 469                        fifo_empty = !!(val & FIFO_EMPTY);
 470                        fifo_overflow = !!(val & FIFO_OVF);
 471                } else {
 472                        /* cio-das802/16 has no fifo empty status bit */
 473                        fifo_empty = false;
 474                        fifo_overflow = !!(inb(dev->iobase + DAS800_GAIN) &
 475                                                CIO_FFOV);
 476                }
 477                if (fifo_empty || fifo_overflow)
 478                        break;
 479
 480                if (s->maxdata == 0x0fff)
 481                        val >>= 4;      /* 12-bit sample */
 482
 483                val &= s->maxdata;
 484                comedi_buf_write_samples(s, &val, 1);
 485
 486                if (cmd->stop_src == TRIG_COUNT &&
 487                    async->scans_done >= cmd->stop_arg) {
 488                        async->events |= COMEDI_CB_EOA;
 489                        break;
 490                }
 491        }
 492
 493        if (fifo_overflow) {
 494                spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 495                async->events |= COMEDI_CB_ERROR;
 496                comedi_handle_events(dev, s);
 497                return IRQ_HANDLED;
 498        }
 499
 500        if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
 501                /*
 502                 * Re-enable card's interrupt.
 503                 * We already have spinlock, so indirect addressing is safe
 504                 */
 505                das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
 506                                 CONTROL1);
 507                spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 508        } else {
 509                /* otherwise, stop taking data */
 510                spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 511                das800_disable(dev);
 512        }
 513        comedi_handle_events(dev, s);
 514        return IRQ_HANDLED;
 515}
 516
 517static int das800_ai_eoc(struct comedi_device *dev,
 518                         struct comedi_subdevice *s,
 519                         struct comedi_insn *insn,
 520                         unsigned long context)
 521{
 522        unsigned int status;
 523
 524        status = inb(dev->iobase + DAS800_STATUS);
 525        if ((status & BUSY) == 0)
 526                return 0;
 527        return -EBUSY;
 528}
 529
 530static int das800_ai_insn_read(struct comedi_device *dev,
 531                               struct comedi_subdevice *s,
 532                               struct comedi_insn *insn,
 533                               unsigned int *data)
 534{
 535        struct das800_private *devpriv = dev->private;
 536        unsigned int chan = CR_CHAN(insn->chanspec);
 537        unsigned int range = CR_RANGE(insn->chanspec);
 538        unsigned long irq_flags;
 539        unsigned int val;
 540        int ret;
 541        int i;
 542
 543        das800_disable(dev);
 544
 545        /* set multiplexer */
 546        spin_lock_irqsave(&dev->spinlock, irq_flags);
 547        das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1);
 548        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 549
 550        /* set gain / range */
 551        if (s->maxdata == 0x0fff && range)
 552                range += 0x7;
 553        range &= 0xf;
 554        outb(range, dev->iobase + DAS800_GAIN);
 555
 556        udelay(5);
 557
 558        for (i = 0; i < insn->n; i++) {
 559                /* trigger conversion */
 560                outb_p(0, dev->iobase + DAS800_MSB);
 561
 562                ret = comedi_timeout(dev, s, insn, das800_ai_eoc, 0);
 563                if (ret)
 564                        return ret;
 565
 566                val = das800_ai_get_sample(dev);
 567                if (s->maxdata == 0x0fff)
 568                        val >>= 4;      /* 12-bit sample */
 569                data[i] = val & s->maxdata;
 570        }
 571
 572        return insn->n;
 573}
 574
 575static int das800_di_insn_bits(struct comedi_device *dev,
 576                               struct comedi_subdevice *s,
 577                               struct comedi_insn *insn,
 578                               unsigned int *data)
 579{
 580        data[1] = (inb(dev->iobase + DAS800_STATUS) >> 4) & 0x7;
 581
 582        return insn->n;
 583}
 584
 585static int das800_do_insn_bits(struct comedi_device *dev,
 586                               struct comedi_subdevice *s,
 587                               struct comedi_insn *insn,
 588                               unsigned int *data)
 589{
 590        struct das800_private *devpriv = dev->private;
 591        unsigned long irq_flags;
 592
 593        if (comedi_dio_update_state(s, data)) {
 594                devpriv->do_bits = s->state << 4;
 595
 596                spin_lock_irqsave(&dev->spinlock, irq_flags);
 597                das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
 598                                 CONTROL1);
 599                spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 600        }
 601
 602        data[1] = s->state;
 603
 604        return insn->n;
 605}
 606
 607static const struct das800_board *das800_probe(struct comedi_device *dev)
 608{
 609        const struct das800_board *board = dev->board_ptr;
 610        int index = board ? board - das800_boards : -EINVAL;
 611        int id_bits;
 612        unsigned long irq_flags;
 613
 614        /*
 615         * The dev->board_ptr will be set by comedi_device_attach() if the
 616         * board name provided by the user matches a board->name in this
 617         * driver. If so, this function sanity checks the id_bits to verify
 618         * that the board is correct.
 619         *
 620         * If the dev->board_ptr is not set, the user is trying to attach
 621         * an unspecified board to this driver. In this case the id_bits
 622         * are used to 'probe' for the correct dev->board_ptr.
 623         */
 624        spin_lock_irqsave(&dev->spinlock, irq_flags);
 625        id_bits = das800_ind_read(dev, ID) & 0x3;
 626        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 627
 628        switch (id_bits) {
 629        case 0x0:
 630                if (index == BOARD_DAS800 || index == BOARD_CIODAS800)
 631                        return board;
 632                index = BOARD_DAS800;
 633                break;
 634        case 0x2:
 635                if (index == BOARD_DAS801 || index == BOARD_CIODAS801)
 636                        return board;
 637                index = BOARD_DAS801;
 638                break;
 639        case 0x3:
 640                if (index == BOARD_DAS802 || index == BOARD_CIODAS802 ||
 641                    index == BOARD_CIODAS80216)
 642                        return board;
 643                index = BOARD_DAS802;
 644                break;
 645        default:
 646                dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n",
 647                        id_bits);
 648                return NULL;
 649        }
 650        dev_dbg(dev->class_dev, "Board model (probed): %s series\n",
 651                das800_boards[index].name);
 652
 653        return &das800_boards[index];
 654}
 655
 656static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 657{
 658        const struct das800_board *board;
 659        struct das800_private *devpriv;
 660        struct comedi_subdevice *s;
 661        unsigned int irq = it->options[1];
 662        unsigned long irq_flags;
 663        int ret;
 664
 665        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 666        if (!devpriv)
 667                return -ENOMEM;
 668
 669        ret = comedi_request_region(dev, it->options[0], 0x8);
 670        if (ret)
 671                return ret;
 672
 673        board = das800_probe(dev);
 674        if (!board)
 675                return -ENODEV;
 676        dev->board_ptr = board;
 677        dev->board_name = board->name;
 678
 679        if (irq > 1 && irq <= 7) {
 680                ret = request_irq(irq, das800_interrupt, 0, dev->board_name,
 681                                  dev);
 682                if (ret == 0)
 683                        dev->irq = irq;
 684        }
 685
 686        dev->pacer = comedi_8254_init(dev->iobase + DAS800_8254,
 687                                      I8254_OSC_BASE_1MHZ, I8254_IO8, 0);
 688        if (!dev->pacer)
 689                return -ENOMEM;
 690
 691        ret = comedi_alloc_subdevices(dev, 3);
 692        if (ret)
 693                return ret;
 694
 695        /* Analog Input subdevice */
 696        s = &dev->subdevices[0];
 697        dev->read_subdev = s;
 698        s->type         = COMEDI_SUBD_AI;
 699        s->subdev_flags = SDF_READABLE | SDF_GROUND;
 700        s->n_chan       = 8;
 701        s->maxdata      = (1 << board->resolution) - 1;
 702        s->range_table  = board->ai_range;
 703        s->insn_read    = das800_ai_insn_read;
 704        if (dev->irq) {
 705                s->subdev_flags |= SDF_CMD_READ;
 706                s->len_chanlist = 8;
 707                s->do_cmdtest   = das800_ai_do_cmdtest;
 708                s->do_cmd       = das800_ai_do_cmd;
 709                s->cancel       = das800_cancel;
 710        }
 711
 712        /* Digital Input subdevice */
 713        s = &dev->subdevices[1];
 714        s->type         = COMEDI_SUBD_DI;
 715        s->subdev_flags = SDF_READABLE;
 716        s->n_chan       = 3;
 717        s->maxdata      = 1;
 718        s->range_table  = &range_digital;
 719        s->insn_bits    = das800_di_insn_bits;
 720
 721        /* Digital Output subdevice */
 722        s = &dev->subdevices[2];
 723        s->type         = COMEDI_SUBD_DO;
 724        s->subdev_flags = SDF_WRITABLE;
 725        s->n_chan       = 4;
 726        s->maxdata      = 1;
 727        s->range_table  = &range_digital;
 728        s->insn_bits    = das800_do_insn_bits;
 729
 730        das800_disable(dev);
 731
 732        /* initialize digital out channels */
 733        spin_lock_irqsave(&dev->spinlock, irq_flags);
 734        das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
 735        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 736
 737        return 0;
 738};
 739
 740static struct comedi_driver driver_das800 = {
 741        .driver_name    = "das800",
 742        .module         = THIS_MODULE,
 743        .attach         = das800_attach,
 744        .detach         = comedi_legacy_detach,
 745        .num_names      = ARRAY_SIZE(das800_boards),
 746        .board_name     = &das800_boards[0].name,
 747        .offset         = sizeof(struct das800_board),
 748};
 749module_comedi_driver(driver_das800);
 750
 751MODULE_AUTHOR("Comedi http://www.comedi.org");
 752MODULE_DESCRIPTION("Comedi low-level driver");
 753MODULE_LICENSE("GPL");
 754