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