linux/drivers/staging/comedi/drivers/aio_iiro_16.c
<<
>>
Prefs
   1/*
   2 * aio_iiro_16.c
   3 * Comedi driver for Access I/O Products 104-IIRO-16 board
   4 * Copyright (C) 2006 C&C Technologies, Inc.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 */
  16
  17/*
  18 * Driver: aio_iiro_16
  19 * Description: Access I/O Products PC/104 Isolated Input/Relay Output Board
  20 * Author: Zachary Ware <zach.ware@cctechnol.com>
  21 * Devices: [Access I/O] 104-IIRO-16 (aio_iiro_16)
  22 * Status: experimental
  23 *
  24 * Configuration Options:
  25 *   [0] - I/O port base address
  26 *   [1] - IRQ (optional)
  27 *
  28 * The board supports interrupts on change of state of the digital inputs.
  29 * The sample data returned by the async command indicates which inputs
  30 * changed state and the current state of the inputs:
  31 *
  32 *      Bit 23 - IRQ Enable (1) / Disable (0)
  33 *      Bit 17 - Input 8-15 Changed State (1 = Changed, 0 = No Change)
  34 *      Bit 16 - Input 0-7 Changed State (1 = Changed, 0 = No Change)
  35 *      Bit 15 - Digital input 15
  36 *      ...
  37 *      Bit 0  - Digital input 0
  38 */
  39
  40#include <linux/module.h>
  41#include <linux/interrupt.h>
  42
  43#include "../comedidev.h"
  44
  45#define AIO_IIRO_16_RELAY_0_7           0x00
  46#define AIO_IIRO_16_INPUT_0_7           0x01
  47#define AIO_IIRO_16_IRQ                 0x02
  48#define AIO_IIRO_16_RELAY_8_15          0x04
  49#define AIO_IIRO_16_INPUT_8_15          0x05
  50#define AIO_IIRO_16_STATUS              0x07
  51#define AIO_IIRO_16_STATUS_IRQE         BIT(7)
  52#define AIO_IIRO_16_STATUS_INPUT_8_15   BIT(1)
  53#define AIO_IIRO_16_STATUS_INPUT_0_7    BIT(0)
  54
  55static unsigned int aio_iiro_16_read_inputs(struct comedi_device *dev)
  56{
  57        unsigned int val;
  58
  59        val = inb(dev->iobase + AIO_IIRO_16_INPUT_0_7);
  60        val |= inb(dev->iobase + AIO_IIRO_16_INPUT_8_15) << 8;
  61
  62        return val;
  63}
  64
  65static irqreturn_t aio_iiro_16_cos(int irq, void *d)
  66{
  67        struct comedi_device *dev = d;
  68        struct comedi_subdevice *s = dev->read_subdev;
  69        unsigned int status;
  70        unsigned int val;
  71
  72        status = inb(dev->iobase + AIO_IIRO_16_STATUS);
  73        if (!(status & AIO_IIRO_16_STATUS_IRQE))
  74                return IRQ_NONE;
  75
  76        val = aio_iiro_16_read_inputs(dev);
  77        val |= (status << 16);
  78
  79        comedi_buf_write_samples(s, &val, 1);
  80        comedi_handle_events(dev, s);
  81
  82        return IRQ_HANDLED;
  83}
  84
  85static void aio_iiro_enable_irq(struct comedi_device *dev, bool enable)
  86{
  87        if (enable)
  88                inb(dev->iobase + AIO_IIRO_16_IRQ);
  89        else
  90                outb(0, dev->iobase + AIO_IIRO_16_IRQ);
  91}
  92
  93static int aio_iiro_16_cos_cancel(struct comedi_device *dev,
  94                                  struct comedi_subdevice *s)
  95{
  96        aio_iiro_enable_irq(dev, false);
  97
  98        return 0;
  99}
 100
 101static int aio_iiro_16_cos_cmd(struct comedi_device *dev,
 102                               struct comedi_subdevice *s)
 103{
 104        aio_iiro_enable_irq(dev, true);
 105
 106        return 0;
 107}
 108
 109static int aio_iiro_16_cos_cmdtest(struct comedi_device *dev,
 110                                   struct comedi_subdevice *s,
 111                                   struct comedi_cmd *cmd)
 112{
 113        int err = 0;
 114
 115        /* Step 1 : check if triggers are trivially valid */
 116
 117        err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
 118        err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
 119        err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
 120        err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 121        err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
 122
 123        if (err)
 124                return 1;
 125
 126        /* Step 2a : make sure trigger sources are unique */
 127        /* Step 2b : and mutually compatible */
 128
 129        /* Step 3: check if arguments are trivially valid */
 130
 131        err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 132        err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 133        err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
 134        err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 135                                           cmd->chanlist_len);
 136        err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 137
 138        if (err)
 139                return 3;
 140
 141        /* Step 4: fix up any arguments */
 142
 143        /* Step 5: check channel list if it exists */
 144
 145        return 0;
 146}
 147
 148static int aio_iiro_16_do_insn_bits(struct comedi_device *dev,
 149                                    struct comedi_subdevice *s,
 150                                    struct comedi_insn *insn,
 151                                    unsigned int *data)
 152{
 153        if (comedi_dio_update_state(s, data)) {
 154                outb(s->state & 0xff, dev->iobase + AIO_IIRO_16_RELAY_0_7);
 155                outb((s->state >> 8) & 0xff,
 156                     dev->iobase + AIO_IIRO_16_RELAY_8_15);
 157        }
 158
 159        data[1] = s->state;
 160
 161        return insn->n;
 162}
 163
 164static int aio_iiro_16_di_insn_bits(struct comedi_device *dev,
 165                                    struct comedi_subdevice *s,
 166                                    struct comedi_insn *insn,
 167                                    unsigned int *data)
 168{
 169        data[1] = aio_iiro_16_read_inputs(dev);
 170
 171        return insn->n;
 172}
 173
 174static int aio_iiro_16_attach(struct comedi_device *dev,
 175                              struct comedi_devconfig *it)
 176{
 177        struct comedi_subdevice *s;
 178        int ret;
 179
 180        ret = comedi_request_region(dev, it->options[0], 0x8);
 181        if (ret)
 182                return ret;
 183
 184        aio_iiro_enable_irq(dev, false);
 185
 186        /*
 187         * Digital input change of state interrupts are optionally supported
 188         * using IRQ 2-7, 10-12, 14, or 15.
 189         */
 190        if ((1 << it->options[1]) & 0xdcfc) {
 191                ret = request_irq(it->options[1], aio_iiro_16_cos, 0,
 192                                  dev->board_name, dev);
 193                if (ret == 0)
 194                        dev->irq = it->options[1];
 195        }
 196
 197        ret = comedi_alloc_subdevices(dev, 2);
 198        if (ret)
 199                return ret;
 200
 201        /* Digital Output subdevice */
 202        s = &dev->subdevices[0];
 203        s->type         = COMEDI_SUBD_DO;
 204        s->subdev_flags = SDF_WRITABLE;
 205        s->n_chan       = 16;
 206        s->maxdata      = 1;
 207        s->range_table  = &range_digital;
 208        s->insn_bits    = aio_iiro_16_do_insn_bits;
 209
 210        /* get the initial state of the relays */
 211        s->state = inb(dev->iobase + AIO_IIRO_16_RELAY_0_7) |
 212                   (inb(dev->iobase + AIO_IIRO_16_RELAY_8_15) << 8);
 213
 214        /* Digital Input subdevice */
 215        s = &dev->subdevices[1];
 216        s->type         = COMEDI_SUBD_DI;
 217        s->subdev_flags = SDF_READABLE;
 218        s->n_chan       = 16;
 219        s->maxdata      = 1;
 220        s->range_table  = &range_digital;
 221        s->insn_bits    = aio_iiro_16_di_insn_bits;
 222        if (dev->irq) {
 223                dev->read_subdev = s;
 224                s->subdev_flags |= SDF_CMD_READ | SDF_LSAMPL;
 225                s->len_chanlist = 1;
 226                s->do_cmdtest   = aio_iiro_16_cos_cmdtest;
 227                s->do_cmd       = aio_iiro_16_cos_cmd;
 228                s->cancel       = aio_iiro_16_cos_cancel;
 229        }
 230
 231        return 0;
 232}
 233
 234static struct comedi_driver aio_iiro_16_driver = {
 235        .driver_name    = "aio_iiro_16",
 236        .module         = THIS_MODULE,
 237        .attach         = aio_iiro_16_attach,
 238        .detach         = comedi_legacy_detach,
 239};
 240module_comedi_driver(aio_iiro_16_driver);
 241
 242MODULE_AUTHOR("Comedi http://www.comedi.org");
 243MODULE_DESCRIPTION("Comedi driver for Access I/O Products 104-IIRO-16 board");
 244MODULE_LICENSE("GPL");
 245