linux/drivers/staging/comedi/drivers/pcmmio.c
<<
>>
Prefs
   1/*
   2    comedi/drivers/pcmmio.c
   3    Driver for Winsystems PC-104 based multifunction IO board.
   4
   5    COMEDI - Linux Control and Measurement Device Interface
   6    Copyright (C) 2007 Calin A. Culianu <calin@ajvar.org>
   7
   8    This program is free software; you can redistribute it and/or modify
   9    it under the terms of the GNU General Public License as published by
  10    the Free Software Foundation; either version 2 of the License, or
  11    (at your option) any later version.
  12
  13    This program is distributed in the hope that it will be useful,
  14    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16    GNU General Public License for more details.
  17
  18    You should have received a copy of the GNU General Public License
  19    along with this program; if not, write to the Free Software
  20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21*/
  22/*
  23Driver: pcmmio
  24Description: A driver for the PCM-MIO multifunction board
  25Devices: [Winsystems] PCM-MIO (pcmmio)
  26Author: Calin Culianu <calin@ajvar.org>
  27Updated: Wed, May 16 2007 16:21:10 -0500
  28Status: works
  29
  30A driver for the relatively new PCM-MIO multifunction board from
  31Winsystems.  This board is a PC-104 based I/O board.  It contains
  32four subdevices:
  33  subdevice 0 - 16 channels of 16-bit AI
  34  subdevice 1 - 8 channels of 16-bit AO
  35  subdevice 2 - first 24 channels of the 48 channel of DIO (with edge-triggered interrupt support)
  36  subdevice 3 - last 24 channels of the 48 channel DIO (no interrupt support for this bank of channels)
  37
  38  Some notes:
  39
  40  Synchronous reads and writes are the only things implemented for AI and AO,
  41  even though the hardware itself can do streaming acquisition, etc.  Anyone
  42  want to add asynchronous I/O for AI/AO as a feature?  Be my guest...
  43
  44  Asynchronous I/O for the DIO subdevices *is* implemented, however!  They are
  45  basically edge-triggered interrupts for any configuration of the first
  46  24 DIO-lines.
  47
  48  Also note that this interrupt support is untested.
  49
  50  A few words about edge-detection IRQ support (commands on DIO):
  51
  52  * To use edge-detection IRQ support for the DIO subdevice, pass the IRQ
  53    of the board to the comedi_config command.  The board IRQ is not jumpered
  54    but rather configured through software, so any IRQ from 1-15 is OK.
  55
  56  * Due to the genericity of the comedi API, you need to create a special
  57    comedi_command in order to use edge-triggered interrupts for DIO.
  58
  59  * Use comedi_commands with TRIG_NOW.  Your callback will be called each
  60    time an edge is detected on the specified DIO line(s), and the data
  61    values will be two sample_t's, which should be concatenated to form
  62    one 32-bit unsigned int.  This value is the mask of channels that had
  63    edges detected from your channel list.  Note that the bits positions
  64    in the mask correspond to positions in your chanlist when you
  65    specified the command and *not* channel id's!
  66
  67 *  To set the polarity of the edge-detection interrupts pass a nonzero value
  68    for either CR_RANGE or CR_AREF for edge-up polarity, or a zero
  69    value for both CR_RANGE and CR_AREF if you want edge-down polarity.
  70
  71Configuration Options:
  72  [0] - I/O port base address
  73  [1] - IRQ (optional -- for edge-detect interrupt support only, leave out if you don't need this feature)
  74*/
  75
  76#include <linux/interrupt.h>
  77#include "../comedidev.h"
  78#include "pcm_common.h"
  79#include <linux/pci.h>          /* for PCI devices */
  80
  81/* This stuff is all from pcmuio.c -- it refers to the DIO subdevices only */
  82#define CHANS_PER_PORT   8
  83#define PORTS_PER_ASIC   6
  84#define INTR_PORTS_PER_ASIC   3
  85#define MAX_CHANS_PER_SUBDEV 24 /* number of channels per comedi subdevice */
  86#define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT)
  87#define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC)
  88#define INTR_CHANS_PER_ASIC 24
  89#define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT)
  90#define MAX_DIO_CHANS   (PORTS_PER_ASIC*1*CHANS_PER_PORT)
  91#define MAX_ASICS       (MAX_DIO_CHANS/CHANS_PER_ASIC)
  92#define SDEV_NO ((int)(s - dev->subdevices))
  93#define CALC_N_DIO_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/)
  94/* IO Memory sizes */
  95#define ASIC_IOSIZE (0x0B)
  96#define PCMMIO48_IOSIZE ASIC_IOSIZE
  97
  98/* Some offsets - these are all in the 16byte IO memory offset from
  99   the base address.  Note that there is a paging scheme to swap out
 100   offsets 0x8-0xA using the PAGELOCK register.  See the table below.
 101
 102  Register(s)       Pages        R/W?        Description
 103  --------------------------------------------------------------
 104  REG_PORTx         All          R/W         Read/Write/Configure IO
 105  REG_INT_PENDING   All          ReadOnly    Quickly see which INT_IDx has int.
 106  REG_PAGELOCK      All          WriteOnly   Select a page
 107  REG_POLx          Pg. 1 only   WriteOnly   Select edge-detection polarity
 108  REG_ENABx         Pg. 2 only   WriteOnly   Enable/Disable edge-detect. int.
 109  REG_INT_IDx       Pg. 3 only   R/W         See which ports/bits have ints.
 110 */
 111#define REG_PORT0 0x0
 112#define REG_PORT1 0x1
 113#define REG_PORT2 0x2
 114#define REG_PORT3 0x3
 115#define REG_PORT4 0x4
 116#define REG_PORT5 0x5
 117#define REG_INT_PENDING 0x6
 118#define REG_PAGELOCK 0x7        /* page selector register, upper 2 bits select a page
 119                                   and bits 0-5 are used to 'lock down' a particular
 120                                   port above to make it readonly.  */
 121#define REG_POL0 0x8
 122#define REG_POL1 0x9
 123#define REG_POL2 0xA
 124#define REG_ENAB0 0x8
 125#define REG_ENAB1 0x9
 126#define REG_ENAB2 0xA
 127#define REG_INT_ID0 0x8
 128#define REG_INT_ID1 0x9
 129#define REG_INT_ID2 0xA
 130
 131#define NUM_PAGED_REGS 3
 132#define NUM_PAGES 4
 133#define FIRST_PAGED_REG 0x8
 134#define REG_PAGE_BITOFFSET 6
 135#define REG_LOCK_BITOFFSET 0
 136#define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
 137#define REG_LOCK_MASK ~(REG_PAGE_MASK)
 138#define PAGE_POL 1
 139#define PAGE_ENAB 2
 140#define PAGE_INT_ID 3
 141
 142typedef int (*comedi_insn_fn_t) (struct comedi_device *,
 143                                 struct comedi_subdevice *,
 144                                 struct comedi_insn *, unsigned int *);
 145
 146static int ai_rinsn(struct comedi_device *, struct comedi_subdevice *,
 147                    struct comedi_insn *, unsigned int *);
 148static int ao_rinsn(struct comedi_device *, struct comedi_subdevice *,
 149                    struct comedi_insn *, unsigned int *);
 150static int ao_winsn(struct comedi_device *, struct comedi_subdevice *,
 151                    struct comedi_insn *, unsigned int *);
 152
 153/*
 154 * Board descriptions for two imaginary boards.  Describing the
 155 * boards in this way is optional, and completely driver-dependent.
 156 * Some drivers use arrays such as this, other do not.
 157 */
 158struct pcmmio_board {
 159        const char *name;
 160        const int dio_num_asics;
 161        const int dio_num_ports;
 162        const int total_iosize;
 163        const int ai_bits;
 164        const int ao_bits;
 165        const int n_ai_chans;
 166        const int n_ao_chans;
 167        const struct comedi_lrange *ai_range_table, *ao_range_table;
 168        comedi_insn_fn_t ai_rinsn, ao_rinsn, ao_winsn;
 169};
 170
 171static const struct comedi_lrange ranges_ai =
 172    { 4, {RANGE(-5., 5.), RANGE(-10., 10.), RANGE(0., 5.), RANGE(0.,
 173                                                                 10.)}
 174};
 175
 176static const struct comedi_lrange ranges_ao =
 177    { 6, {RANGE(0., 5.), RANGE(0., 10.), RANGE(-5., 5.), RANGE(-10., 10.),
 178          RANGE(-2.5, 2.5), RANGE(-2.5, 7.5)}
 179};
 180
 181static const struct pcmmio_board pcmmio_boards[] = {
 182        {
 183         .name = "pcmmio",
 184         .dio_num_asics = 1,
 185         .dio_num_ports = 6,
 186         .total_iosize = 32,
 187         .ai_bits = 16,
 188         .ao_bits = 16,
 189         .n_ai_chans = 16,
 190         .n_ao_chans = 8,
 191         .ai_range_table = &ranges_ai,
 192         .ao_range_table = &ranges_ao,
 193         .ai_rinsn = ai_rinsn,
 194         .ao_rinsn = ao_rinsn,
 195         .ao_winsn = ao_winsn},
 196};
 197
 198/*
 199 * Useful for shorthand access to the particular board structure
 200 */
 201#define thisboard ((const struct pcmmio_board *)dev->board_ptr)
 202
 203/* this structure is for data unique to this subdevice.  */
 204struct pcmmio_subdev_private {
 205
 206        union {
 207                /* for DIO: mapping of halfwords (bytes) in port/chanarray to iobase */
 208                unsigned long iobases[PORTS_PER_SUBDEV];
 209
 210                /* for AI/AO */
 211                unsigned long iobase;
 212        };
 213        union {
 214                struct {
 215
 216                        /* The below is only used for intr subdevices */
 217                        struct {
 218                                int asic;       /* if non-negative, this subdev has an interrupt asic */
 219                                int first_chan; /* if nonnegative, the first channel id for
 220                                                   interrupts. */
 221                                int num_asic_chans;     /* the number of asic channels in this subdev
 222                                                           that have interrutps */
 223                                int asic_chan;  /* if nonnegative, the first channel id with
 224                                                   respect to the asic that has interrupts */
 225                                int enabled_mask;       /* subdev-relative channel mask for channels
 226                                                           we are interested in */
 227                                int active;
 228                                int stop_count;
 229                                int continuous;
 230                                spinlock_t spinlock;
 231                        } intr;
 232                } dio;
 233                struct {
 234                        unsigned int shadow_samples[8]; /* the last unsigned int data written */
 235                } ao;
 236        };
 237};
 238
 239/* this structure is for data unique to this hardware driver.  If
 240   several hardware drivers keep similar information in this structure,
 241   feel free to suggest moving the variable to the struct comedi_device struct.  */
 242struct pcmmio_private {
 243        /* stuff for DIO */
 244        struct {
 245                unsigned char pagelock; /* current page and lock */
 246                unsigned char pol[NUM_PAGED_REGS];      /* shadow of POLx registers */
 247                unsigned char enab[NUM_PAGED_REGS];     /* shadow of ENABx registers */
 248                int num;
 249                unsigned long iobase;
 250                unsigned int irq;
 251                spinlock_t spinlock;
 252        } asics[MAX_ASICS];
 253        struct pcmmio_subdev_private *sprivs;
 254};
 255
 256/*
 257 * most drivers define the following macro to make it easy to
 258 * access the private structure.
 259 */
 260#define devpriv ((struct pcmmio_private *)dev->private)
 261#define subpriv ((struct pcmmio_subdev_private *)s->private)
 262/*
 263 * The struct comedi_driver structure tells the Comedi core module
 264 * which functions to call to configure/deconfigure (attach/detach)
 265 * the board, and also about the kernel module that contains
 266 * the device code.
 267 */
 268static int pcmmio_attach(struct comedi_device *dev,
 269                         struct comedi_devconfig *it);
 270static int pcmmio_detach(struct comedi_device *dev);
 271
 272static struct comedi_driver driver = {
 273        .driver_name = "pcmmio",
 274        .module = THIS_MODULE,
 275        .attach = pcmmio_attach,
 276        .detach = pcmmio_detach,
 277/* It is not necessary to implement the following members if you are
 278 * writing a driver for a ISA PnP or PCI card */
 279        /* Most drivers will support multiple types of boards by
 280         * having an array of board structures.  These were defined
 281         * in pcmmio_boards[] above.  Note that the element 'name'
 282         * was first in the structure -- Comedi uses this fact to
 283         * extract the name of the board without knowing any details
 284         * about the structure except for its length.
 285         * When a device is attached (by comedi_config), the name
 286         * of the device is given to Comedi, and Comedi tries to
 287         * match it by going through the list of board names.  If
 288         * there is a match, the address of the pointer is put
 289         * into dev->board_ptr and driver->attach() is called.
 290         *
 291         * Note that these are not necessary if you can determine
 292         * the type of board in software.  ISA PnP, PCI, and PCMCIA
 293         * devices are such boards.
 294         */
 295        .board_name = &pcmmio_boards[0].name,
 296        .offset = sizeof(struct pcmmio_board),
 297        .num_names = ARRAY_SIZE(pcmmio_boards),
 298};
 299
 300static int pcmmio_dio_insn_bits(struct comedi_device *dev,
 301                                struct comedi_subdevice *s,
 302                                struct comedi_insn *insn, unsigned int *data);
 303static int pcmmio_dio_insn_config(struct comedi_device *dev,
 304                                  struct comedi_subdevice *s,
 305                                  struct comedi_insn *insn, unsigned int *data);
 306
 307static irqreturn_t interrupt_pcmmio(int irq, void *d);
 308static void pcmmio_stop_intr(struct comedi_device *, struct comedi_subdevice *);
 309static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
 310static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
 311static int pcmmio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
 312                          struct comedi_cmd *cmd);
 313
 314/* some helper functions to deal with specifics of this device's registers */
 315static void init_asics(struct comedi_device *dev);      /* sets up/clears ASIC chips to defaults */
 316static void switch_page(struct comedi_device *dev, int asic, int page);
 317#ifdef notused
 318static void lock_port(struct comedi_device *dev, int asic, int port);
 319static void unlock_port(struct comedi_device *dev, int asic, int port);
 320#endif
 321
 322/*
 323 * Attach is called by the Comedi core to configure the driver
 324 * for a particular board.  If you specified a board_name array
 325 * in the driver structure, dev->board_ptr contains that
 326 * address.
 327 */
 328static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 329{
 330        struct comedi_subdevice *s;
 331        int sdev_no, chans_left, n_dio_subdevs, n_subdevs, port, asic,
 332            thisasic_chanct = 0;
 333        unsigned long iobase;
 334        unsigned int irq[MAX_ASICS];
 335
 336        iobase = it->options[0];
 337        irq[0] = it->options[1];
 338
 339        printk("comedi%d: %s: io: %lx ", dev->minor, driver.driver_name,
 340               iobase);
 341
 342        dev->iobase = iobase;
 343
 344        if (!iobase || !request_region(iobase,
 345                                       thisboard->total_iosize,
 346                                       driver.driver_name)) {
 347                printk("I/O port conflict\n");
 348                return -EIO;
 349        }
 350
 351/*
 352 * Initialize dev->board_name.  Note that we can use the "thisboard"
 353 * macro now, since we just initialized it in the last line.
 354 */
 355        dev->board_name = thisboard->name;
 356
 357/*
 358 * Allocate the private structure area.  alloc_private() is a
 359 * convenient macro defined in comedidev.h.
 360 */
 361        if (alloc_private(dev, sizeof(struct pcmmio_private)) < 0) {
 362                printk("cannot allocate private data structure\n");
 363                return -ENOMEM;
 364        }
 365
 366        for (asic = 0; asic < MAX_ASICS; ++asic) {
 367                devpriv->asics[asic].num = asic;
 368                devpriv->asics[asic].iobase =
 369                    dev->iobase + 16 + asic * ASIC_IOSIZE;
 370                devpriv->asics[asic].irq = 0;   /* this gets actually set at the end of
 371                                                   this function when we
 372                                                   request_irqs */
 373                spin_lock_init(&devpriv->asics[asic].spinlock);
 374        }
 375
 376        chans_left = CHANS_PER_ASIC * thisboard->dio_num_asics;
 377        n_dio_subdevs = CALC_N_DIO_SUBDEVS(chans_left);
 378        n_subdevs = n_dio_subdevs + 2;
 379        devpriv->sprivs =
 380            kcalloc(n_subdevs, sizeof(struct pcmmio_subdev_private),
 381                    GFP_KERNEL);
 382        if (!devpriv->sprivs) {
 383                printk("cannot allocate subdevice private data structures\n");
 384                return -ENOMEM;
 385        }
 386        /*
 387         * Allocate the subdevice structures.  alloc_subdevice() is a
 388         * convenient macro defined in comedidev.h.
 389         *
 390         * Allocate 1 AI + 1 AO + 2 DIO subdevs (24 lines per DIO)
 391         */
 392        if (alloc_subdevices(dev, n_subdevs) < 0) {
 393                printk("cannot allocate subdevice data structures\n");
 394                return -ENOMEM;
 395        }
 396
 397        /* First, AI */
 398        sdev_no = 0;
 399        s = dev->subdevices + sdev_no;
 400        s->private = devpriv->sprivs + sdev_no;
 401        s->maxdata = (1 << thisboard->ai_bits) - 1;
 402        s->range_table = thisboard->ai_range_table;
 403        s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
 404        s->type = COMEDI_SUBD_AI;
 405        s->n_chan = thisboard->n_ai_chans;
 406        s->len_chanlist = s->n_chan;
 407        s->insn_read = thisboard->ai_rinsn;
 408        subpriv->iobase = dev->iobase + 0;
 409        /* initialize the resource enable register by clearing it */
 410        outb(0, subpriv->iobase + 3);
 411        outb(0, subpriv->iobase + 4 + 3);
 412
 413        /* Next, AO */
 414        ++sdev_no;
 415        s = dev->subdevices + sdev_no;
 416        s->private = devpriv->sprivs + sdev_no;
 417        s->maxdata = (1 << thisboard->ao_bits) - 1;
 418        s->range_table = thisboard->ao_range_table;
 419        s->subdev_flags = SDF_READABLE;
 420        s->type = COMEDI_SUBD_AO;
 421        s->n_chan = thisboard->n_ao_chans;
 422        s->len_chanlist = s->n_chan;
 423        s->insn_read = thisboard->ao_rinsn;
 424        s->insn_write = thisboard->ao_winsn;
 425        subpriv->iobase = dev->iobase + 8;
 426        /* initialize the resource enable register by clearing it */
 427        outb(0, subpriv->iobase + 3);
 428        outb(0, subpriv->iobase + 4 + 3);
 429
 430        ++sdev_no;
 431        port = 0;
 432        asic = 0;
 433        for (; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
 434                int byte_no;
 435
 436                s = dev->subdevices + sdev_no;
 437                s->private = devpriv->sprivs + sdev_no;
 438                s->maxdata = 1;
 439                s->range_table = &range_digital;
 440                s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 441                s->type = COMEDI_SUBD_DIO;
 442                s->insn_bits = pcmmio_dio_insn_bits;
 443                s->insn_config = pcmmio_dio_insn_config;
 444                s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
 445                subpriv->dio.intr.asic = -1;
 446                subpriv->dio.intr.first_chan = -1;
 447                subpriv->dio.intr.asic_chan = -1;
 448                subpriv->dio.intr.num_asic_chans = -1;
 449                subpriv->dio.intr.active = 0;
 450                s->len_chanlist = 1;
 451
 452                /* save the ioport address for each 'port' of 8 channels in the
 453                   subdevice */
 454                for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
 455                        if (port >= PORTS_PER_ASIC) {
 456                                port = 0;
 457                                ++asic;
 458                                thisasic_chanct = 0;
 459                        }
 460                        subpriv->iobases[byte_no] =
 461                            devpriv->asics[asic].iobase + port;
 462
 463                        if (thisasic_chanct <
 464                            CHANS_PER_PORT * INTR_PORTS_PER_ASIC
 465                            && subpriv->dio.intr.asic < 0) {
 466                                /* this is an interrupt subdevice, so setup the struct */
 467                                subpriv->dio.intr.asic = asic;
 468                                subpriv->dio.intr.active = 0;
 469                                subpriv->dio.intr.stop_count = 0;
 470                                subpriv->dio.intr.first_chan = byte_no * 8;
 471                                subpriv->dio.intr.asic_chan = thisasic_chanct;
 472                                subpriv->dio.intr.num_asic_chans =
 473                                    s->n_chan - subpriv->dio.intr.first_chan;
 474                                s->cancel = pcmmio_cancel;
 475                                s->do_cmd = pcmmio_cmd;
 476                                s->do_cmdtest = pcmmio_cmdtest;
 477                                s->len_chanlist =
 478                                    subpriv->dio.intr.num_asic_chans;
 479                        }
 480                        thisasic_chanct += CHANS_PER_PORT;
 481                }
 482                spin_lock_init(&subpriv->dio.intr.spinlock);
 483
 484                chans_left -= s->n_chan;
 485
 486                if (!chans_left) {
 487                        asic = 0;       /* reset the asic to our first asic, to do intr subdevs */
 488                        port = 0;
 489                }
 490
 491        }
 492
 493        init_asics(dev);        /* clear out all the registers, basically */
 494
 495        for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
 496                if (irq[asic]
 497                    && request_irq(irq[asic], interrupt_pcmmio,
 498                                   IRQF_SHARED, thisboard->name, dev)) {
 499                        int i;
 500                        /* unroll the allocated irqs.. */
 501                        for (i = asic - 1; i >= 0; --i) {
 502                                free_irq(irq[i], dev);
 503                                devpriv->asics[i].irq = irq[i] = 0;
 504                        }
 505                        irq[asic] = 0;
 506                }
 507                devpriv->asics[asic].irq = irq[asic];
 508        }
 509
 510        dev->irq = irq[0];      /* grr.. wish comedi dev struct supported multiple
 511                                   irqs.. */
 512
 513        if (irq[0]) {
 514                printk("irq: %u ", irq[0]);
 515                if (irq[1] && thisboard->dio_num_asics == 2)
 516                        printk("second ASIC irq: %u ", irq[1]);
 517        } else {
 518                printk("(IRQ mode disabled) ");
 519        }
 520
 521        printk("attached\n");
 522
 523        return 1;
 524}
 525
 526/*
 527 * _detach is called to deconfigure a device.  It should deallocate
 528 * resources.
 529 * This function is also called when _attach() fails, so it should be
 530 * careful not to release resources that were not necessarily
 531 * allocated by _attach().  dev->private and dev->subdevices are
 532 * deallocated automatically by the core.
 533 */
 534static int pcmmio_detach(struct comedi_device *dev)
 535{
 536        int i;
 537
 538        printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
 539        if (dev->iobase)
 540                release_region(dev->iobase, thisboard->total_iosize);
 541
 542        for (i = 0; i < MAX_ASICS; ++i) {
 543                if (devpriv && devpriv->asics[i].irq)
 544                        free_irq(devpriv->asics[i].irq, dev);
 545        }
 546
 547        if (devpriv && devpriv->sprivs)
 548                kfree(devpriv->sprivs);
 549
 550        return 0;
 551}
 552
 553/* DIO devices are slightly special.  Although it is possible to
 554 * implement the insn_read/insn_write interface, it is much more
 555 * useful to applications if you implement the insn_bits interface.
 556 * This allows packed reading/writing of the DIO channels.  The
 557 * comedi core can convert between insn_bits and insn_read/write */
 558static int pcmmio_dio_insn_bits(struct comedi_device *dev,
 559                                struct comedi_subdevice *s,
 560                                struct comedi_insn *insn, unsigned int *data)
 561{
 562        int byte_no;
 563        if (insn->n != 2)
 564                return -EINVAL;
 565
 566        /* NOTE:
 567           reading a 0 means this channel was high
 568           writine a 0 sets the channel high
 569           reading a 1 means this channel was low
 570           writing a 1 means set this channel low
 571
 572           Therefore everything is always inverted. */
 573
 574        /* The insn data is a mask in data[0] and the new data
 575         * in data[1], each channel cooresponding to a bit. */
 576
 577#ifdef DAMMIT_ITS_BROKEN
 578        /* DEBUG */
 579        printk("write mask: %08x  data: %08x\n", data[0], data[1]);
 580#endif
 581
 582        s->state = 0;
 583
 584        for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
 585                /* address of 8-bit port */
 586                unsigned long ioaddr = subpriv->iobases[byte_no],
 587                    /* bit offset of port in 32-bit doubleword */
 588                    offset = byte_no * 8;
 589                /* this 8-bit port's data */
 590                unsigned char byte = 0,
 591                    /* The write mask for this port (if any) */
 592                    write_mask_byte = (data[0] >> offset) & 0xff,
 593                    /* The data byte for this port */
 594                    data_byte = (data[1] >> offset) & 0xff;
 595
 596                byte = inb(ioaddr);     /* read all 8-bits for this port */
 597
 598#ifdef DAMMIT_ITS_BROKEN
 599                /* DEBUG */
 600                printk
 601                    ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ",
 602                     byte_no, (unsigned)write_mask_byte, (unsigned)data_byte,
 603                     offset, ioaddr, (unsigned)byte);
 604#endif
 605
 606                if (write_mask_byte) {
 607                        /* this byte has some write_bits -- so set the output lines */
 608                        byte &= ~write_mask_byte;       /* clear bits for write mask */
 609                        byte |= ~data_byte & write_mask_byte;   /* set to inverted data_byte */
 610                        /* Write out the new digital output state */
 611                        outb(byte, ioaddr);
 612                }
 613#ifdef DAMMIT_ITS_BROKEN
 614                /* DEBUG */
 615                printk("data_out_byte %02x\n", (unsigned)byte);
 616#endif
 617                /* save the digital input lines for this byte.. */
 618                s->state |= ((unsigned int)byte) << offset;
 619        }
 620
 621        /* now return the DIO lines to data[1] - note they came inverted! */
 622        data[1] = ~s->state;
 623
 624#ifdef DAMMIT_ITS_BROKEN
 625        /* DEBUG */
 626        printk("s->state %08x data_out %08x\n", s->state, data[1]);
 627#endif
 628
 629        return 2;
 630}
 631
 632/* The input or output configuration of each digital line is
 633 * configured by a special insn_config instruction.  chanspec
 634 * contains the channel to be changed, and data[0] contains the
 635 * value COMEDI_INPUT or COMEDI_OUTPUT. */
 636static int pcmmio_dio_insn_config(struct comedi_device *dev,
 637                                  struct comedi_subdevice *s,
 638                                  struct comedi_insn *insn, unsigned int *data)
 639{
 640        int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
 641            chan % 8;
 642        unsigned long ioaddr;
 643        unsigned char byte;
 644
 645        /* Compute ioaddr for this channel */
 646        ioaddr = subpriv->iobases[byte_no];
 647
 648        /* NOTE:
 649           writing a 0 an IO channel's bit sets the channel to INPUT
 650           and pulls the line high as well
 651
 652           writing a 1 to an IO channel's  bit pulls the line low
 653
 654           All channels are implicitly always in OUTPUT mode -- but when
 655           they are high they can be considered to be in INPUT mode..
 656
 657           Thus, we only force channels low if the config request was INPUT,
 658           otherwise we do nothing to the hardware.    */
 659
 660        switch (data[0]) {
 661        case INSN_CONFIG_DIO_OUTPUT:
 662                /* save to io_bits -- don't actually do anything since
 663                   all input channels are also output channels... */
 664                s->io_bits |= 1 << chan;
 665                break;
 666        case INSN_CONFIG_DIO_INPUT:
 667                /* write a 0 to the actual register representing the channel
 668                   to set it to 'input'.  0 means "float high". */
 669                byte = inb(ioaddr);
 670                byte &= ~(1 << bit_no);
 671                                /**< set input channel to '0' */
 672
 673                /* write out byte -- this is the only time we actually affect the
 674                   hardware as all channels are implicitly output -- but input
 675                   channels are set to float-high */
 676                outb(byte, ioaddr);
 677
 678                /* save to io_bits */
 679                s->io_bits &= ~(1 << chan);
 680                break;
 681
 682        case INSN_CONFIG_DIO_QUERY:
 683                /* retreive from shadow register */
 684                data[1] =
 685                    (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
 686                return insn->n;
 687                break;
 688
 689        default:
 690                return -EINVAL;
 691                break;
 692        }
 693
 694        return insn->n;
 695}
 696
 697static void init_asics(struct comedi_device *dev)
 698{                               /* sets up an
 699                                   ASIC chip to defaults */
 700        int asic;
 701
 702        for (asic = 0; asic < thisboard->dio_num_asics; ++asic) {
 703                int port, page;
 704                unsigned long baseaddr = devpriv->asics[asic].iobase;
 705
 706                switch_page(dev, asic, 0);      /* switch back to page 0 */
 707
 708                /* first, clear all the DIO port bits */
 709                for (port = 0; port < PORTS_PER_ASIC; ++port)
 710                        outb(0, baseaddr + REG_PORT0 + port);
 711
 712                /* Next, clear all the paged registers for each page */
 713                for (page = 1; page < NUM_PAGES; ++page) {
 714                        int reg;
 715                        /* now clear all the paged registers */
 716                        switch_page(dev, asic, page);
 717                        for (reg = FIRST_PAGED_REG;
 718                             reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
 719                                outb(0, baseaddr + reg);
 720                }
 721
 722                /* DEBUG  set rising edge interrupts on port0 of both asics */
 723                /*switch_page(dev, asic, PAGE_POL);
 724                   outb(0xff, baseaddr + REG_POL0);
 725                   switch_page(dev, asic, PAGE_ENAB);
 726                   outb(0xff, baseaddr + REG_ENAB0); */
 727                /* END DEBUG */
 728
 729                switch_page(dev, asic, 0);      /* switch back to default page 0 */
 730
 731        }
 732}
 733
 734static void switch_page(struct comedi_device *dev, int asic, int page)
 735{
 736        if (asic < 0 || asic >= thisboard->dio_num_asics)
 737                return;         /* paranoia */
 738        if (page < 0 || page >= NUM_PAGES)
 739                return;         /* more paranoia */
 740
 741        devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
 742        devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
 743
 744        /* now write out the shadow register */
 745        outb(devpriv->asics[asic].pagelock,
 746             devpriv->asics[asic].iobase + REG_PAGELOCK);
 747}
 748
 749#ifdef notused
 750static void lock_port(struct comedi_device *dev, int asic, int port)
 751{
 752        if (asic < 0 || asic >= thisboard->dio_num_asics)
 753                return;         /* paranoia */
 754        if (port < 0 || port >= PORTS_PER_ASIC)
 755                return;         /* more paranoia */
 756
 757        devpriv->asics[asic].pagelock |= 0x1 << port;
 758        /* now write out the shadow register */
 759        outb(devpriv->asics[asic].pagelock,
 760             devpriv->asics[asic].iobase + REG_PAGELOCK);
 761        return;
 762}
 763
 764static void unlock_port(struct comedi_device *dev, int asic, int port)
 765{
 766        if (asic < 0 || asic >= thisboard->dio_num_asics)
 767                return;         /* paranoia */
 768        if (port < 0 || port >= PORTS_PER_ASIC)
 769                return;         /* more paranoia */
 770        devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
 771        /* now write out the shadow register */
 772        outb(devpriv->asics[asic].pagelock,
 773             devpriv->asics[asic].iobase + REG_PAGELOCK);
 774}
 775#endif /* notused */
 776
 777static irqreturn_t interrupt_pcmmio(int irq, void *d)
 778{
 779        int asic, got1 = 0;
 780        struct comedi_device *dev = (struct comedi_device *)d;
 781
 782        for (asic = 0; asic < MAX_ASICS; ++asic) {
 783                if (irq == devpriv->asics[asic].irq) {
 784                        unsigned long flags;
 785                        unsigned triggered = 0;
 786                        unsigned long iobase = devpriv->asics[asic].iobase;
 787                        /* it is an interrupt for ASIC #asic */
 788                        unsigned char int_pend;
 789
 790                        spin_lock_irqsave(&devpriv->asics[asic].spinlock,
 791                                          flags);
 792
 793                        int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
 794
 795                        if (int_pend) {
 796                                int port;
 797                                for (port = 0; port < INTR_PORTS_PER_ASIC;
 798                                     ++port) {
 799                                        if (int_pend & (0x1 << port)) {
 800                                                unsigned char
 801                                                    io_lines_with_edges = 0;
 802                                                switch_page(dev, asic,
 803                                                            PAGE_INT_ID);
 804                                                io_lines_with_edges =
 805                                                    inb(iobase +
 806                                                        REG_INT_ID0 + port);
 807
 808                                                if (io_lines_with_edges)
 809                                                        /* clear pending interrupt */
 810                                                        outb(0, iobase +
 811                                                             REG_INT_ID0 +
 812                                                             port);
 813
 814                                                triggered |=
 815                                                    io_lines_with_edges <<
 816                                                    port * 8;
 817                                        }
 818                                }
 819
 820                                ++got1;
 821                        }
 822
 823                        spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
 824                                               flags);
 825
 826                        if (triggered) {
 827                                struct comedi_subdevice *s;
 828                                /* TODO here: dispatch io lines to subdevs with commands.. */
 829                                printk
 830                                    ("PCMMIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n",
 831                                     irq, asic, triggered);
 832                                for (s = dev->subdevices + 2;
 833                                     s < dev->subdevices + dev->n_subdevices;
 834                                     ++s) {
 835                                        if (subpriv->dio.intr.asic == asic) {   /* this is an interrupt subdev, and it matches this asic! */
 836                                                unsigned long flags;
 837                                                unsigned oldevents;
 838
 839                                                spin_lock_irqsave(&subpriv->dio.
 840                                                                  intr.spinlock,
 841                                                                  flags);
 842
 843                                                oldevents = s->async->events;
 844
 845                                                if (subpriv->dio.intr.active) {
 846                                                        unsigned mytrig =
 847                                                            ((triggered >>
 848                                                              subpriv->dio.intr.asic_chan)
 849                                                             &
 850                                                             ((0x1 << subpriv->
 851                                                               dio.intr.
 852                                                               num_asic_chans) -
 853                                                              1)) << subpriv->
 854                                                            dio.intr.first_chan;
 855                                                        if (mytrig &
 856                                                            subpriv->dio.
 857                                                            intr.enabled_mask) {
 858                                                                unsigned int val
 859                                                                    = 0;
 860                                                                unsigned int n,
 861                                                                    ch, len;
 862
 863                                                                len =
 864                                                                    s->
 865                                                                    async->cmd.chanlist_len;
 866                                                                for (n = 0;
 867                                                                     n < len;
 868                                                                     n++) {
 869                                                                        ch = CR_CHAN(s->async->cmd.chanlist[n]);
 870                                                                        if (mytrig & (1U << ch)) {
 871                                                                                val |= (1U << n);
 872                                                                        }
 873                                                                }
 874                                                                /* Write the scan to the buffer. */
 875                                                                if (comedi_buf_put(s->async, ((short *)&val)[0])
 876                                                                    &&
 877                                                                    comedi_buf_put
 878                                                                    (s->async,
 879                                                                     ((short *)
 880                                                                      &val)[1]))
 881                                                                {
 882                                                                        s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
 883                                                                } else {
 884                                                                        /* Overflow! Stop acquisition!! */
 885                                                                        /* TODO: STOP_ACQUISITION_CALL_HERE!! */
 886                                                                        pcmmio_stop_intr
 887                                                                            (dev,
 888                                                                             s);
 889                                                                }
 890
 891                                                                /* Check for end of acquisition. */
 892                                                                if (!subpriv->dio.intr.continuous) {
 893                                                                        /* stop_src == TRIG_COUNT */
 894                                                                        if (subpriv->dio.intr.stop_count > 0) {
 895                                                                                subpriv->dio.intr.stop_count--;
 896                                                                                if (subpriv->dio.intr.stop_count == 0) {
 897                                                                                        s->async->events |= COMEDI_CB_EOA;
 898                                                                                        /* TODO: STOP_ACQUISITION_CALL_HERE!! */
 899                                                                                        pcmmio_stop_intr
 900                                                                                            (dev,
 901                                                                                             s);
 902                                                                                }
 903                                                                        }
 904                                                                }
 905                                                        }
 906                                                }
 907
 908                                                spin_unlock_irqrestore
 909                                                    (&subpriv->dio.intr.
 910                                                     spinlock, flags);
 911
 912                                                if (oldevents !=
 913                                                    s->async->events) {
 914                                                        comedi_event(dev, s);
 915                                                }
 916
 917                                        }
 918
 919                                }
 920                        }
 921
 922                }
 923        }
 924        if (!got1)
 925                return IRQ_NONE;        /* interrupt from other source */
 926        return IRQ_HANDLED;
 927}
 928
 929static void pcmmio_stop_intr(struct comedi_device *dev,
 930                             struct comedi_subdevice *s)
 931{
 932        int nports, firstport, asic, port;
 933
 934        asic = subpriv->dio.intr.asic;
 935        if (asic < 0)
 936                return;         /* not an interrupt subdev */
 937
 938        subpriv->dio.intr.enabled_mask = 0;
 939        subpriv->dio.intr.active = 0;
 940        s->async->inttrig = 0;
 941        nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
 942        firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
 943        switch_page(dev, asic, PAGE_ENAB);
 944        for (port = firstport; port < firstport + nports; ++port) {
 945                /* disable all intrs for this subdev.. */
 946                outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
 947        }
 948}
 949
 950static int pcmmio_start_intr(struct comedi_device *dev,
 951                             struct comedi_subdevice *s)
 952{
 953        if (!subpriv->dio.intr.continuous && subpriv->dio.intr.stop_count == 0) {
 954                /* An empty acquisition! */
 955                s->async->events |= COMEDI_CB_EOA;
 956                subpriv->dio.intr.active = 0;
 957                return 1;
 958        } else {
 959                unsigned bits = 0, pol_bits = 0, n;
 960                int nports, firstport, asic, port;
 961                struct comedi_cmd *cmd = &s->async->cmd;
 962
 963                asic = subpriv->dio.intr.asic;
 964                if (asic < 0)
 965                        return 1;       /* not an interrupt
 966                                           subdev */
 967                subpriv->dio.intr.enabled_mask = 0;
 968                subpriv->dio.intr.active = 1;
 969                nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
 970                firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
 971                if (cmd->chanlist) {
 972                        for (n = 0; n < cmd->chanlist_len; n++) {
 973                                bits |= (1U << CR_CHAN(cmd->chanlist[n]));
 974                                pol_bits |= (CR_AREF(cmd->chanlist[n])
 975                                             || CR_RANGE(cmd->
 976                                                         chanlist[n]) ? 1U : 0U)
 977                                    << CR_CHAN(cmd->chanlist[n]);
 978                        }
 979                }
 980                bits &= ((0x1 << subpriv->dio.intr.num_asic_chans) -
 981                         1) << subpriv->dio.intr.first_chan;
 982                subpriv->dio.intr.enabled_mask = bits;
 983
 984                {               /* the below code configures the board to use a specific IRQ from 0-15. */
 985                        unsigned char b;
 986                        /* set resource enable register to enable IRQ operation */
 987                        outb(1 << 4, dev->iobase + 3);
 988                        /* set bits 0-3 of b to the irq number from 0-15 */
 989                        b = dev->irq & ((1 << 4) - 1);
 990                        outb(b, dev->iobase + 2);
 991                        /* done, we told the board what irq to use */
 992                }
 993
 994                switch_page(dev, asic, PAGE_ENAB);
 995                for (port = firstport; port < firstport + nports; ++port) {
 996                        unsigned enab =
 997                            bits >> (subpriv->dio.intr.first_chan + (port -
 998                                                                     firstport)
 999                                     * 8) & 0xff, pol =
1000                            pol_bits >> (subpriv->dio.intr.first_chan +
1001                                         (port - firstport) * 8) & 0xff;
1002                        /* set enab intrs for this subdev.. */
1003                        outb(enab,
1004                             devpriv->asics[asic].iobase + REG_ENAB0 + port);
1005                        switch_page(dev, asic, PAGE_POL);
1006                        outb(pol,
1007                             devpriv->asics[asic].iobase + REG_ENAB0 + port);
1008                }
1009        }
1010        return 0;
1011}
1012
1013static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
1014{
1015        unsigned long flags;
1016
1017        spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
1018        if (subpriv->dio.intr.active)
1019                pcmmio_stop_intr(dev, s);
1020        spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
1021
1022        return 0;
1023}
1024
1025/*
1026 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
1027 */
1028static int
1029pcmmio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
1030                          unsigned int trignum)
1031{
1032        unsigned long flags;
1033        int event = 0;
1034
1035        if (trignum != 0)
1036                return -EINVAL;
1037
1038        spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
1039        s->async->inttrig = 0;
1040        if (subpriv->dio.intr.active) {
1041                event = pcmmio_start_intr(dev, s);
1042        }
1043        spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
1044
1045        if (event) {
1046                comedi_event(dev, s);
1047        }
1048
1049        return 1;
1050}
1051
1052/*
1053 * 'do_cmd' function for an 'INTERRUPT' subdevice.
1054 */
1055static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
1056{
1057        struct comedi_cmd *cmd = &s->async->cmd;
1058        unsigned long flags;
1059        int event = 0;
1060
1061        spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
1062        subpriv->dio.intr.active = 1;
1063
1064        /* Set up end of acquisition. */
1065        switch (cmd->stop_src) {
1066        case TRIG_COUNT:
1067                subpriv->dio.intr.continuous = 0;
1068                subpriv->dio.intr.stop_count = cmd->stop_arg;
1069                break;
1070        default:
1071                /* TRIG_NONE */
1072                subpriv->dio.intr.continuous = 1;
1073                subpriv->dio.intr.stop_count = 0;
1074                break;
1075        }
1076
1077        /* Set up start of acquisition. */
1078        switch (cmd->start_src) {
1079        case TRIG_INT:
1080                s->async->inttrig = pcmmio_inttrig_start_intr;
1081                break;
1082        default:
1083                /* TRIG_NOW */
1084                event = pcmmio_start_intr(dev, s);
1085                break;
1086        }
1087        spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
1088
1089        if (event) {
1090                comedi_event(dev, s);
1091        }
1092
1093        return 0;
1094}
1095
1096static int
1097pcmmio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
1098               struct comedi_cmd *cmd)
1099{
1100        return comedi_pcm_cmdtest(dev, s, cmd);
1101}
1102
1103static int adc_wait_ready(unsigned long iobase)
1104{
1105        unsigned long retry = 100000;
1106        while (retry--)
1107                if (inb(iobase + 3) & 0x80)
1108                        return 0;
1109        return 1;
1110}
1111
1112/* All this is for AI and AO */
1113static int ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
1114                    struct comedi_insn *insn, unsigned int *data)
1115{
1116        int n;
1117        unsigned long iobase = subpriv->iobase;
1118
1119        /*
1120           1. write the CMD byte (to BASE+2)
1121           2. read junk lo byte (BASE+0)
1122           3. read junk hi byte (BASE+1)
1123           4. (mux settled so) write CMD byte again (BASE+2)
1124           5. read valid lo byte(BASE+0)
1125           6. read valid hi byte(BASE+1)
1126
1127           Additionally note that the BASE += 4 if the channel >= 8
1128         */
1129
1130        /* convert n samples */
1131        for (n = 0; n < insn->n; n++) {
1132                unsigned chan = CR_CHAN(insn->chanspec), range =
1133                    CR_RANGE(insn->chanspec), aref = CR_AREF(insn->chanspec);
1134                unsigned char command_byte = 0;
1135                unsigned iooffset = 0;
1136                short sample, adc_adjust = 0;
1137
1138                if (chan > 7)
1139                        chan -= 8, iooffset = 4;        /* use the second dword for channels > 7 */
1140
1141                if (aref != AREF_DIFF) {
1142                        aref = AREF_GROUND;
1143                        command_byte |= 1 << 7; /* set bit 7 to indicate single-ended */
1144                }
1145                if (range < 2)
1146                        adc_adjust = 0x8000;    /* bipolar ranges (-5,5 .. -10,10 need to be adjusted -- that is.. they need to wrap around by adding 0x8000 */
1147
1148                if (chan % 2) {
1149                        command_byte |= 1 << 6; /* odd-numbered channels have bit 6 set */
1150                }
1151
1152                /* select the channel, bits 4-5 == chan/2 */
1153                command_byte |= ((chan / 2) & 0x3) << 4;
1154
1155                /* set the range, bits 2-3 */
1156                command_byte |= (range & 0x3) << 2;
1157
1158                /* need to do this twice to make sure mux settled */
1159                outb(command_byte, iobase + iooffset + 2);      /* chan/range/aref select */
1160
1161                adc_wait_ready(iobase + iooffset);      /* wait for the adc to say it finised the conversion */
1162
1163                outb(command_byte, iobase + iooffset + 2);      /* select the chan/range/aref AGAIN */
1164
1165                adc_wait_ready(iobase + iooffset);
1166
1167                sample = inb(iobase + iooffset + 0);    /* read data lo byte */
1168                sample |= inb(iobase + iooffset + 1) << 8;      /* read data hi byte */
1169                sample += adc_adjust;   /* adjustment .. munge data */
1170                data[n] = sample;
1171        }
1172        /* return the number of samples read/written */
1173        return n;
1174}
1175
1176static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
1177                    struct comedi_insn *insn, unsigned int *data)
1178{
1179        int n;
1180        for (n = 0; n < insn->n; n++) {
1181                unsigned chan = CR_CHAN(insn->chanspec);
1182                if (chan < s->n_chan)
1183                        data[n] = subpriv->ao.shadow_samples[chan];
1184        }
1185        return n;
1186}
1187
1188static int wait_dac_ready(unsigned long iobase)
1189{
1190        unsigned long retry = 100000L;
1191
1192        /* This may seem like an absurd way to handle waiting and violates the
1193           "no busy waiting" policy. The fact is that the hardware is
1194           normally so fast that we usually only need one time through the loop
1195           anyway. The longer timeout is for rare occasions and for detecting
1196           non-existant hardware.  */
1197
1198        while (retry--) {
1199                if (inb(iobase + 3) & 0x80)
1200                        return 0;
1201
1202        }
1203        return 1;
1204}
1205
1206static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
1207                    struct comedi_insn *insn, unsigned int *data)
1208{
1209        int n;
1210        unsigned iobase = subpriv->iobase, iooffset = 0;
1211
1212        for (n = 0; n < insn->n; n++) {
1213                unsigned chan = CR_CHAN(insn->chanspec), range =
1214                    CR_RANGE(insn->chanspec);
1215                if (chan < s->n_chan) {
1216                        unsigned char command_byte = 0, range_byte =
1217                            range & ((1 << 4) - 1);
1218                        if (chan >= 4)
1219                                chan -= 4, iooffset += 4;
1220                        /* set the range.. */
1221                        outb(range_byte, iobase + iooffset + 0);
1222                        outb(0, iobase + iooffset + 1);
1223
1224                        /* tell it to begin */
1225                        command_byte = (chan << 1) | 0x60;
1226                        outb(command_byte, iobase + iooffset + 2);
1227
1228                        wait_dac_ready(iobase + iooffset);
1229
1230                        outb(data[n] & 0xff, iobase + iooffset + 0);    /* low order byte */
1231                        outb((data[n] >> 8) & 0xff, iobase + iooffset + 1);     /* high order byte */
1232                        command_byte = 0x70 | (chan << 1);      /* set bit 4 of command byte to indicate data is loaded and trigger conversion */
1233                        /* trigger converion */
1234                        outb(command_byte, iobase + iooffset + 2);
1235
1236                        wait_dac_ready(iobase + iooffset);
1237
1238                        subpriv->ao.shadow_samples[chan] = data[n];     /* save to shadow register for ao_rinsn */
1239                }
1240        }
1241        return n;
1242}
1243
1244/*
1245 * A convenient macro that defines init_module() and cleanup_module(),
1246 * as necessary.
1247 */
1248COMEDI_INITCLEANUP(driver);
1249