2 comedi/drivers/comedi_bond.c
3 A Comedi driver to 'bond' or merge multiple drivers and devices as one.
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
7 Copyright (C) 2005 Calin A. Culianu <calin@ajvar.org>
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 Description: A driver to 'bond' (merge) multiple subdevices from multiple
27 devices together as one.
30 Updated: Mon, 10 Oct 00:18:25 -0500
33 This driver allows you to 'bond' (merge) multiple comedi subdevices
34 (coming from possibly difference boards and/or drivers) together. For
35 example, if you had a board with 2 different DIO subdevices, and
36 another with 1 DIO subdevice, you could 'bond' them with this driver
37 so that they look like one big fat DIO subdevice. This makes writing
38 applications slightly easier as you don't have to worry about managing
39 different subdevices in the application -- you just worry about
40 indexing one linear array of channel id's.
42 Right now only DIO subdevices are supported as that's the personal itch
43 I am scratching with this driver. If you want to add support for AI and AO
44 subdevs, go right on ahead and do so!
46 Commands aren't supported -- although it would be cool if they were.
48 Configuration Options:
49 List of comedi-minors to bond. All subdevices of the same type
50 within each minor will be concatenated together in the order given here.
53 #include <linux/string.h>
54 #include <linux/slab.h>
55 #include "../comedi.h"
56 #include "../comedilib.h"
57 #include "../comedidev.h"
59 /* The maxiumum number of channels per subdevice. */
62 #define MODULE_NAME "comedi_bond"
65 # define STR(x) STR1(x)
69 module_param(debug, int, 0644);
70 MODULE_PARM_DESC(debug, "If true, print extra cryptic debugging output useful"
71 "only to developers.");
73 #define LOG_MSG(x...) printk(KERN_INFO MODULE_NAME": "x)
77 printk(KERN_DEBUG MODULE_NAME": DEBUG: "x); \
79 #define WARNING(x...) printk(KERN_WARNING MODULE_NAME ": WARNING: "x)
80 #define ERROR(x...) printk(KERN_ERR MODULE_NAME ": INTERNAL ERROR: "x)
83 * Board descriptions for two imaginary boards. Describing the
84 * boards in this way is optional, and completely driver-dependent.
85 * Some drivers use arrays such as this, other do not.
92 * Useful for shorthand access to the particular board structure
94 #define thisboard ((const struct BondingBoard *)dev->board_ptr)
97 struct comedi_device *dev;
100 unsigned subdev_type;
102 unsigned chanid_offset; /* The offset into our unified linear
103 channel-id's of chanid 0 on this
107 /* this structure is for data unique to this hardware driver. If
108 several hardware drivers keep similar information in this structure,
109 feel free to suggest moving the variable to the struct comedi_device struct. */
111 # define MAX_BOARD_NAME 256
112 char name[MAX_BOARD_NAME];
113 struct BondedDevice **devs;
115 struct BondedDevice *chanIdDevMap[MAX_CHANS];
120 * most drivers define the following macro to make it easy to
121 * access the private structure.
123 #define devpriv ((struct Private *)dev->private)
125 /* DIO devices are slightly special. Although it is possible to
126 * implement the insn_read/insn_write interface, it is much more
127 * useful to applications if you implement the insn_bits interface.
128 * This allows packed reading/writing of the DIO channels. The
129 * comedi core can convert between insn_bits and insn_read/write */
130 static int bonding_dio_insn_bits(struct comedi_device *dev,
131 struct comedi_subdevice *s,
132 struct comedi_insn *insn, unsigned int *data)
134 #define LSAMPL_BITS (sizeof(unsigned int)*8)
135 unsigned nchans = LSAMPL_BITS, num_done = 0, i;
137 if (devpriv->nchans < nchans)
138 nchans = devpriv->nchans;
140 /* The insn data is a mask in data[0] and the new data
141 * in data[1], each channel cooresponding to a bit. */
142 for (i = 0; num_done < nchans && i < devpriv->ndevs; ++i) {
143 struct BondedDevice *bdev = devpriv->devs[i];
144 /* Grab the channel mask and data of only the bits corresponding
145 to this subdevice.. need to shift them to zero position of
147 /* Bits corresponding to this subdev. */
148 unsigned int subdevMask = ((1 << bdev->nchans) - 1);
149 unsigned int writeMask, dataBits;
151 /* Argh, we have >= LSAMPL_BITS chans.. take all bits */
152 if (bdev->nchans >= LSAMPL_BITS)
153 subdevMask = (unsigned int)(-1);
155 writeMask = (data[0] >> num_done) & subdevMask;
156 dataBits = (data[1] >> num_done) & subdevMask;
158 /* Read/Write the new digital lines */
159 if (comedi_dio_bitfield(bdev->dev, bdev->subdev, writeMask,
163 /* Make room for the new bits in data[1], the return value */
164 data[1] &= ~(subdevMask << num_done);
165 /* Put the bits in the return value */
166 data[1] |= (dataBits & subdevMask) << num_done;
167 /* Save the new bits to the saved state.. */
170 num_done += bdev->nchans;
176 static int bonding_dio_insn_config(struct comedi_device *dev,
177 struct comedi_subdevice *s,
178 struct comedi_insn *insn, unsigned int *data)
180 int chan = CR_CHAN(insn->chanspec), ret, io_bits = s->io_bits;
182 struct BondedDevice *bdev;
184 if (chan < 0 || chan >= devpriv->nchans)
186 bdev = devpriv->chanIdDevMap[chan];
188 /* The input or output configuration of each digital line is
189 * configured by a special insn_config instruction. chanspec
190 * contains the channel to be changed, and data[0] contains the
191 * value COMEDI_INPUT or COMEDI_OUTPUT. */
193 case INSN_CONFIG_DIO_OUTPUT:
194 io = COMEDI_OUTPUT; /* is this really necessary? */
195 io_bits |= 1 << chan;
197 case INSN_CONFIG_DIO_INPUT:
198 io = COMEDI_INPUT; /* is this really necessary? */
199 io_bits &= ~(1 << chan);
201 case INSN_CONFIG_DIO_QUERY:
203 (io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
210 /* 'real' channel id for this subdev.. */
211 chan -= bdev->chanid_offset;
212 ret = comedi_dio_config(bdev->dev, bdev->subdev, chan, io);
215 /* Finally, save the new io_bits values since we didn't get
217 s->io_bits = io_bits;
221 static void *Realloc(const void *oldmem, size_t newlen, size_t oldlen)
223 void *newmem = kmalloc(newlen, GFP_KERNEL);
225 if (newmem && oldmem)
226 memcpy(newmem, oldmem, min(oldlen, newlen));
231 static int doDevConfig(struct comedi_device *dev, struct comedi_devconfig *it)
234 struct comedi_device *devs_opened[COMEDI_NUM_BOARD_MINORS];
236 memset(devs_opened, 0, sizeof(devs_opened));
237 devpriv->name[0] = 0;
238 /* Loop through all comedi devices specified on the command-line,
239 building our device list */
240 for (i = 0; i < COMEDI_NDEVCONFOPTS && (!i || it->options[i]); ++i) {
241 char file[] = "/dev/comediXXXXXX";
242 int minor = it->options[i];
243 struct comedi_device *d;
244 int sdev = -1, nchans, tmp;
245 struct BondedDevice *bdev = NULL;
247 if (minor < 0 || minor >= COMEDI_NUM_BOARD_MINORS) {
248 ERROR("Minor %d is invalid!\n", minor);
251 if (minor == dev->minor) {
252 ERROR("Cannot bond this driver to itself!\n");
255 if (devs_opened[minor]) {
256 ERROR("Minor %d specified more than once!\n", minor);
260 snprintf(file, sizeof(file), "/dev/comedi%u", minor);
261 file[sizeof(file) - 1] = 0;
263 d = devs_opened[minor] = comedi_open(file);
266 ERROR("Minor %u could not be opened\n", minor);
270 /* Do DIO, as that's all we support now.. */
271 while ((sdev = comedi_find_subdevice_by_type(d, COMEDI_SUBD_DIO,
273 nchans = comedi_get_n_channels(d, sdev);
275 ERROR("comedi_get_n_channels() returned %d "
276 "on minor %u subdev %d!\n",
277 nchans, minor, sdev);
280 bdev = kmalloc(sizeof(*bdev), GFP_KERNEL);
282 ERROR("Out of memory.\n");
288 bdev->subdev_type = COMEDI_SUBD_DIO;
289 bdev->nchans = nchans;
290 bdev->chanid_offset = devpriv->nchans;
292 /* map channel id's to BondedDevice * pointer.. */
294 devpriv->chanIdDevMap[devpriv->nchans++] = bdev;
296 /* Now put bdev pointer at end of devpriv->devs array
299 /* ergh.. ugly.. we need to realloc :( */
300 tmp = devpriv->ndevs * sizeof(bdev);
302 Realloc(devpriv->devs,
303 ++devpriv->ndevs * sizeof(bdev), tmp);
304 if (!devpriv->devs) {
305 ERROR("Could not allocate memory. "
310 devpriv->devs[devpriv->ndevs - 1] = bdev;
312 /** Append dev:subdev to devpriv->name */
315 MAX_BOARD_NAME - strlen(devpriv->name) - 1;
316 snprintf(buf, sizeof(buf), "%d:%d ", dev->minor,
318 buf[sizeof(buf) - 1] = 0;
319 strncat(devpriv->name, buf, left);
325 if (!devpriv->nchans) {
326 ERROR("No channels found!\n");
333 static int bonding_attach(struct comedi_device *dev,
334 struct comedi_devconfig *it)
336 struct comedi_subdevice *s;
339 LOG_MSG("comedi%d\n", dev->minor);
342 * Allocate the private structure area. alloc_private() is a
343 * convenient macro defined in comedidev.h.
345 if (alloc_private(dev, sizeof(struct Private)) < 0)
349 * Setup our bonding from config params.. sets up our Private struct..
351 if (!doDevConfig(dev, it))
355 * Initialize dev->board_name. Note that we can use the "thisboard"
356 * macro now, since we just initialized it in the last line.
358 dev->board_name = devpriv->name;
360 ret = comedi_alloc_subdevices(dev, 1);
364 s = dev->subdevices + 0;
365 s->type = COMEDI_SUBD_DIO;
366 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
367 s->n_chan = devpriv->nchans;
369 s->range_table = &range_digital;
370 s->insn_bits = bonding_dio_insn_bits;
371 s->insn_config = bonding_dio_insn_config;
373 LOG_MSG("attached with %u DIO channels coming from %u different "
374 "subdevices all bonded together. "
375 "John Lennon would be proud!\n",
376 devpriv->nchans, devpriv->ndevs);
381 static void bonding_detach(struct comedi_device *dev)
383 unsigned long devs_closed = 0;
386 while (devpriv->ndevs-- && devpriv->devs) {
387 struct BondedDevice *bdev;
389 bdev = devpriv->devs[devpriv->ndevs];
392 if (!(devs_closed & (0x1 << bdev->minor))) {
393 comedi_close(bdev->dev);
394 devs_closed |= (0x1 << bdev->minor);
398 kfree(devpriv->devs);
399 devpriv->devs = NULL;
405 static const struct BondingBoard bondingBoards[] = {
407 .name = "comedi_bond",
411 static struct comedi_driver bonding_driver = {
412 .driver_name = "comedi_bond",
413 .module = THIS_MODULE,
414 .attach = bonding_attach,
415 .detach = bonding_detach,
416 .board_name = &bondingBoards[0].name,
417 .offset = sizeof(struct BondingBoard),
418 .num_names = ARRAY_SIZE(bondingBoards),
420 module_comedi_driver(bonding_driver);
422 MODULE_AUTHOR("Calin A. Culianu");
423 MODULE_DESCRIPTION(MODULE_NAME "A driver for COMEDI to bond multiple COMEDI "
424 "devices together as one. In the words of John Lennon: "
425 "'And the world will live as one...'");
426 MODULE_LICENSE("GPL");