2 comedi/drivers/pcmda12.c
3 Driver for Winsystems PC-104 based PCM-D/A-12 8-channel AO board.
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 2006 Calin A. Culianu <calin@ajvar.org>
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.
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.
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.
24 Description: A driver for the Winsystems PCM-D/A-12
25 Devices: [Winsystems] PCM-D/A-12 (pcmda12)
26 Author: Calin Culianu <calin@ajvar.org>
27 Updated: Fri, 13 Jan 2006 12:01:01 -0500
30 A driver for the relatively straightforward-to-program PCM-D/A-12.
31 This board doesn't support commands, and the only way to set its
32 analog output range is to jumper the board. As such,
33 comedi_data_write() ignores the range value specified.
35 The board uses 16 consecutive I/O addresses starting at the I/O port
36 base address. Each address corresponds to the LSB then MSB of a
37 particular channel from 0-7.
39 Note that the board is not ISA-PNP capable and thus
40 needs the I/O port comedi_config parameter.
42 Note that passing a nonzero value as the second config option will
43 enable "simultaneous xfer" mode for this board, in which AO writes
44 will not take effect until a subsequent read of any AO channel. This
45 is so that one can speed up programming by preloading all AO registers
46 with values before simultaneously setting them to take effect with one
49 Configuration Options:
50 [0] - I/O port base address
51 [1] - Do Simultaneous Xfer (see description)
54 #include "../comedidev.h"
56 #include <linux/pci.h> /* for PCI devices */
58 #define MIN(a,b) ( ((a) < (b)) ? (a) : (b) )
59 #define SDEV_NO ((int)(s - dev->subdevices))
62 #define LSB(x) ((unsigned char)((x) & 0xff))
63 #define MSB(x) ((unsigned char)((((unsigned short)(x))>>8) & 0xff))
64 #define LSB_PORT(chan) (dev->iobase + (chan)*2)
65 #define MSB_PORT(chan) (LSB_PORT(chan)+1)
71 typedef struct pcmda12_board_struct {
75 /* note these have no effect and are merely here for reference..
76 these are configured by jumpering the board! */
77 static const struct comedi_lrange pcmda12_ranges = {
80 UNI_RANGE(5), UNI_RANGE(10), BIP_RANGE(5)
84 static const pcmda12_board pcmda12_boards[] = {
91 * Useful for shorthand access to the particular board structure
93 #define thisboard ((const pcmda12_board *)dev->board_ptr)
96 unsigned int ao_readback[CHANS];
97 int simultaneous_xfer_mode;
100 #define devpriv ((pcmda12_private *)(dev->private))
103 * The struct comedi_driver structure tells the Comedi core module
104 * which functions to call to configure/deconfigure (attach/detach)
105 * the board, and also about the kernel module that contains
108 static int pcmda12_attach(struct comedi_device * dev, struct comedi_devconfig * it);
109 static int pcmda12_detach(struct comedi_device * dev);
111 static void zero_chans(struct comedi_device * dev);
113 static struct comedi_driver driver = {
114 driver_name:"pcmda12",
116 attach:pcmda12_attach,
117 detach:pcmda12_detach,
118 /* It is not necessary to implement the following members if you are
119 * writing a driver for a ISA PnP or PCI card */
120 /* Most drivers will support multiple types of boards by
121 * having an array of board structures. These were defined
122 * in pcmda12_boards[] above. Note that the element 'name'
123 * was first in the structure -- Comedi uses this fact to
124 * extract the name of the board without knowing any details
125 * about the structure except for its length.
126 * When a device is attached (by comedi_config), the name
127 * of the device is given to Comedi, and Comedi tries to
128 * match it by going through the list of board names. If
129 * there is a match, the address of the pointer is put
130 * into dev->board_ptr and driver->attach() is called.
132 * Note that these are not necessary if you can determine
133 * the type of board in software. ISA PnP, PCI, and PCMCIA
134 * devices are such boards.
136 board_name:&pcmda12_boards[0].name,
137 offset:sizeof(pcmda12_board),
138 num_names:sizeof(pcmda12_boards) / sizeof(pcmda12_board),
141 static int ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
142 struct comedi_insn * insn, unsigned int * data);
143 static int ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
144 struct comedi_insn * insn, unsigned int * data);
147 * Attach is called by the Comedi core to configure the driver
148 * for a particular board. If you specified a board_name array
149 * in the driver structure, dev->board_ptr contains that
152 static int pcmda12_attach(struct comedi_device * dev, struct comedi_devconfig * it)
154 struct comedi_subdevice *s;
155 unsigned long iobase;
157 iobase = it->options[0];
158 printk("comedi%d: %s: io: %lx %s ", dev->minor, driver.driver_name,
159 iobase, it->options[1] ? "simultaneous xfer mode enabled" : "");
161 if (!request_region(iobase, IOSIZE, driver.driver_name)) {
162 printk("I/O port conflict\n");
165 dev->iobase = iobase;
168 * Initialize dev->board_name. Note that we can use the "thisboard"
169 * macro now, since we just initialized it in the last line.
171 dev->board_name = thisboard->name;
174 * Allocate the private structure area. alloc_private() is a
175 * convenient macro defined in comedidev.h.
177 if (alloc_private(dev, sizeof(pcmda12_private)) < 0) {
178 printk("cannot allocate private data structure\n");
182 devpriv->simultaneous_xfer_mode = it->options[1];
185 * Allocate the subdevice structures. alloc_subdevice() is a
186 * convenient macro defined in comedidev.h.
188 * Allocate 2 subdevs (32 + 16 DIO lines) or 3 32 DIO subdevs for the
189 * 96-channel version of the board.
191 if (alloc_subdevices(dev, 1) < 0) {
192 printk("cannot allocate subdevice data structures\n");
198 s->maxdata = (0x1 << BITS) - 1;
199 s->range_table = &pcmda12_ranges;
200 s->type = COMEDI_SUBD_AO;
201 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
203 s->insn_write = &ao_winsn;
204 s->insn_read = &ao_rinsn;
206 zero_chans(dev); /* clear out all the registers, basically */
208 printk("attached\n");
214 * _detach is called to deconfigure a device. It should deallocate
216 * This function is also called when _attach() fails, so it should be
217 * careful not to release resources that were not necessarily
218 * allocated by _attach(). dev->private and dev->subdevices are
219 * deallocated automatically by the core.
221 static int pcmda12_detach(struct comedi_device * dev)
223 printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
225 release_region(dev->iobase, IOSIZE);
229 static void zero_chans(struct comedi_device * dev)
231 ASIC chip to defaults */
233 for (i = 0; i < CHANS; ++i) {
234 /* /\* do this as one instruction?? *\/ */
235 /* outw(0, LSB_PORT(chan)); */
236 outb(0, LSB_PORT(i));
237 outb(0, MSB_PORT(i));
239 inb(LSB_PORT(0)); /* update chans. */
242 static int ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
243 struct comedi_insn * insn, unsigned int * data)
246 int chan = CR_CHAN(insn->chanspec);
248 /* Writing a list of values to an AO channel is probably not
249 * very useful, but that's how the interface is defined. */
250 for (i = 0; i < insn->n; ++i) {
252 /* /\* do this as one instruction?? *\/ */
253 /* outw(data[i], LSB_PORT(chan)); */
255 /* Need to do this as two instructions due to 8-bit bus?? */
256 /* first, load the low byte */
257 outb(LSB(data[i]), LSB_PORT(chan));
258 /* next, write the high byte */
259 outb(MSB(data[i]), MSB_PORT(chan));
261 /* save shadow register */
262 devpriv->ao_readback[chan] = data[i];
264 if (!devpriv->simultaneous_xfer_mode)
268 /* return the number of samples written */
272 /* AO subdevices should have a read insn as well as a write insn.
274 Usually this means copying a value stored in devpriv->ao_readback.
275 However, since this driver supports simultaneous xfer then sometimes
276 this function actually accomplishes work.
278 Simultaneaous xfer mode is accomplished by loading ALL the values
279 you want for AO in all the channels, then READing off one of the AO
280 registers to initiate the instantaneous simultaneous update of all
281 DAC outputs, which makes all AO channels update simultaneously.
282 This is useful for some control applications, I would imagine.
284 static int ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
285 struct comedi_insn * insn, unsigned int * data)
288 int chan = CR_CHAN(insn->chanspec);
290 for (i = 0; i < insn->n; i++) {
291 if (devpriv->simultaneous_xfer_mode)
293 /* read back shadow register */
294 data[i] = devpriv->ao_readback[chan];
301 * A convenient macro that defines init_module() and cleanup_module(),
304 COMEDI_INITCLEANUP(driver);