linux/drivers/staging/comedi/drivers/pcl816.c
<<
>>
Prefs
   1/*
   2   comedi/drivers/pcl816.c
   3
   4   Author:  Juan Grigera <juan@grigera.com.ar>
   5            based on pcl818 by Michal Dobes <dobes@tesnet.cz> and bits of pcl812
   6
   7   hardware driver for Advantech cards:
   8    card:   PCL-816, PCL814B
   9    driver: pcl816
  10*/
  11/*
  12Driver: pcl816
  13Description: Advantech PCL-816 cards, PCL-814
  14Author: Juan Grigera <juan@grigera.com.ar>
  15Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b)
  16Status: works
  17Updated: Tue,  2 Apr 2002 23:15:21 -0800
  18
  19PCL 816 and 814B have 16 SE/DIFF ADCs, 16 DACs, 16 DI and 16 DO.
  20Differences are at resolution (16 vs 12 bits).
  21
  22The driver support AI command mode, other subdevices not written.
  23
  24Analog output and digital input and output are not supported.
  25
  26Configuration Options:
  27  [0] - IO Base
  28  [1] - IRQ     (0=disable, 2, 3, 4, 5, 6, 7)
  29  [2] - DMA     (0=disable, 1, 3)
  30  [3] - 0, 10=10MHz clock for 8254
  31            1= 1MHz clock for 8254
  32
  33*/
  34
  35#include "../comedidev.h"
  36
  37#include <linux/ioport.h>
  38#include <linux/mc146818rtc.h>
  39#include <linux/delay.h>
  40#include <asm/dma.h>
  41
  42#include "8253.h"
  43
  44#define DEBUG(x) x
  45
  46/* boards constants */
  47/* IO space len */
  48#define PCLx1x_RANGE 16
  49
  50/* #define outb(x,y)  printk("OUTB(%x, 200+%d)\n", x,y-0x200); outb(x,y) */
  51
  52/* INTEL 8254 counters */
  53#define PCL816_CTR0 4
  54#define PCL816_CTR1 5
  55#define PCL816_CTR2 6
  56/* R: counter read-back register W: counter control */
  57#define PCL816_CTRCTL 7
  58
  59/* R: A/D high byte W: A/D range control */
  60#define PCL816_RANGE 9
  61/* W: clear INT request */
  62#define PCL816_CLRINT 10
  63/* R: next mux scan channel W: mux scan channel & range control pointer */
  64#define PCL816_MUX 11
  65/* R/W: operation control register */
  66#define PCL816_CONTROL 12
  67
  68/* R: return status byte  W: set DMA/IRQ */
  69#define PCL816_STATUS 13
  70#define PCL816_STATUS_DRDY_MASK 0x80
  71
  72/* R: low byte of A/D W: soft A/D trigger */
  73#define PCL816_AD_LO 8
  74/* R: high byte of A/D W: A/D range control */
  75#define PCL816_AD_HI 9
  76
  77/* type of interrupt handler */
  78#define INT_TYPE_AI1_INT 1
  79#define INT_TYPE_AI1_DMA 2
  80#define INT_TYPE_AI3_INT 4
  81#define INT_TYPE_AI3_DMA 5
  82#ifdef unused
  83#define INT_TYPE_AI1_DMA_RTC 9
  84#define INT_TYPE_AI3_DMA_RTC 10
  85
  86/* RTC stuff... */
  87#define RTC_IRQ         8
  88#define RTC_IO_EXTENT   0x10
  89#endif
  90
  91#define MAGIC_DMA_WORD 0x5a5a
  92
  93static const struct comedi_lrange range_pcl816 = { 8, {
  94                                                       BIP_RANGE(10),
  95                                                       BIP_RANGE(5),
  96                                                       BIP_RANGE(2.5),
  97                                                       BIP_RANGE(1.25),
  98                                                       UNI_RANGE(10),
  99                                                       UNI_RANGE(5),
 100                                                       UNI_RANGE(2.5),
 101                                                       UNI_RANGE(1.25),
 102                                                       }
 103};
 104
 105struct pcl816_board {
 106
 107        const char *name;       /*  board name */
 108        int n_ranges;           /*  len of range list */
 109        int n_aichan;           /*  num of A/D chans in diferencial mode */
 110        unsigned int ai_ns_min; /*  minimal alllowed delay between samples (in ns) */
 111        int n_aochan;           /*  num of D/A chans */
 112        int n_dichan;           /*  num of DI chans */
 113        int n_dochan;           /*  num of DO chans */
 114        const struct comedi_lrange *ai_range_type;      /*  default A/D rangelist */
 115        const struct comedi_lrange *ao_range_type;      /*  dafault D/A rangelist */
 116        unsigned int io_range;  /*  len of IO space */
 117        unsigned int IRQbits;   /*  allowed interrupts */
 118        unsigned int DMAbits;   /*  allowed DMA chans */
 119        int ai_maxdata;         /*  maxdata for A/D */
 120        int ao_maxdata;         /*  maxdata for D/A */
 121        int ai_chanlist;        /*  allowed len of channel list A/D */
 122        int ao_chanlist;        /*  allowed len of channel list D/A */
 123        int i8254_osc_base;     /*  1/frequency of on board oscilator in ns */
 124};
 125
 126static const struct pcl816_board boardtypes[] = {
 127        {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816,
 128         &range_pcl816, PCLx1x_RANGE,
 129         0x00fc,                /*  IRQ mask */
 130         0x0a,                  /*  DMA mask */
 131         0xffff,                /*  16-bit card */
 132         0xffff,                /*  D/A maxdata */
 133         1024,
 134         1,                     /*  ao chan list */
 135         100},
 136        {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816,
 137         &range_pcl816, PCLx1x_RANGE,
 138         0x00fc,
 139         0x0a,
 140         0x3fff,                /* 14 bit card */
 141         0x3fff,
 142         1024,
 143         1,
 144         100},
 145};
 146
 147#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl816_board))
 148#define devpriv ((struct pcl816_private *)dev->private)
 149#define this_board ((const struct pcl816_board *)dev->board_ptr)
 150
 151static int pcl816_attach(struct comedi_device *dev,
 152                         struct comedi_devconfig *it);
 153static int pcl816_detach(struct comedi_device *dev);
 154
 155#ifdef unused
 156static int RTC_lock = 0;        /* RTC lock */
 157static int RTC_timer_lock = 0;  /* RTC int lock */
 158#endif
 159
 160static struct comedi_driver driver_pcl816 = {
 161        .driver_name = "pcl816",
 162        .module = THIS_MODULE,
 163        .attach = pcl816_attach,
 164        .detach = pcl816_detach,
 165        .board_name = &boardtypes[0].name,
 166        .num_names = n_boardtypes,
 167        .offset = sizeof(struct pcl816_board),
 168};
 169
 170COMEDI_INITCLEANUP(driver_pcl816);
 171
 172struct pcl816_private {
 173
 174        unsigned int dma;       /*  used DMA, 0=don't use DMA */
 175        int dma_rtc;            /*  1=RTC used with DMA, 0=no RTC alloc */
 176#ifdef unused
 177        unsigned long rtc_iobase;       /*  RTC port region */
 178        unsigned int rtc_iosize;
 179        unsigned int rtc_irq;
 180#endif
 181        unsigned long dmabuf[2];        /*  pointers to begin of DMA buffers */
 182        unsigned int dmapages[2];       /*  len of DMA buffers in PAGE_SIZEs */
 183        unsigned int hwdmaptr[2];       /*  hardware address of DMA buffers */
 184        unsigned int hwdmasize[2];      /*  len of DMA buffers in Bytes */
 185        unsigned int dmasamplsize;      /*  size in samples hwdmasize[0]/2 */
 186        unsigned int last_top_dma;      /*  DMA pointer in last RTC int */
 187        int next_dma_buf;       /*  which DMA buffer will be used next round */
 188        long dma_runs_to_end;   /*  how many we must permorm DMA transfer to end of record */
 189        unsigned long last_dma_run;     /*  how many bytes we must transfer on last DMA page */
 190
 191        unsigned int ai_scans;  /*  len of scanlist */
 192        unsigned char ai_neverending;   /*  if=1, then we do neverending record (you must use cancel()) */
 193        int irq_free;           /*  1=have allocated IRQ */
 194        int irq_blocked;        /*  1=IRQ now uses any subdev */
 195#ifdef unused
 196        int rtc_irq_blocked;    /*  1=we now do AI with DMA&RTC */
 197#endif
 198        int irq_was_now_closed; /*  when IRQ finish, there's stored int816_mode for last interrupt */
 199        int int816_mode;        /*  who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
 200        struct comedi_subdevice *last_int_sub;  /*  ptr to subdevice which now finish */
 201        int ai_act_scan;        /*  how many scans we finished */
 202        unsigned int ai_act_chanlist[16];       /*  MUX setting for actual AI operations */
 203        unsigned int ai_act_chanlist_len;       /*  how long is actual MUX list */
 204        unsigned int ai_act_chanlist_pos;       /*  actual position in MUX list */
 205        unsigned int ai_poll_ptr;       /*  how many sampes transfer poll */
 206        struct comedi_subdevice *sub_ai;        /*  ptr to AI subdevice */
 207#ifdef unused
 208        struct timer_list rtc_irq_timer;        /*  timer for RTC sanity check */
 209        unsigned long rtc_freq; /*  RTC int freq */
 210#endif
 211};
 212
 213/*
 214==============================================================================
 215*/
 216static int check_and_setup_channel_list(struct comedi_device *dev,
 217                                        struct comedi_subdevice *s,
 218                                        unsigned int *chanlist, int chanlen);
 219static int pcl816_ai_cancel(struct comedi_device *dev,
 220                            struct comedi_subdevice *s);
 221static void start_pacer(struct comedi_device *dev, int mode,
 222                        unsigned int divisor1, unsigned int divisor2);
 223#ifdef unused
 224static int set_rtc_irq_bit(unsigned char bit);
 225#endif
 226
 227static int pcl816_ai_cmdtest(struct comedi_device *dev,
 228                             struct comedi_subdevice *s,
 229                             struct comedi_cmd *cmd);
 230static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
 231
 232/*
 233==============================================================================
 234   ANALOG INPUT MODE0, 816 cards, slow version
 235*/
 236static int pcl816_ai_insn_read(struct comedi_device *dev,
 237                               struct comedi_subdevice *s,
 238                               struct comedi_insn *insn, unsigned int *data)
 239{
 240        int n;
 241        int timeout;
 242
 243        DPRINTK("mode 0 analog input\n");
 244        /*  software trigger, DMA and INT off */
 245        outb(0, dev->iobase + PCL816_CONTROL);
 246        /*  clear INT (conversion end) flag */
 247        outb(0, dev->iobase + PCL816_CLRINT);
 248
 249        /*  Set the input channel */
 250        outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
 251        outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE);     /* select gain */
 252
 253        for (n = 0; n < insn->n; n++) {
 254
 255                outb(0, dev->iobase + PCL816_AD_LO);    /* start conversion */
 256
 257                timeout = 100;
 258                while (timeout--) {
 259                        if (!(inb(dev->iobase + PCL816_STATUS) &
 260                              PCL816_STATUS_DRDY_MASK)) {
 261                                /*  return read value */
 262                                data[n] =
 263                                    ((inb(dev->iobase +
 264                                          PCL816_AD_HI) << 8) |
 265                                     (inb(dev->iobase + PCL816_AD_LO)));
 266
 267                                outb(0, dev->iobase + PCL816_CLRINT);   /* clear INT (conversion end) flag */
 268                                break;
 269                        }
 270                        udelay(1);
 271                }
 272                /*  Return timeout error */
 273                if (!timeout) {
 274                        comedi_error(dev, "A/D insn timeout\n");
 275                        data[0] = 0;
 276                        outb(0, dev->iobase + PCL816_CLRINT);   /* clear INT (conversion end) flag */
 277                        return -EIO;
 278                }
 279
 280        }
 281        return n;
 282}
 283
 284/*
 285==============================================================================
 286   analog input interrupt mode 1 & 3, 818 cards
 287   one sample per interrupt version
 288*/
 289static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
 290{
 291        struct comedi_device *dev = d;
 292        struct comedi_subdevice *s = dev->subdevices + 0;
 293        int low, hi;
 294        int timeout = 50;       /* wait max 50us */
 295
 296        while (timeout--) {
 297                if (!(inb(dev->iobase + PCL816_STATUS) &
 298                      PCL816_STATUS_DRDY_MASK))
 299                        break;
 300                udelay(1);
 301        }
 302        if (!timeout) {         /*  timeout, bail error */
 303                outb(0, dev->iobase + PCL816_CLRINT);   /* clear INT request */
 304                comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
 305                pcl816_ai_cancel(dev, s);
 306                s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
 307                comedi_event(dev, s);
 308                return IRQ_HANDLED;
 309
 310        }
 311
 312        /*  get the sample */
 313        low = inb(dev->iobase + PCL816_AD_LO);
 314        hi = inb(dev->iobase + PCL816_AD_HI);
 315
 316        comedi_buf_put(s->async, (hi << 8) | low);
 317
 318        outb(0, dev->iobase + PCL816_CLRINT);   /* clear INT request */
 319
 320        if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
 321                devpriv->ai_act_chanlist_pos = 0;
 322
 323        if (s->async->cur_chan == 0) {
 324                devpriv->ai_act_scan++;
 325        }
 326
 327        if (!devpriv->ai_neverending)
 328                if (devpriv->ai_act_scan >= devpriv->ai_scans) {        /* all data sampled */
 329                        /* all data sampled */
 330                        pcl816_ai_cancel(dev, s);
 331                        s->async->events |= COMEDI_CB_EOA;
 332                }
 333        comedi_event(dev, s);
 334        return IRQ_HANDLED;
 335}
 336
 337/*
 338==============================================================================
 339   analog input dma mode 1 & 3, 816 cards
 340*/
 341static void transfer_from_dma_buf(struct comedi_device *dev,
 342                                  struct comedi_subdevice *s, short *ptr,
 343                                  unsigned int bufptr, unsigned int len)
 344{
 345        int i;
 346
 347        s->async->events = 0;
 348
 349        for (i = 0; i < len; i++) {
 350
 351                comedi_buf_put(s->async, ptr[bufptr++]);
 352
 353                if (++devpriv->ai_act_chanlist_pos >=
 354                    devpriv->ai_act_chanlist_len) {
 355                        devpriv->ai_act_chanlist_pos = 0;
 356                        devpriv->ai_act_scan++;
 357                }
 358
 359                if (!devpriv->ai_neverending)
 360                        if (devpriv->ai_act_scan >= devpriv->ai_scans) {        /*  all data sampled */
 361                                pcl816_ai_cancel(dev, s);
 362                                s->async->events |= COMEDI_CB_EOA;
 363                                s->async->events |= COMEDI_CB_BLOCK;
 364                                break;
 365                        }
 366        }
 367
 368        comedi_event(dev, s);
 369}
 370
 371static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
 372{
 373        struct comedi_device *dev = d;
 374        struct comedi_subdevice *s = dev->subdevices + 0;
 375        int len, bufptr, this_dma_buf;
 376        unsigned long dma_flags;
 377        short *ptr;
 378
 379        disable_dma(devpriv->dma);
 380        this_dma_buf = devpriv->next_dma_buf;
 381
 382        if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) {       /*  switch dma bufs */
 383
 384                devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
 385                set_dma_mode(devpriv->dma, DMA_MODE_READ);
 386                dma_flags = claim_dma_lock();
 387/* clear_dma_ff (devpriv->dma); */
 388                set_dma_addr(devpriv->dma,
 389                             devpriv->hwdmaptr[devpriv->next_dma_buf]);
 390                if (devpriv->dma_runs_to_end) {
 391                        set_dma_count(devpriv->dma,
 392                                      devpriv->hwdmasize[devpriv->
 393                                                         next_dma_buf]);
 394                } else {
 395                        set_dma_count(devpriv->dma, devpriv->last_dma_run);
 396                }
 397                release_dma_lock(dma_flags);
 398                enable_dma(devpriv->dma);
 399        }
 400
 401        devpriv->dma_runs_to_end--;
 402        outb(0, dev->iobase + PCL816_CLRINT);   /* clear INT request */
 403
 404        ptr = (short *)devpriv->dmabuf[this_dma_buf];
 405
 406        len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
 407        bufptr = devpriv->ai_poll_ptr;
 408        devpriv->ai_poll_ptr = 0;
 409
 410        transfer_from_dma_buf(dev, s, ptr, bufptr, len);
 411        return IRQ_HANDLED;
 412}
 413
 414/*
 415==============================================================================
 416    INT procedure
 417*/
 418static irqreturn_t interrupt_pcl816(int irq, void *d)
 419{
 420        struct comedi_device *dev = d;
 421        DPRINTK("<I>");
 422
 423        if (!dev->attached) {
 424                comedi_error(dev, "premature interrupt");
 425                return IRQ_HANDLED;
 426        }
 427
 428        switch (devpriv->int816_mode) {
 429        case INT_TYPE_AI1_DMA:
 430        case INT_TYPE_AI3_DMA:
 431                return interrupt_pcl816_ai_mode13_dma(irq, d);
 432        case INT_TYPE_AI1_INT:
 433        case INT_TYPE_AI3_INT:
 434                return interrupt_pcl816_ai_mode13_int(irq, d);
 435        }
 436
 437        outb(0, dev->iobase + PCL816_CLRINT);   /* clear INT request */
 438        if ((!dev->irq) | (!devpriv->irq_free) | (!devpriv->irq_blocked) |
 439            (!devpriv->int816_mode)) {
 440                if (devpriv->irq_was_now_closed) {
 441                        devpriv->irq_was_now_closed = 0;
 442                        /*  comedi_error(dev,"last IRQ.."); */
 443                        return IRQ_HANDLED;
 444                }
 445                comedi_error(dev, "bad IRQ!");
 446                return IRQ_NONE;
 447        }
 448        comedi_error(dev, "IRQ from unknow source!");
 449        return IRQ_NONE;
 450}
 451
 452/*
 453==============================================================================
 454   COMMAND MODE
 455*/
 456static void pcl816_cmdtest_out(int e, struct comedi_cmd *cmd)
 457{
 458        printk("pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
 459               cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
 460        printk("pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
 461               cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
 462        printk("pcl816 e=%d stopsrc=%x scanend=%x\n", e, cmd->stop_src,
 463               cmd->scan_end_src);
 464        printk("pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n", e,
 465               cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
 466}
 467
 468/*
 469==============================================================================
 470*/
 471static int pcl816_ai_cmdtest(struct comedi_device *dev,
 472                             struct comedi_subdevice *s, struct comedi_cmd *cmd)
 473{
 474        int err = 0;
 475        int tmp, divisor1, divisor2;
 476
 477        DEBUG(printk("pcl816 pcl812_ai_cmdtest\n"); pcl816_cmdtest_out(-1, cmd);
 478            );
 479
 480        /* step 1: make sure trigger sources are trivially valid */
 481        tmp = cmd->start_src;
 482        cmd->start_src &= TRIG_NOW;
 483        if (!cmd->start_src || tmp != cmd->start_src)
 484                err++;
 485
 486        tmp = cmd->scan_begin_src;
 487        cmd->scan_begin_src &= TRIG_FOLLOW;
 488        if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
 489                err++;
 490
 491        if (!(cmd->convert_src & (TRIG_EXT | TRIG_TIMER)))
 492                err++;
 493
 494        tmp = cmd->scan_end_src;
 495        cmd->scan_end_src &= TRIG_COUNT;
 496        if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
 497                err++;
 498
 499        tmp = cmd->stop_src;
 500        cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
 501        if (!cmd->stop_src || tmp != cmd->stop_src)
 502                err++;
 503
 504        if (err) {
 505                return 1;
 506        }
 507
 508        /* step 2: make sure trigger sources are unique and mutually compatible */
 509
 510        if (cmd->start_src != TRIG_NOW) {
 511                cmd->start_src = TRIG_NOW;
 512                err++;
 513        }
 514
 515        if (cmd->scan_begin_src != TRIG_FOLLOW) {
 516                cmd->scan_begin_src = TRIG_FOLLOW;
 517                err++;
 518        }
 519
 520        if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) {
 521                cmd->convert_src = TRIG_TIMER;
 522                err++;
 523        }
 524
 525        if (cmd->scan_end_src != TRIG_COUNT) {
 526                cmd->scan_end_src = TRIG_COUNT;
 527                err++;
 528        }
 529
 530        if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
 531                err++;
 532
 533        if (err) {
 534                return 2;
 535        }
 536
 537        /* step 3: make sure arguments are trivially compatible */
 538        if (cmd->start_arg != 0) {
 539                cmd->start_arg = 0;
 540                err++;
 541        }
 542
 543        if (cmd->scan_begin_arg != 0) {
 544                cmd->scan_begin_arg = 0;
 545                err++;
 546        }
 547        if (cmd->convert_src == TRIG_TIMER) {
 548                if (cmd->convert_arg < this_board->ai_ns_min) {
 549                        cmd->convert_arg = this_board->ai_ns_min;
 550                        err++;
 551                }
 552        } else {                /* TRIG_EXT */
 553                if (cmd->convert_arg != 0) {
 554                        cmd->convert_arg = 0;
 555                        err++;
 556                }
 557        }
 558
 559        if (!cmd->chanlist_len) {
 560                cmd->chanlist_len = 1;
 561                err++;
 562        }
 563        if (cmd->chanlist_len > this_board->n_aichan) {
 564                cmd->chanlist_len = this_board->n_aichan;
 565                err++;
 566        }
 567        if (cmd->scan_end_arg != cmd->chanlist_len) {
 568                cmd->scan_end_arg = cmd->chanlist_len;
 569                err++;
 570        }
 571        if (cmd->stop_src == TRIG_COUNT) {
 572                if (!cmd->stop_arg) {
 573                        cmd->stop_arg = 1;
 574                        err++;
 575                }
 576        } else {                /* TRIG_NONE */
 577                if (cmd->stop_arg != 0) {
 578                        cmd->stop_arg = 0;
 579                        err++;
 580                }
 581        }
 582
 583        if (err) {
 584                return 3;
 585        }
 586
 587        /* step 4: fix up any arguments */
 588        if (cmd->convert_src == TRIG_TIMER) {
 589                tmp = cmd->convert_arg;
 590                i8253_cascade_ns_to_timer(this_board->i8254_osc_base,
 591                                          &divisor1, &divisor2,
 592                                          &cmd->convert_arg,
 593                                          cmd->flags & TRIG_ROUND_MASK);
 594                if (cmd->convert_arg < this_board->ai_ns_min)
 595                        cmd->convert_arg = this_board->ai_ns_min;
 596                if (tmp != cmd->convert_arg)
 597                        err++;
 598        }
 599
 600        if (err) {
 601                return 4;
 602        }
 603
 604        return 0;
 605}
 606
 607static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 608{
 609        unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
 610        struct comedi_cmd *cmd = &s->async->cmd;
 611
 612        if (cmd->start_src != TRIG_NOW)
 613                return -EINVAL;
 614        if (cmd->scan_begin_src != TRIG_FOLLOW)
 615                return -EINVAL;
 616        if (cmd->scan_end_src != TRIG_COUNT)
 617                return -EINVAL;
 618        if (cmd->scan_end_arg != cmd->chanlist_len)
 619                return -EINVAL;
 620/* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */
 621        if (devpriv->irq_blocked)
 622                return -EBUSY;
 623
 624        if (cmd->convert_src == TRIG_TIMER) {
 625                if (cmd->convert_arg < this_board->ai_ns_min)
 626                        cmd->convert_arg = this_board->ai_ns_min;
 627
 628                i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1,
 629                                          &divisor2, &cmd->convert_arg,
 630                                          cmd->flags & TRIG_ROUND_MASK);
 631                if (divisor1 == 1) {    /*  PCL816 crash if any divisor is set to 1 */
 632                        divisor1 = 2;
 633                        divisor2 /= 2;
 634                }
 635                if (divisor2 == 1) {
 636                        divisor2 = 2;
 637                        divisor1 /= 2;
 638                }
 639        }
 640
 641        start_pacer(dev, -1, 0, 0);     /*  stop pacer */
 642
 643        if (!check_and_setup_channel_list(dev, s, cmd->chanlist,
 644                                          cmd->chanlist_len))
 645                return -EINVAL;
 646        udelay(1);
 647
 648        devpriv->ai_act_scan = 0;
 649        s->async->cur_chan = 0;
 650        devpriv->irq_blocked = 1;
 651        devpriv->ai_poll_ptr = 0;
 652        devpriv->irq_was_now_closed = 0;
 653
 654        if (cmd->stop_src == TRIG_COUNT) {
 655                devpriv->ai_scans = cmd->stop_arg;
 656                devpriv->ai_neverending = 0;
 657        } else {
 658                devpriv->ai_scans = 0;
 659                devpriv->ai_neverending = 1;
 660        }
 661
 662        if ((cmd->flags & TRIG_WAKE_EOS)) {     /*  don't we want wake up every scan? */
 663                printk("pl816: You wankt WAKE_EOS but I dont want handle it");
 664                /*               devpriv->ai_eos=1; */
 665                /* if (devpriv->ai_n_chan==1) */
 666                /*       devpriv->dma=0; // DMA is useless for this situation */
 667        }
 668
 669        if (devpriv->dma) {
 670                bytes = devpriv->hwdmasize[0];
 671                if (!devpriv->ai_neverending) {
 672                        bytes = s->async->cmd.chanlist_len * s->async->cmd.chanlist_len * sizeof(short);        /*  how many */
 673                        devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize[0];       /*  how many DMA pages we must fill */
 674                        devpriv->last_dma_run = bytes % devpriv->hwdmasize[0];  /* on last dma transfer must be moved */
 675                        devpriv->dma_runs_to_end--;
 676                        if (devpriv->dma_runs_to_end >= 0)
 677                                bytes = devpriv->hwdmasize[0];
 678                } else
 679                        devpriv->dma_runs_to_end = -1;
 680
 681                devpriv->next_dma_buf = 0;
 682                set_dma_mode(devpriv->dma, DMA_MODE_READ);
 683                dma_flags = claim_dma_lock();
 684                clear_dma_ff(devpriv->dma);
 685                set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
 686                set_dma_count(devpriv->dma, bytes);
 687                release_dma_lock(dma_flags);
 688                enable_dma(devpriv->dma);
 689        }
 690
 691        start_pacer(dev, 1, divisor1, divisor2);
 692        dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
 693
 694        switch (cmd->convert_src) {
 695        case TRIG_TIMER:
 696                devpriv->int816_mode = INT_TYPE_AI1_DMA;
 697                outb(0x32, dev->iobase + PCL816_CONTROL);       /*  Pacer+IRQ+DMA */
 698                outb(dmairq, dev->iobase + PCL816_STATUS);      /*  write irq and DMA to card */
 699                break;
 700
 701        default:
 702                devpriv->int816_mode = INT_TYPE_AI3_DMA;
 703                outb(0x34, dev->iobase + PCL816_CONTROL);       /*  Ext trig+IRQ+DMA */
 704                outb(dmairq, dev->iobase + PCL816_STATUS);      /*  write irq to card */
 705                break;
 706        }
 707
 708        DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
 709        return 0;
 710}
 711
 712static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
 713{
 714        unsigned long flags;
 715        unsigned int top1, top2, i;
 716
 717        if (!devpriv->dma)
 718                return 0;       /*  poll is valid only for DMA transfer */
 719
 720        spin_lock_irqsave(&dev->spinlock, flags);
 721
 722        for (i = 0; i < 20; i++) {
 723                top1 = get_dma_residue(devpriv->dma);   /*  where is now DMA */
 724                top2 = get_dma_residue(devpriv->dma);
 725                if (top1 == top2)
 726                        break;
 727        }
 728        if (top1 != top2) {
 729                spin_unlock_irqrestore(&dev->spinlock, flags);
 730                return 0;
 731        }
 732
 733        top1 = devpriv->hwdmasize[0] - top1;    /*  where is now DMA in buffer */
 734        top1 >>= 1;             /*  sample position */
 735        top2 = top1 - devpriv->ai_poll_ptr;
 736        if (top2 < 1) {         /*  no new samples */
 737                spin_unlock_irqrestore(&dev->spinlock, flags);
 738                return 0;
 739        }
 740
 741        transfer_from_dma_buf(dev, s,
 742                              (short *)devpriv->dmabuf[devpriv->next_dma_buf],
 743                              devpriv->ai_poll_ptr, top2);
 744
 745        devpriv->ai_poll_ptr = top1;    /*  new buffer position */
 746        spin_unlock_irqrestore(&dev->spinlock, flags);
 747
 748        return s->async->buf_write_count - s->async->buf_read_count;
 749}
 750
 751/*
 752==============================================================================
 753 cancel any mode 1-4 AI
 754*/
 755static int pcl816_ai_cancel(struct comedi_device *dev,
 756                            struct comedi_subdevice *s)
 757{
 758/* DEBUG(printk("pcl816_ai_cancel()\n");) */
 759
 760        if (devpriv->irq_blocked > 0) {
 761                switch (devpriv->int816_mode) {
 762#ifdef unused
 763                case INT_TYPE_AI1_DMA_RTC:
 764                case INT_TYPE_AI3_DMA_RTC:
 765                        set_rtc_irq_bit(0);     /*  stop RTC */
 766                        del_timer(&devpriv->rtc_irq_timer);
 767#endif
 768                case INT_TYPE_AI1_DMA:
 769                case INT_TYPE_AI3_DMA:
 770                        disable_dma(devpriv->dma);
 771                case INT_TYPE_AI1_INT:
 772                case INT_TYPE_AI3_INT:
 773                        outb(inb(dev->iobase + PCL816_CONTROL) & 0x73, dev->iobase + PCL816_CONTROL);   /* Stop A/D */
 774                        udelay(1);
 775                        outb(0, dev->iobase + PCL816_CONTROL);  /* Stop A/D */
 776                        outb(0xb0, dev->iobase + PCL816_CTRCTL);        /* Stop pacer */
 777                        outb(0x70, dev->iobase + PCL816_CTRCTL);
 778                        outb(0, dev->iobase + PCL816_AD_LO);
 779                        inb(dev->iobase + PCL816_AD_LO);
 780                        inb(dev->iobase + PCL816_AD_HI);
 781                        outb(0, dev->iobase + PCL816_CLRINT);   /* clear INT request */
 782                        outb(0, dev->iobase + PCL816_CONTROL);  /* Stop A/D */
 783                        devpriv->irq_blocked = 0;
 784                        devpriv->irq_was_now_closed = devpriv->int816_mode;
 785                        devpriv->int816_mode = 0;
 786                        devpriv->last_int_sub = s;
 787/* s->busy = 0; */
 788                        break;
 789                }
 790        }
 791
 792        DEBUG(printk("comedi: pcl816_ai_cancel() successful\n");)
 793            return 0;
 794}
 795
 796/*
 797==============================================================================
 798 chech for PCL816
 799*/
 800static int pcl816_check(unsigned long iobase)
 801{
 802        outb(0x00, iobase + PCL816_MUX);
 803        udelay(1);
 804        if (inb(iobase + PCL816_MUX) != 0x00)
 805                return 1;       /* there isn't card */
 806        outb(0x55, iobase + PCL816_MUX);
 807        udelay(1);
 808        if (inb(iobase + PCL816_MUX) != 0x55)
 809                return 1;       /* there isn't card */
 810        outb(0x00, iobase + PCL816_MUX);
 811        udelay(1);
 812        outb(0x18, iobase + PCL816_CONTROL);
 813        udelay(1);
 814        if (inb(iobase + PCL816_CONTROL) != 0x18)
 815                return 1;       /* there isn't card */
 816        return 0;               /*  ok, card exist */
 817}
 818
 819/*
 820==============================================================================
 821 reset whole PCL-816 cards
 822*/
 823static void pcl816_reset(struct comedi_device *dev)
 824{
 825/* outb (0, dev->iobase + PCL818_DA_LO);         DAC=0V */
 826/* outb (0, dev->iobase + PCL818_DA_HI); */
 827/* udelay (1); */
 828/* outb (0, dev->iobase + PCL818_DO_HI);        DO=$0000 */
 829/* outb (0, dev->iobase + PCL818_DO_LO); */
 830/* udelay (1); */
 831        outb(0, dev->iobase + PCL816_CONTROL);
 832        outb(0, dev->iobase + PCL816_MUX);
 833        outb(0, dev->iobase + PCL816_CLRINT);
 834        outb(0xb0, dev->iobase + PCL816_CTRCTL);        /* Stop pacer */
 835        outb(0x70, dev->iobase + PCL816_CTRCTL);
 836        outb(0x30, dev->iobase + PCL816_CTRCTL);
 837        outb(0, dev->iobase + PCL816_RANGE);
 838}
 839
 840/*
 841==============================================================================
 842 Start/stop pacer onboard pacer
 843*/
 844static void
 845start_pacer(struct comedi_device *dev, int mode, unsigned int divisor1,
 846            unsigned int divisor2)
 847{
 848        outb(0x32, dev->iobase + PCL816_CTRCTL);
 849        outb(0xff, dev->iobase + PCL816_CTR0);
 850        outb(0x00, dev->iobase + PCL816_CTR0);
 851        udelay(1);
 852        outb(0xb4, dev->iobase + PCL816_CTRCTL);        /*  set counter 2 as mode 3 */
 853        outb(0x74, dev->iobase + PCL816_CTRCTL);        /*  set counter 1 as mode 3 */
 854        udelay(1);
 855
 856        if (mode == 1) {
 857                DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
 858                        divisor2);
 859                outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
 860                outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
 861                outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
 862                outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
 863        }
 864
 865        /* clear pending interrupts (just in case) */
 866/* outb(0, dev->iobase + PCL816_CLRINT); */
 867}
 868
 869/*
 870==============================================================================
 871 Check if channel list from user is builded correctly
 872 If it's ok, then program scan/gain logic
 873*/
 874static int
 875check_and_setup_channel_list(struct comedi_device *dev,
 876                             struct comedi_subdevice *s, unsigned int *chanlist,
 877                             int chanlen)
 878{
 879        unsigned int chansegment[16];
 880        unsigned int i, nowmustbechan, seglen, segpos;
 881
 882        /*  correct channel and range number check itself comedi/range.c */
 883        if (chanlen < 1) {
 884                comedi_error(dev, "range/channel list is empty!");
 885                return 0;
 886        }
 887
 888        if (chanlen > 1) {
 889                chansegment[0] = chanlist[0];   /*  first channel is everytime ok */
 890                for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
 891                        /*  build part of chanlist */
 892                        DEBUG(printk("%d. %d %d\n", i, CR_CHAN(chanlist[i]),
 893                                     CR_RANGE(chanlist[i]));)
 894                            if (chanlist[0] == chanlist[i])
 895                                break;  /*  we detect loop, this must by finish */
 896                        nowmustbechan =
 897                            (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
 898                        if (nowmustbechan != CR_CHAN(chanlist[i])) {
 899                                /*  channel list isn't continous :-( */
 900                                printk
 901                                    ("comedi%d: pcl816: channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
 902                                     dev->minor, i, CR_CHAN(chanlist[i]),
 903                                     nowmustbechan, CR_CHAN(chanlist[0]));
 904                                return 0;
 905                        }
 906                        chansegment[i] = chanlist[i];   /*  well, this is next correct channel in list */
 907                }
 908
 909                for (i = 0, segpos = 0; i < chanlen; i++) {     /*  check whole chanlist */
 910                        DEBUG(printk("%d %d=%d %d\n",
 911                                     CR_CHAN(chansegment[i % seglen]),
 912                                     CR_RANGE(chansegment[i % seglen]),
 913                                     CR_CHAN(chanlist[i]),
 914                                     CR_RANGE(chanlist[i]));)
 915                            if (chanlist[i] != chansegment[i % seglen]) {
 916                                printk
 917                                    ("comedi%d: pcl816: bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
 918                                     dev->minor, i, CR_CHAN(chansegment[i]),
 919                                     CR_RANGE(chansegment[i]),
 920                                     CR_AREF(chansegment[i]),
 921                                     CR_CHAN(chanlist[i % seglen]),
 922                                     CR_RANGE(chanlist[i % seglen]),
 923                                     CR_AREF(chansegment[i % seglen]));
 924                                return 0;       /*  chan/gain list is strange */
 925                        }
 926                }
 927        } else {
 928                seglen = 1;
 929        }
 930
 931        devpriv->ai_act_chanlist_len = seglen;
 932        devpriv->ai_act_chanlist_pos = 0;
 933
 934        for (i = 0; i < seglen; i++) {  /*  store range list to card */
 935                devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
 936                outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
 937                outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE);        /* select gain */
 938        }
 939
 940        udelay(1);
 941
 942        outb(devpriv->ai_act_chanlist[0] | (devpriv->ai_act_chanlist[seglen - 1] << 4), dev->iobase + PCL816_MUX);      /* select channel interval to scan */
 943
 944        return 1;               /*  we can serve this with MUX logic */
 945}
 946
 947#ifdef unused
 948/*
 949==============================================================================
 950  Enable(1)/disable(0) periodic interrupts from RTC
 951*/
 952static int set_rtc_irq_bit(unsigned char bit)
 953{
 954        unsigned char val;
 955        unsigned long flags;
 956
 957        if (bit == 1) {
 958                RTC_timer_lock++;
 959                if (RTC_timer_lock > 1)
 960                        return 0;
 961        } else {
 962                RTC_timer_lock--;
 963                if (RTC_timer_lock < 0)
 964                        RTC_timer_lock = 0;
 965                if (RTC_timer_lock > 0)
 966                        return 0;
 967        }
 968
 969        save_flags(flags);
 970        cli();
 971        val = CMOS_READ(RTC_CONTROL);
 972        if (bit) {
 973                val |= RTC_PIE;
 974        } else {
 975                val &= ~RTC_PIE;
 976        }
 977        CMOS_WRITE(val, RTC_CONTROL);
 978        CMOS_READ(RTC_INTR_FLAGS);
 979        restore_flags(flags);
 980        return 0;
 981}
 982#endif
 983
 984/*
 985==============================================================================
 986  Free any resources that we have claimed
 987*/
 988static void free_resources(struct comedi_device *dev)
 989{
 990        /* printk("free_resource()\n"); */
 991        if (dev->private) {
 992                pcl816_ai_cancel(dev, devpriv->sub_ai);
 993                pcl816_reset(dev);
 994                if (devpriv->dma)
 995                        free_dma(devpriv->dma);
 996                if (devpriv->dmabuf[0])
 997                        free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
 998                if (devpriv->dmabuf[1])
 999                        free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
1000#ifdef unused
1001                if (devpriv->rtc_irq)
1002                        free_irq(devpriv->rtc_irq, dev);
1003                if ((devpriv->dma_rtc) && (RTC_lock == 1)) {
1004                        if (devpriv->rtc_iobase)
1005                                release_region(devpriv->rtc_iobase,
1006                                               devpriv->rtc_iosize);
1007                }
1008#endif
1009        }
1010
1011        if (dev->irq)
1012                free_irq(dev->irq, dev);
1013        if (dev->iobase)
1014                release_region(dev->iobase, this_board->io_range);
1015        /* printk("free_resource() end\n"); */
1016}
1017
1018/*
1019==============================================================================
1020
1021   Initialization
1022
1023*/
1024static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1025{
1026        int ret;
1027        unsigned long iobase;
1028        unsigned int irq, dma;
1029        unsigned long pages;
1030        /* int i; */
1031        struct comedi_subdevice *s;
1032
1033        /* claim our I/O space */
1034        iobase = it->options[0];
1035        printk("comedi%d: pcl816:  board=%s, ioport=0x%03lx", dev->minor,
1036               this_board->name, iobase);
1037
1038        if (!request_region(iobase, this_board->io_range, "pcl816")) {
1039                printk("I/O port conflict\n");
1040                return -EIO;
1041        }
1042
1043        dev->iobase = iobase;
1044
1045        if (pcl816_check(iobase)) {
1046                printk(", I cann't detect board. FAIL!\n");
1047                return -EIO;
1048        }
1049
1050        ret = alloc_private(dev, sizeof(struct pcl816_private));
1051        if (ret < 0)
1052                return ret;     /* Can't alloc mem */
1053
1054        /* set up some name stuff */
1055        dev->board_name = this_board->name;
1056
1057        /* grab our IRQ */
1058        irq = 0;
1059        if (this_board->IRQbits != 0) { /* board support IRQ */
1060                irq = it->options[1];
1061                if (irq) {      /* we want to use IRQ */
1062                        if (((1 << irq) & this_board->IRQbits) == 0) {
1063                                printk
1064                                    (", IRQ %u is out of allowed range, DISABLING IT",
1065                                     irq);
1066                                irq = 0;        /* Bad IRQ */
1067                        } else {
1068                                if (request_irq
1069                                    (irq, interrupt_pcl816, 0, "pcl816", dev)) {
1070                                        printk
1071                                            (", unable to allocate IRQ %u, DISABLING IT",
1072                                             irq);
1073                                        irq = 0;        /* Can't use IRQ */
1074                                } else {
1075                                        printk(", irq=%u", irq);
1076                                }
1077                        }
1078                }
1079        }
1080
1081        dev->irq = irq;
1082        if (irq) {
1083                devpriv->irq_free = 1;
1084        } /* 1=we have allocated irq */
1085        else {
1086                devpriv->irq_free = 0;
1087        }
1088        devpriv->irq_blocked = 0;       /* number of subdevice which use IRQ */
1089        devpriv->int816_mode = 0;       /* mode of irq */
1090
1091#ifdef unused
1092        /* grab RTC for DMA operations */
1093        devpriv->dma_rtc = 0;
1094        if (it->options[2] > 0) {       /*  we want to use DMA */
1095                if (RTC_lock == 0) {
1096                        if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
1097                                            "pcl816 (RTC)"))
1098                                goto no_rtc;
1099                }
1100                devpriv->rtc_iobase = RTC_PORT(0);
1101                devpriv->rtc_iosize = RTC_IO_EXTENT;
1102                RTC_lock++;
1103#ifdef UNTESTED_CODE
1104                if (!request_irq(RTC_IRQ, interrupt_pcl816_ai_mode13_dma_rtc, 0,
1105                                 "pcl816 DMA (RTC)", dev)) {
1106                        devpriv->dma_rtc = 1;
1107                        devpriv->rtc_irq = RTC_IRQ;
1108                        printk(", dma_irq=%u", devpriv->rtc_irq);
1109                } else {
1110                        RTC_lock--;
1111                        if (RTC_lock == 0) {
1112                                if (devpriv->rtc_iobase)
1113                                        release_region(devpriv->rtc_iobase,
1114                                                       devpriv->rtc_iosize);
1115                        }
1116                        devpriv->rtc_iobase = 0;
1117                        devpriv->rtc_iosize = 0;
1118                }
1119#else
1120                printk("pcl816: RTC code missing");
1121#endif
1122
1123        }
1124
1125no_rtc:
1126#endif
1127        /* grab our DMA */
1128        dma = 0;
1129        devpriv->dma = dma;
1130        if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
1131                goto no_dma;    /* if we haven't IRQ, we can't use DMA */
1132
1133        if (this_board->DMAbits != 0) { /* board support DMA */
1134                dma = it->options[2];
1135                if (dma < 1)
1136                        goto no_dma;    /* DMA disabled */
1137
1138                if (((1 << dma) & this_board->DMAbits) == 0) {
1139                        printk(", DMA is out of allowed range, FAIL!\n");
1140                        return -EINVAL; /* Bad DMA */
1141                }
1142                ret = request_dma(dma, "pcl816");
1143                if (ret) {
1144                        printk(", unable to allocate DMA %u, FAIL!\n", dma);
1145                        return -EBUSY;  /* DMA isn't free */
1146                }
1147
1148                devpriv->dma = dma;
1149                printk(", dma=%u", dma);
1150                pages = 2;      /* we need 16KB */
1151                devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1152
1153                if (!devpriv->dmabuf[0]) {
1154                        printk(", unable to allocate DMA buffer, FAIL!\n");
1155                        /* maybe experiment with try_to_free_pages() will help .... */
1156                        return -EBUSY;  /* no buffer :-( */
1157                }
1158                devpriv->dmapages[0] = pages;
1159                devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1160                devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
1161                /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
1162
1163                if (devpriv->dma_rtc == 0) {    /*  we must do duble buff :-( */
1164                        devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1165                        if (!devpriv->dmabuf[1]) {
1166                                printk
1167                                    (", unable to allocate DMA buffer, FAIL!\n");
1168                                return -EBUSY;
1169                        }
1170                        devpriv->dmapages[1] = pages;
1171                        devpriv->hwdmaptr[1] =
1172                            virt_to_bus((void *)devpriv->dmabuf[1]);
1173                        devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
1174                }
1175        }
1176
1177no_dma:
1178
1179/*  if (this_board->n_aochan > 0)
1180    subdevs[1] = COMEDI_SUBD_AO;
1181  if (this_board->n_dichan > 0)
1182    subdevs[2] = COMEDI_SUBD_DI;
1183  if (this_board->n_dochan > 0)
1184    subdevs[3] = COMEDI_SUBD_DO;
1185*/
1186
1187        ret = alloc_subdevices(dev, 1);
1188        if (ret < 0)
1189                return ret;
1190
1191        s = dev->subdevices + 0;
1192        if (this_board->n_aichan > 0) {
1193                s->type = COMEDI_SUBD_AI;
1194                devpriv->sub_ai = s;
1195                dev->read_subdev = s;
1196                s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
1197                s->n_chan = this_board->n_aichan;
1198                s->subdev_flags |= SDF_DIFF;
1199                /* printk (", %dchans DIFF DAC - %d", s->n_chan, i); */
1200                s->maxdata = this_board->ai_maxdata;
1201                s->len_chanlist = this_board->ai_chanlist;
1202                s->range_table = this_board->ai_range_type;
1203                s->cancel = pcl816_ai_cancel;
1204                s->do_cmdtest = pcl816_ai_cmdtest;
1205                s->do_cmd = pcl816_ai_cmd;
1206                s->poll = pcl816_ai_poll;
1207                s->insn_read = pcl816_ai_insn_read;
1208        } else {
1209                s->type = COMEDI_SUBD_UNUSED;
1210        }
1211
1212#if 0
1213case COMEDI_SUBD_AO:
1214        s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1215        s->n_chan = this_board->n_aochan;
1216        s->maxdata = this_board->ao_maxdata;
1217        s->len_chanlist = this_board->ao_chanlist;
1218        s->range_table = this_board->ao_range_type;
1219        break;
1220
1221case COMEDI_SUBD_DI:
1222        s->subdev_flags = SDF_READABLE;
1223        s->n_chan = this_board->n_dichan;
1224        s->maxdata = 1;
1225        s->len_chanlist = this_board->n_dichan;
1226        s->range_table = &range_digital;
1227        break;
1228
1229case COMEDI_SUBD_DO:
1230        s->subdev_flags = SDF_WRITABLE;
1231        s->n_chan = this_board->n_dochan;
1232        s->maxdata = 1;
1233        s->len_chanlist = this_board->n_dochan;
1234        s->range_table = &range_digital;
1235        break;
1236#endif
1237
1238        pcl816_reset(dev);
1239
1240        printk("\n");
1241
1242        return 0;
1243}
1244
1245/*
1246==============================================================================
1247  Removes device
1248 */
1249static int pcl816_detach(struct comedi_device *dev)
1250{
1251        DEBUG(printk("comedi%d: pcl816: remove\n", dev->minor);)
1252            free_resources(dev);
1253#ifdef unused
1254        if (devpriv->dma_rtc)
1255                RTC_lock--;
1256#endif
1257        return 0;
1258}
1259