3 * Comedi driver for Access I/O Products 104-IIRO-16 board
4 * Copyright (C) 2006 C&C Technologies, Inc.
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.
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.
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
24 * Configuration Options:
25 * [0] - I/O port base address
26 * [1] - IRQ (optional)
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:
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
37 * Bit 0 - Digital input 0
40 #include <linux/module.h>
41 #include <linux/interrupt.h>
43 #include "../comedidev.h"
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)
55 static unsigned int aio_iiro_16_read_inputs(struct comedi_device *dev)
59 val = inb(dev->iobase + AIO_IIRO_16_INPUT_0_7);
60 val |= inb(dev->iobase + AIO_IIRO_16_INPUT_8_15) << 8;
65 static irqreturn_t aio_iiro_16_cos(int irq, void *d)
67 struct comedi_device *dev = d;
68 struct comedi_subdevice *s = dev->read_subdev;
72 status = inb(dev->iobase + AIO_IIRO_16_STATUS);
73 if (!(status & AIO_IIRO_16_STATUS_IRQE))
76 val = aio_iiro_16_read_inputs(dev);
77 val |= (status << 16);
79 comedi_buf_write_samples(s, &val, 1);
80 comedi_handle_events(dev, s);
85 static void aio_iiro_enable_irq(struct comedi_device *dev, bool enable)
88 inb(dev->iobase + AIO_IIRO_16_IRQ);
90 outb(0, dev->iobase + AIO_IIRO_16_IRQ);
93 static int aio_iiro_16_cos_cancel(struct comedi_device *dev,
94 struct comedi_subdevice *s)
96 aio_iiro_enable_irq(dev, false);
101 static int aio_iiro_16_cos_cmd(struct comedi_device *dev,
102 struct comedi_subdevice *s)
104 aio_iiro_enable_irq(dev, true);
109 static int aio_iiro_16_cos_cmdtest(struct comedi_device *dev,
110 struct comedi_subdevice *s,
111 struct comedi_cmd *cmd)
115 /* Step 1 : check if triggers are trivially valid */
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);
126 /* Step 2a : make sure trigger sources are unique */
127 /* Step 2b : and mutually compatible */
129 /* Step 3: check if arguments are trivially valid */
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,
136 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
141 /* Step 4: fix up any arguments */
143 /* Step 5: check channel list if it exists */
148 static int aio_iiro_16_do_insn_bits(struct comedi_device *dev,
149 struct comedi_subdevice *s,
150 struct comedi_insn *insn,
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);
164 static int aio_iiro_16_di_insn_bits(struct comedi_device *dev,
165 struct comedi_subdevice *s,
166 struct comedi_insn *insn,
169 data[1] = aio_iiro_16_read_inputs(dev);
174 static int aio_iiro_16_attach(struct comedi_device *dev,
175 struct comedi_devconfig *it)
177 struct comedi_subdevice *s;
180 ret = comedi_request_region(dev, it->options[0], 0x8);
184 aio_iiro_enable_irq(dev, false);
187 * Digital input change of state interrupts are optionally supported
188 * using IRQ 2-7, 10-12, 14, or 15.
190 if ((1 << it->options[1]) & 0xdcfc) {
191 ret = request_irq(it->options[1], aio_iiro_16_cos, 0,
192 dev->board_name, dev);
194 dev->irq = it->options[1];
197 ret = comedi_alloc_subdevices(dev, 2);
201 /* Digital Output subdevice */
202 s = &dev->subdevices[0];
203 s->type = COMEDI_SUBD_DO;
204 s->subdev_flags = SDF_WRITABLE;
207 s->range_table = &range_digital;
208 s->insn_bits = aio_iiro_16_do_insn_bits;
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);
214 /* Digital Input subdevice */
215 s = &dev->subdevices[1];
216 s->type = COMEDI_SUBD_DI;
217 s->subdev_flags = SDF_READABLE;
220 s->range_table = &range_digital;
221 s->insn_bits = aio_iiro_16_di_insn_bits;
223 dev->read_subdev = s;
224 s->subdev_flags |= SDF_CMD_READ | SDF_LSAMPL;
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;
234 static 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,
240 module_comedi_driver(aio_iiro_16_driver);
242 MODULE_AUTHOR("Comedi http://www.comedi.org");
243 MODULE_DESCRIPTION("Comedi driver for Access I/O Products 104-IIRO-16 board");
244 MODULE_LICENSE("GPL");