linux/drivers/staging/comedi/drivers/das16m1.c
<<
>>
Prefs
   1/*
   2    comedi/drivers/das16m1.c
   3    CIO-DAS16/M1 driver
   4    Author: Frank Mori Hess, based on code from the das16
   5      driver.
   6    Copyright (C) 2001 Frank Mori Hess <fmhess@users.sourceforge.net>
   7
   8    COMEDI - Linux Control and Measurement Device Interface
   9    Copyright (C) 2000 David A. Schleef <ds@schleef.org>
  10
  11    This program is free software; you can redistribute it and/or modify
  12    it under the terms of the GNU General Public License as published by
  13    the Free Software Foundation; either version 2 of the License, or
  14    (at your option) any later version.
  15
  16    This program is distributed in the hope that it will be useful,
  17    but WITHOUT ANY WARRANTY; without even the implied warranty of
  18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19    GNU General Public License for more details.
  20
  21    You should have received a copy of the GNU General Public License
  22    along with this program; if not, write to the Free Software
  23    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  24
  25************************************************************************
  26*/
  27/*
  28Driver: das16m1
  29Description: CIO-DAS16/M1
  30Author: Frank Mori Hess <fmhess@users.sourceforge.net>
  31Devices: [Measurement Computing] CIO-DAS16/M1 (cio-das16/m1)
  32Status: works
  33
  34This driver supports a single board - the CIO-DAS16/M1.
  35As far as I know, there are no other boards that have
  36the same register layout.  Even the CIO-DAS16/M1/16 is
  37significantly different.
  38
  39I was _barely_ able to reach the full 1 MHz capability
  40of this board, using a hard real-time interrupt
  41(set the TRIG_RT flag in your struct comedi_cmd and use
  42rtlinux or RTAI).  The board can't do dma, so the bottleneck is
  43pulling the data across the ISA bus.  I timed the interrupt
  44handler, and it took my computer ~470 microseconds to pull 512
  45samples from the board.  So at 1 Mhz sampling rate,
  46expect your CPU to be spending almost all of its
  47time in the interrupt handler.
  48
  49This board has some unusual restrictions for its channel/gain list.  If the
  50list has 2 or more channels in it, then two conditions must be satisfied:
  51(1) - even/odd channels must appear at even/odd indices in the list
  52(2) - the list must have an even number of entries.
  53
  54Options:
  55        [0] - base io address
  56        [1] - irq (optional, but you probably want it)
  57
  58irq can be omitted, although the cmd interface will not work without it.
  59*/
  60
  61#include <linux/ioport.h>
  62#include <linux/interrupt.h>
  63#include "../comedidev.h"
  64
  65#include "8255.h"
  66#include "8253.h"
  67#include "comedi_fc.h"
  68
  69#define DAS16M1_SIZE 16
  70#define DAS16M1_SIZE2 8
  71
  72#define DAS16M1_XTAL 100        /* 10 MHz master clock */
  73
  74#define FIFO_SIZE 1024          /*  1024 sample fifo */
  75
  76/*
  77    CIO-DAS16_M1.pdf
  78
  79    "cio-das16/m1"
  80
  81  0     a/d bits 0-3, mux               start 12 bit
  82  1     a/d bits 4-11           unused
  83  2     status          control
  84  3     di 4 bit                do 4 bit
  85  4     unused                  clear interrupt
  86  5     interrupt, pacer
  87  6     channel/gain queue address
  88  7     channel/gain queue data
  89  89ab  8254
  90  cdef  8254
  91  400   8255
  92  404-407       8254
  93
  94*/
  95
  96#define DAS16M1_AI             0        /*  16-bit wide register */
  97#define   AI_CHAN(x)             ((x) & 0xf)
  98#define DAS16M1_CS             2
  99#define   EXT_TRIG_BIT           0x1
 100#define   OVRUN                  0x20
 101#define   IRQDATA                0x80
 102#define DAS16M1_DIO            3
 103#define DAS16M1_CLEAR_INTR     4
 104#define DAS16M1_INTR_CONTROL   5
 105#define   EXT_PACER              0x2
 106#define   INT_PACER              0x3
 107#define   PACER_MASK             0x3
 108#define   INTE                   0x80
 109#define DAS16M1_QUEUE_ADDR     6
 110#define DAS16M1_QUEUE_DATA     7
 111#define   Q_CHAN(x)              ((x) & 0x7)
 112#define   Q_RANGE(x)             (((x) & 0xf) << 4)
 113#define   UNIPOLAR               0x40
 114#define DAS16M1_8254_FIRST             0x8
 115#define DAS16M1_8254_FIRST_CNTRL       0xb
 116#define   TOTAL_CLEAR                    0x30
 117#define DAS16M1_8254_SECOND            0xc
 118#define DAS16M1_82C55                  0x400
 119#define DAS16M1_8254_THIRD             0x404
 120
 121static const struct comedi_lrange range_das16m1 = { 9,
 122        {
 123         BIP_RANGE(5),
 124         BIP_RANGE(2.5),
 125         BIP_RANGE(1.25),
 126         BIP_RANGE(0.625),
 127         UNI_RANGE(10),
 128         UNI_RANGE(5),
 129         UNI_RANGE(2.5),
 130         UNI_RANGE(1.25),
 131         BIP_RANGE(10),
 132         }
 133};
 134
 135static int das16m1_do_wbits(struct comedi_device *dev,
 136                            struct comedi_subdevice *s,
 137                            struct comedi_insn *insn, unsigned int *data);
 138static int das16m1_di_rbits(struct comedi_device *dev,
 139                            struct comedi_subdevice *s,
 140                            struct comedi_insn *insn, unsigned int *data);
 141static int das16m1_ai_rinsn(struct comedi_device *dev,
 142                            struct comedi_subdevice *s,
 143                            struct comedi_insn *insn, unsigned int *data);
 144
 145static int das16m1_cmd_test(struct comedi_device *dev,
 146                            struct comedi_subdevice *s, struct comedi_cmd *cmd);
 147static int das16m1_cmd_exec(struct comedi_device *dev,
 148                            struct comedi_subdevice *s);
 149static int das16m1_cancel(struct comedi_device *dev,
 150                          struct comedi_subdevice *s);
 151
 152static int das16m1_poll(struct comedi_device *dev, struct comedi_subdevice *s);
 153static irqreturn_t das16m1_interrupt(int irq, void *d);
 154static void das16m1_handler(struct comedi_device *dev, unsigned int status);
 155
 156static unsigned int das16m1_set_pacer(struct comedi_device *dev,
 157                                      unsigned int ns, int round_flag);
 158
 159static int das16m1_irq_bits(unsigned int irq);
 160
 161struct das16m1_board {
 162        const char *name;
 163        unsigned int ai_speed;
 164};
 165
 166static const struct das16m1_board das16m1_boards[] = {
 167        {
 168         .name = "cio-das16/m1",        /*  CIO-DAS16_M1.pdf */
 169         .ai_speed = 1000,      /*  1MHz max speed */
 170         },
 171};
 172
 173static int das16m1_attach(struct comedi_device *dev,
 174                          struct comedi_devconfig *it);
 175static int das16m1_detach(struct comedi_device *dev);
 176static struct comedi_driver driver_das16m1 = {
 177        .driver_name = "das16m1",
 178        .module = THIS_MODULE,
 179        .attach = das16m1_attach,
 180        .detach = das16m1_detach,
 181        .board_name = &das16m1_boards[0].name,
 182        .num_names = ARRAY_SIZE(das16m1_boards),
 183        .offset = sizeof(das16m1_boards[0]),
 184};
 185
 186struct das16m1_private_struct {
 187        unsigned int control_state;
 188        volatile unsigned int adc_count;        /*  number of samples completed */
 189        /* initial value in lower half of hardware conversion counter,
 190         * needed to keep track of whether new count has been loaded into
 191         * counter yet (loaded by first sample conversion) */
 192        u16 initial_hw_count;
 193        short ai_buffer[FIFO_SIZE];
 194        unsigned int do_bits;   /*  saves status of digital output bits */
 195        unsigned int divisor1;  /*  divides master clock to obtain conversion speed */
 196        unsigned int divisor2;  /*  divides master clock to obtain conversion speed */
 197};
 198#define devpriv ((struct das16m1_private_struct *)(dev->private))
 199#define thisboard ((const struct das16m1_board *)(dev->board_ptr))
 200
 201static int __init driver_das16m1_init_module(void)
 202{
 203        return comedi_driver_register(&driver_das16m1);
 204}
 205
 206static void __exit driver_das16m1_cleanup_module(void)
 207{
 208        comedi_driver_unregister(&driver_das16m1);
 209}
 210
 211module_init(driver_das16m1_init_module);
 212module_exit(driver_das16m1_cleanup_module);
 213
 214static inline short munge_sample(short data)
 215{
 216        return (data >> 4) & 0xfff;
 217}
 218
 219static int das16m1_cmd_test(struct comedi_device *dev,
 220                            struct comedi_subdevice *s, struct comedi_cmd *cmd)
 221{
 222        unsigned int err = 0, tmp, i;
 223
 224        /* make sure triggers are valid */
 225        tmp = cmd->start_src;
 226        cmd->start_src &= TRIG_NOW | TRIG_EXT;
 227        if (!cmd->start_src || tmp != cmd->start_src)
 228                err++;
 229
 230        tmp = cmd->scan_begin_src;
 231        cmd->scan_begin_src &= TRIG_FOLLOW;
 232        if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
 233                err++;
 234
 235        tmp = cmd->convert_src;
 236        cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
 237        if (!cmd->convert_src || tmp != cmd->convert_src)
 238                err++;
 239
 240        tmp = cmd->scan_end_src;
 241        cmd->scan_end_src &= TRIG_COUNT;
 242        if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
 243                err++;
 244
 245        tmp = cmd->stop_src;
 246        cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
 247        if (!cmd->stop_src || tmp != cmd->stop_src)
 248                err++;
 249
 250        if (err)
 251                return 1;
 252
 253        /* step 2: make sure trigger sources are unique and mutually compatible */
 254        if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
 255                err++;
 256        if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
 257                err++;
 258        if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
 259                err++;
 260
 261        if (err)
 262                return 2;
 263
 264        /* step 3: make sure arguments are trivially compatible */
 265        if (cmd->start_arg != 0) {
 266                cmd->start_arg = 0;
 267                err++;
 268        }
 269
 270        if (cmd->scan_begin_src == TRIG_FOLLOW) {
 271                /* internal trigger */
 272                if (cmd->scan_begin_arg != 0) {
 273                        cmd->scan_begin_arg = 0;
 274                        err++;
 275                }
 276        }
 277
 278        if (cmd->convert_src == TRIG_TIMER) {
 279                if (cmd->convert_arg < thisboard->ai_speed) {
 280                        cmd->convert_arg = thisboard->ai_speed;
 281                        err++;
 282                }
 283        }
 284
 285        if (cmd->scan_end_arg != cmd->chanlist_len) {
 286                cmd->scan_end_arg = cmd->chanlist_len;
 287                err++;
 288        }
 289
 290        if (cmd->stop_src == TRIG_COUNT) {
 291                /* any count is allowed */
 292        } else {
 293                /* TRIG_NONE */
 294                if (cmd->stop_arg != 0) {
 295                        cmd->stop_arg = 0;
 296                        err++;
 297                }
 298        }
 299
 300        if (err)
 301                return 3;
 302
 303        /* step 4: fix up arguments */
 304
 305        if (cmd->convert_src == TRIG_TIMER) {
 306                tmp = cmd->convert_arg;
 307                /* calculate counter values that give desired timing */
 308                i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL,
 309                                               &(devpriv->divisor1),
 310                                               &(devpriv->divisor2),
 311                                               &(cmd->convert_arg),
 312                                               cmd->flags & TRIG_ROUND_MASK);
 313                if (tmp != cmd->convert_arg)
 314                        err++;
 315        }
 316
 317        if (err)
 318                return 4;
 319
 320        /*  check chanlist against board's peculiarities */
 321        if (cmd->chanlist && cmd->chanlist_len > 1) {
 322                for (i = 0; i < cmd->chanlist_len; i++) {
 323                        /*  even/odd channels must go into even/odd queue addresses */
 324                        if ((i % 2) != (CR_CHAN(cmd->chanlist[i]) % 2)) {
 325                                comedi_error(dev, "bad chanlist:\n"
 326                                             " even/odd channels must go have even/odd chanlist indices");
 327                                err++;
 328                        }
 329                }
 330                if ((cmd->chanlist_len % 2) != 0) {
 331                        comedi_error(dev,
 332                                     "chanlist must be of even length or length 1");
 333                        err++;
 334                }
 335        }
 336
 337        if (err)
 338                return 5;
 339
 340        return 0;
 341}
 342
 343static int das16m1_cmd_exec(struct comedi_device *dev,
 344                            struct comedi_subdevice *s)
 345{
 346        struct comedi_async *async = s->async;
 347        struct comedi_cmd *cmd = &async->cmd;
 348        unsigned int byte, i;
 349
 350        if (dev->irq == 0) {
 351                comedi_error(dev, "irq required to execute comedi_cmd");
 352                return -1;
 353        }
 354
 355        /* disable interrupts and internal pacer */
 356        devpriv->control_state &= ~INTE & ~PACER_MASK;
 357        outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
 358
 359        /*  set software count */
 360        devpriv->adc_count = 0;
 361        /* Initialize lower half of hardware counter, used to determine how
 362         * many samples are in fifo.  Value doesn't actually load into counter
 363         * until counter's next clock (the next a/d conversion) */
 364        i8254_load(dev->iobase + DAS16M1_8254_FIRST, 0, 1, 0, 2);
 365        /* remember current reading of counter so we know when counter has
 366         * actually been loaded */
 367        devpriv->initial_hw_count =
 368            i8254_read(dev->iobase + DAS16M1_8254_FIRST, 0, 1);
 369        /* setup channel/gain queue */
 370        for (i = 0; i < cmd->chanlist_len; i++) {
 371                outb(i, dev->iobase + DAS16M1_QUEUE_ADDR);
 372                byte =
 373                    Q_CHAN(CR_CHAN(cmd->chanlist[i])) |
 374                    Q_RANGE(CR_RANGE(cmd->chanlist[i]));
 375                outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
 376        }
 377
 378        /* set counter mode and counts */
 379        cmd->convert_arg =
 380            das16m1_set_pacer(dev, cmd->convert_arg,
 381                              cmd->flags & TRIG_ROUND_MASK);
 382
 383        /*  set control & status register */
 384        byte = 0;
 385        /* if we are using external start trigger (also board dislikes having
 386         * both start and conversion triggers external simultaneously) */
 387        if (cmd->start_src == TRIG_EXT && cmd->convert_src != TRIG_EXT) {
 388                byte |= EXT_TRIG_BIT;
 389        }
 390        outb(byte, dev->iobase + DAS16M1_CS);
 391        /* clear interrupt bit */
 392        outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
 393
 394        /* enable interrupts and internal pacer */
 395        devpriv->control_state &= ~PACER_MASK;
 396        if (cmd->convert_src == TRIG_TIMER) {
 397                devpriv->control_state |= INT_PACER;
 398        } else {
 399                devpriv->control_state |= EXT_PACER;
 400        }
 401        devpriv->control_state |= INTE;
 402        outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
 403
 404        return 0;
 405}
 406
 407static int das16m1_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
 408{
 409        devpriv->control_state &= ~INTE & ~PACER_MASK;
 410        outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
 411
 412        return 0;
 413}
 414
 415static int das16m1_ai_rinsn(struct comedi_device *dev,
 416                            struct comedi_subdevice *s,
 417                            struct comedi_insn *insn, unsigned int *data)
 418{
 419        int i, n;
 420        int byte;
 421        const int timeout = 1000;
 422
 423        /* disable interrupts and internal pacer */
 424        devpriv->control_state &= ~INTE & ~PACER_MASK;
 425        outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
 426
 427        /* setup channel/gain queue */
 428        outb(0, dev->iobase + DAS16M1_QUEUE_ADDR);
 429        byte =
 430            Q_CHAN(CR_CHAN(insn->chanspec)) | Q_RANGE(CR_RANGE(insn->chanspec));
 431        outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
 432
 433        for (n = 0; n < insn->n; n++) {
 434                /* clear IRQDATA bit */
 435                outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
 436                /* trigger conversion */
 437                outb(0, dev->iobase);
 438
 439                for (i = 0; i < timeout; i++) {
 440                        if (inb(dev->iobase + DAS16M1_CS) & IRQDATA)
 441                                break;
 442                }
 443                if (i == timeout) {
 444                        comedi_error(dev, "timeout");
 445                        return -ETIME;
 446                }
 447                data[n] = munge_sample(inw(dev->iobase));
 448        }
 449
 450        return n;
 451}
 452
 453static int das16m1_di_rbits(struct comedi_device *dev,
 454                            struct comedi_subdevice *s,
 455                            struct comedi_insn *insn, unsigned int *data)
 456{
 457        unsigned int bits;
 458
 459        bits = inb(dev->iobase + DAS16M1_DIO) & 0xf;
 460        data[1] = bits;
 461        data[0] = 0;
 462
 463        return 2;
 464}
 465
 466static int das16m1_do_wbits(struct comedi_device *dev,
 467                            struct comedi_subdevice *s,
 468                            struct comedi_insn *insn, unsigned int *data)
 469{
 470        unsigned int wbits;
 471
 472        /*  only set bits that have been masked */
 473        data[0] &= 0xf;
 474        wbits = devpriv->do_bits;
 475        /*  zero bits that have been masked */
 476        wbits &= ~data[0];
 477        /*  set masked bits */
 478        wbits |= data[0] & data[1];
 479        devpriv->do_bits = wbits;
 480        data[1] = wbits;
 481
 482        outb(devpriv->do_bits, dev->iobase + DAS16M1_DIO);
 483
 484        return 2;
 485}
 486
 487static int das16m1_poll(struct comedi_device *dev, struct comedi_subdevice *s)
 488{
 489        unsigned long flags;
 490        unsigned int status;
 491
 492        /*  prevent race with interrupt handler */
 493        spin_lock_irqsave(&dev->spinlock, flags);
 494        status = inb(dev->iobase + DAS16M1_CS);
 495        das16m1_handler(dev, status);
 496        spin_unlock_irqrestore(&dev->spinlock, flags);
 497
 498        return s->async->buf_write_count - s->async->buf_read_count;
 499}
 500
 501static irqreturn_t das16m1_interrupt(int irq, void *d)
 502{
 503        int status;
 504        struct comedi_device *dev = d;
 505
 506        if (dev->attached == 0) {
 507                comedi_error(dev, "premature interrupt");
 508                return IRQ_HANDLED;
 509        }
 510        /*  prevent race with comedi_poll() */
 511        spin_lock(&dev->spinlock);
 512
 513        status = inb(dev->iobase + DAS16M1_CS);
 514
 515        if ((status & (IRQDATA | OVRUN)) == 0) {
 516                comedi_error(dev, "spurious interrupt");
 517                spin_unlock(&dev->spinlock);
 518                return IRQ_NONE;
 519        }
 520
 521        das16m1_handler(dev, status);
 522
 523        /* clear interrupt */
 524        outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
 525
 526        spin_unlock(&dev->spinlock);
 527        return IRQ_HANDLED;
 528}
 529
 530static void munge_sample_array(short *array, unsigned int num_elements)
 531{
 532        unsigned int i;
 533
 534        for (i = 0; i < num_elements; i++) {
 535                array[i] = munge_sample(array[i]);
 536        }
 537}
 538
 539static void das16m1_handler(struct comedi_device *dev, unsigned int status)
 540{
 541        struct comedi_subdevice *s;
 542        struct comedi_async *async;
 543        struct comedi_cmd *cmd;
 544        u16 num_samples;
 545        u16 hw_counter;
 546
 547        s = dev->read_subdev;
 548        async = s->async;
 549        async->events = 0;
 550        cmd = &async->cmd;
 551
 552        /*  figure out how many samples are in fifo */
 553        hw_counter = i8254_read(dev->iobase + DAS16M1_8254_FIRST, 0, 1);
 554        /* make sure hardware counter reading is not bogus due to initial value
 555         * not having been loaded yet */
 556        if (devpriv->adc_count == 0 && hw_counter == devpriv->initial_hw_count) {
 557                num_samples = 0;
 558        } else {
 559                /* The calculation of num_samples looks odd, but it uses the following facts.
 560                 * 16 bit hardware counter is initialized with value of zero (which really
 561                 * means 0x1000).  The counter decrements by one on each conversion
 562                 * (when the counter decrements from zero it goes to 0xffff).  num_samples
 563                 * is a 16 bit variable, so it will roll over in a similar fashion to the
 564                 * hardware counter.  Work it out, and this is what you get. */
 565                num_samples = -hw_counter - devpriv->adc_count;
 566        }
 567        /*  check if we only need some of the points */
 568        if (cmd->stop_src == TRIG_COUNT) {
 569                if (num_samples > cmd->stop_arg * cmd->chanlist_len)
 570                        num_samples = cmd->stop_arg * cmd->chanlist_len;
 571        }
 572        /*  make sure we dont try to get too many points if fifo has overrun */
 573        if (num_samples > FIFO_SIZE)
 574                num_samples = FIFO_SIZE;
 575        insw(dev->iobase, devpriv->ai_buffer, num_samples);
 576        munge_sample_array(devpriv->ai_buffer, num_samples);
 577        cfc_write_array_to_buffer(s, devpriv->ai_buffer,
 578                                  num_samples * sizeof(short));
 579        devpriv->adc_count += num_samples;
 580
 581        if (cmd->stop_src == TRIG_COUNT) {
 582                if (devpriv->adc_count >= cmd->stop_arg * cmd->chanlist_len) {  /* end of acquisition */
 583                        das16m1_cancel(dev, s);
 584                        async->events |= COMEDI_CB_EOA;
 585                }
 586        }
 587
 588        /* this probably won't catch overruns since the card doesn't generate
 589         * overrun interrupts, but we might as well try */
 590        if (status & OVRUN) {
 591                das16m1_cancel(dev, s);
 592                async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
 593                comedi_error(dev, "fifo overflow");
 594        }
 595
 596        comedi_event(dev, s);
 597
 598}
 599
 600/* This function takes a time in nanoseconds and sets the     *
 601 * 2 pacer clocks to the closest frequency possible. It also  *
 602 * returns the actual sampling period.                        */
 603static unsigned int das16m1_set_pacer(struct comedi_device *dev,
 604                                      unsigned int ns, int rounding_flags)
 605{
 606        i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL, &(devpriv->divisor1),
 607                                       &(devpriv->divisor2), &ns,
 608                                       rounding_flags & TRIG_ROUND_MASK);
 609
 610        /* Write the values of ctr1 and ctr2 into counters 1 and 2 */
 611        i8254_load(dev->iobase + DAS16M1_8254_SECOND, 0, 1, devpriv->divisor1,
 612                   2);
 613        i8254_load(dev->iobase + DAS16M1_8254_SECOND, 0, 2, devpriv->divisor2,
 614                   2);
 615
 616        return ns;
 617}
 618
 619static int das16m1_irq_bits(unsigned int irq)
 620{
 621        int ret;
 622
 623        switch (irq) {
 624        case 10:
 625                ret = 0x0;
 626                break;
 627        case 11:
 628                ret = 0x1;
 629                break;
 630        case 12:
 631                ret = 0x2;
 632                break;
 633        case 15:
 634                ret = 0x3;
 635                break;
 636        case 2:
 637                ret = 0x4;
 638                break;
 639        case 3:
 640                ret = 0x5;
 641                break;
 642        case 5:
 643                ret = 0x6;
 644                break;
 645        case 7:
 646                ret = 0x7;
 647                break;
 648        default:
 649                return -1;
 650                break;
 651        }
 652        return ret << 4;
 653}
 654
 655/*
 656 * Options list:
 657 *   0  I/O base
 658 *   1  IRQ
 659 */
 660
 661static int das16m1_attach(struct comedi_device *dev,
 662                          struct comedi_devconfig *it)
 663{
 664        struct comedi_subdevice *s;
 665        int ret;
 666        unsigned int irq;
 667        unsigned long iobase;
 668
 669        iobase = it->options[0];
 670
 671        printk("comedi%d: das16m1:", dev->minor);
 672
 673        ret = alloc_private(dev, sizeof(struct das16m1_private_struct));
 674        if (ret < 0)
 675                return ret;
 676
 677        dev->board_name = thisboard->name;
 678
 679        printk(" io 0x%lx-0x%lx 0x%lx-0x%lx",
 680               iobase, iobase + DAS16M1_SIZE,
 681               iobase + DAS16M1_82C55, iobase + DAS16M1_82C55 + DAS16M1_SIZE2);
 682        if (!request_region(iobase, DAS16M1_SIZE, driver_das16m1.driver_name)) {
 683                printk(" I/O port conflict\n");
 684                return -EIO;
 685        }
 686        if (!request_region(iobase + DAS16M1_82C55, DAS16M1_SIZE2,
 687                            driver_das16m1.driver_name)) {
 688                release_region(iobase, DAS16M1_SIZE);
 689                printk(" I/O port conflict\n");
 690                return -EIO;
 691        }
 692        dev->iobase = iobase;
 693
 694        /* now for the irq */
 695        irq = it->options[1];
 696        /*  make sure it is valid */
 697        if (das16m1_irq_bits(irq) >= 0) {
 698                ret = request_irq(irq, das16m1_interrupt, 0,
 699                                  driver_das16m1.driver_name, dev);
 700                if (ret < 0) {
 701                        printk(", irq unavailable\n");
 702                        return ret;
 703                }
 704                dev->irq = irq;
 705                printk(", irq %u\n", irq);
 706        } else if (irq == 0) {
 707                printk(", no irq\n");
 708        } else {
 709                printk(", invalid irq\n"
 710                       " valid irqs are 2, 3, 5, 7, 10, 11, 12, or 15\n");
 711                return -EINVAL;
 712        }
 713
 714        ret = alloc_subdevices(dev, 4);
 715        if (ret < 0)
 716                return ret;
 717
 718        s = dev->subdevices + 0;
 719        dev->read_subdev = s;
 720        /* ai */
 721        s->type = COMEDI_SUBD_AI;
 722        s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
 723        s->n_chan = 8;
 724        s->subdev_flags = SDF_DIFF;
 725        s->len_chanlist = 256;
 726        s->maxdata = (1 << 12) - 1;
 727        s->range_table = &range_das16m1;
 728        s->insn_read = das16m1_ai_rinsn;
 729        s->do_cmdtest = das16m1_cmd_test;
 730        s->do_cmd = das16m1_cmd_exec;
 731        s->cancel = das16m1_cancel;
 732        s->poll = das16m1_poll;
 733
 734        s = dev->subdevices + 1;
 735        /* di */
 736        s->type = COMEDI_SUBD_DI;
 737        s->subdev_flags = SDF_READABLE;
 738        s->n_chan = 4;
 739        s->maxdata = 1;
 740        s->range_table = &range_digital;
 741        s->insn_bits = das16m1_di_rbits;
 742
 743        s = dev->subdevices + 2;
 744        /* do */
 745        s->type = COMEDI_SUBD_DO;
 746        s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
 747        s->n_chan = 4;
 748        s->maxdata = 1;
 749        s->range_table = &range_digital;
 750        s->insn_bits = das16m1_do_wbits;
 751
 752        s = dev->subdevices + 3;
 753        /* 8255 */
 754        subdev_8255_init(dev, s, NULL, dev->iobase + DAS16M1_82C55);
 755
 756        /*  disable upper half of hardware conversion counter so it doesn't mess with us */
 757        outb(TOTAL_CLEAR, dev->iobase + DAS16M1_8254_FIRST_CNTRL);
 758
 759        /*  initialize digital output lines */
 760        outb(devpriv->do_bits, dev->iobase + DAS16M1_DIO);
 761
 762        /* set the interrupt level */
 763        if (dev->irq)
 764                devpriv->control_state = das16m1_irq_bits(dev->irq);
 765        else
 766                devpriv->control_state = 0;
 767        outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
 768
 769        return 0;
 770}
 771
 772static int das16m1_detach(struct comedi_device *dev)
 773{
 774        printk("comedi%d: das16m1: remove\n", dev->minor);
 775
 776/* das16m1_reset(dev); */
 777
 778        if (dev->subdevices)
 779                subdev_8255_cleanup(dev, dev->subdevices + 3);
 780
 781        if (dev->irq)
 782                free_irq(dev->irq, dev);
 783
 784        if (dev->iobase) {
 785                release_region(dev->iobase, DAS16M1_SIZE);
 786                release_region(dev->iobase + DAS16M1_82C55, DAS16M1_SIZE2);
 787        }
 788
 789        return 0;
 790}
 791
 792MODULE_AUTHOR("Comedi http://www.comedi.org");
 793MODULE_DESCRIPTION("Comedi low-level driver");
 794MODULE_LICENSE("GPL");
 795