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");