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.
54 * The previous block comment is used to automatically generate
55 * documentation in Comedi and Comedilib. The fields:
57 * Driver: the name of the driver
58 * Description: a short phrase describing the driver. Don't list boards.
59 * Devices: a full list of the boards that attempt to be supported by
60 * the driver. Format is "(manufacturer) board name [comedi name]",
61 * where comedi_name is the name that is used to configure the board.
62 * See the comment near board_name: in the comedi_driver structure
63 * below. If (manufacturer) or [comedi name] is missing, the previous
66 * Updated: date when the _documentation_ was last updated. Use 'date -R'
67 * to get a value for this.
68 * Status: a one-word description of the status. Valid values are:
69 * works - driver works correctly on most boards supported, and
71 * unknown - unknown. Usually put there by ds.
72 * experimental - may not work in any particular release. Author
73 * probably wants assistance testing it.
74 * bitrotten - driver has not been update in a long time, probably
75 * doesn't work, and probably is missing support for significant
76 * Comedi interface features.
77 * untested - author probably wrote it "blind", and is believed to
78 * work, but no confirmation.
80 * These headers should be followed by a blank line, and any comments
81 * you wish to say about the driver. The comment area is the place
82 * to put any known bugs, limitations, unsupported features, supported
83 * command triggers, whether or not commands are supported on particular
86 * Somewhere in the comment should be information about configuration
87 * options that are used with comedi_config.
90 #include "../comedilib.h"
91 #include "../comedidev.h"
92 #include <linux/string.h>
94 /* The maxiumum number of channels per subdevice. */
97 #define MODULE_NAME "comedi_bond"
99 MODULE_LICENSE("GPL");
103 # define STR(x) STR1(x)
107 module_param(debug, int, 0644);
108 MODULE_PARM_DESC(debug, "If true, print extra cryptic debugging output useful"
109 "only to developers.");
111 #define LOG_MSG(x...) printk(KERN_INFO MODULE_NAME": "x)
112 #define DEBUG(x...) \
115 printk(KERN_DEBUG MODULE_NAME": DEBUG: "x); \
117 #define WARNING(x...) printk(KERN_WARNING MODULE_NAME ": WARNING: "x)
118 #define ERROR(x...) printk(KERN_ERR MODULE_NAME ": INTERNAL ERROR: "x)
119 MODULE_AUTHOR("Calin A. Culianu");
120 MODULE_DESCRIPTION(MODULE_NAME "A driver for COMEDI to bond multiple COMEDI "
121 "devices together as one. In the words of John Lennon: "
122 "'And the world will live as one...'");
125 * Board descriptions for two imaginary boards. Describing the
126 * boards in this way is optional, and completely driver-dependent.
127 * Some drivers use arrays such as this, other do not.
129 struct BondingBoard {
132 typedef struct BondingBoard BondingBoard;
134 static const BondingBoard bondingBoards[] = {
141 * Useful for shorthand access to the particular board structure
143 #define thisboard ((const BondingBoard *)dev->board_ptr)
145 struct BondedDevice {
149 unsigned subdev_type;
151 unsigned chanid_offset; /* The offset into our unified linear
152 channel-id's of chanid 0 on this
155 typedef struct BondedDevice BondedDevice;
157 /* this structure is for data unique to this hardware driver. If
158 several hardware drivers keep similar information in this structure,
159 feel free to suggest moving the variable to the comedi_device struct. */
161 # define MAX_BOARD_NAME 256
162 char name[MAX_BOARD_NAME];
163 struct BondedDevice **devs;
165 struct BondedDevice *chanIdDevMap[MAX_CHANS];
168 typedef struct Private Private;
171 * most drivers define the following macro to make it easy to
172 * access the private structure.
174 #define devpriv ((Private *)dev->private)
177 * The comedi_driver structure tells the Comedi core module
178 * which functions to call to configure/deconfigure (attach/detach)
179 * the board, and also about the kernel module that contains
182 static int bonding_attach(comedi_device *dev, comedi_devconfig *it);
183 static int bonding_detach(comedi_device *dev);
184 /** Build Private array of all devices.. */
185 static int doDevConfig(comedi_device *dev, comedi_devconfig *it);
186 static void doDevUnconfig(comedi_device *dev);
187 /* Ugly implementation of realloc that always copies memory around -- I'm lazy,
188 * what can I say? I like to do wasteful memcopies.. :) */
189 static void *Realloc(const void *ptr, size_t len, size_t old_len);
191 static comedi_driver driver_bonding = {
192 .driver_name = MODULE_NAME,
193 .module = THIS_MODULE,
194 .attach = bonding_attach,
195 .detach = bonding_detach,
196 /* It is not necessary to implement the following members if you are
197 * writing a driver for a ISA PnP or PCI card */
198 /* Most drivers will support multiple types of boards by
199 * having an array of board structures. These were defined
200 * in skel_boards[] above. Note that the element 'name'
201 * was first in the structure -- Comedi uses this fact to
202 * extract the name of the board without knowing any details
203 * about the structure except for its length.
204 * When a device is attached (by comedi_config), the name
205 * of the device is given to Comedi, and Comedi tries to
206 * match it by going through the list of board names. If
207 * there is a match, the address of the pointer is put
208 * into dev->board_ptr and driver->attach() is called.
210 * Note that these are not necessary if you can determine
211 * the type of board in software. ISA PnP, PCI, and PCMCIA
212 * devices are such boards.
214 .board_name = &bondingBoards[0].name,
215 .offset = sizeof(BondingBoard),
216 .num_names = sizeof(bondingBoards) / sizeof(BondingBoard),
219 static int bonding_dio_insn_bits(comedi_device *dev, comedi_subdevice *s,
220 comedi_insn *insn, lsampl_t *data);
221 static int bonding_dio_insn_config(comedi_device *dev, comedi_subdevice *s,
222 comedi_insn *insn, lsampl_t *data);
225 * Attach is called by the Comedi core to configure the driver
226 * for a particular board. If you specified a board_name array
227 * in the driver structure, dev->board_ptr contains that
230 static int bonding_attach(comedi_device *dev, comedi_devconfig *it)
234 LOG_MSG("comedi%d\n", dev->minor);
237 * Allocate the private structure area. alloc_private() is a
238 * convenient macro defined in comedidev.h.
240 if (alloc_private(dev, sizeof(Private)) < 0)
244 * Setup our bonding from config params.. sets up our Private struct..
246 if (!doDevConfig(dev, it))
250 * Initialize dev->board_name. Note that we can use the "thisboard"
251 * macro now, since we just initialized it in the last line.
253 dev->board_name = devpriv->name;
256 * Allocate the subdevice structures. alloc_subdevice() is a
257 * convenient macro defined in comedidev.h.
259 if (alloc_subdevices(dev, 1) < 0)
262 s = dev->subdevices + 0;
263 s->type = COMEDI_SUBD_DIO;
264 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
265 s->n_chan = devpriv->nchans;
267 s->range_table = &range_digital;
268 s->insn_bits = bonding_dio_insn_bits;
269 s->insn_config = bonding_dio_insn_config;
271 LOG_MSG("attached with %u DIO channels coming from %u different "
272 "subdevices all bonded together. "
273 "John Lennon would be proud!\n",
274 devpriv->nchans, devpriv->ndevs);
280 * _detach is called to deconfigure a device. It should deallocate
282 * This function is also called when _attach() fails, so it should be
283 * careful not to release resources that were not necessarily
284 * allocated by _attach(). dev->private and dev->subdevices are
285 * deallocated automatically by the core.
287 static int bonding_detach(comedi_device *dev)
289 LOG_MSG("comedi%d: remove\n", dev->minor);
294 /* DIO devices are slightly special. Although it is possible to
295 * implement the insn_read/insn_write interface, it is much more
296 * useful to applications if you implement the insn_bits interface.
297 * This allows packed reading/writing of the DIO channels. The
298 * comedi core can convert between insn_bits and insn_read/write */
299 static int bonding_dio_insn_bits(comedi_device *dev, comedi_subdevice *s,
300 comedi_insn *insn, lsampl_t *data)
302 #define LSAMPL_BITS (sizeof(lsampl_t)*8)
303 unsigned nchans = LSAMPL_BITS, num_done = 0, i;
307 if (devpriv->nchans < nchans)
308 nchans = devpriv->nchans;
310 /* The insn data is a mask in data[0] and the new data
311 * in data[1], each channel cooresponding to a bit. */
312 for (i = 0; num_done < nchans && i < devpriv->ndevs; ++i) {
313 BondedDevice *bdev = devpriv->devs[i];
314 /* Grab the channel mask and data of only the bits corresponding
315 to this subdevice.. need to shift them to zero position of
317 /* Bits corresponding to this subdev. */
318 lsampl_t subdevMask = ((1 << bdev->nchans) - 1);
319 lsampl_t writeMask, dataBits;
321 /* Argh, we have >= LSAMPL_BITS chans.. take all bits */
322 if (bdev->nchans >= LSAMPL_BITS)
323 subdevMask = (lsampl_t) (-1);
325 writeMask = (data[0] >> num_done) & subdevMask;
326 dataBits = (data[1] >> num_done) & subdevMask;
328 /* Read/Write the new digital lines */
329 if (comedi_dio_bitfield(bdev->dev, bdev->subdev, writeMask,
333 /* Make room for the new bits in data[1], the return value */
334 data[1] &= ~(subdevMask << num_done);
335 /* Put the bits in the return value */
336 data[1] |= (dataBits & subdevMask) << num_done;
337 /* Save the new bits to the saved state.. */
340 num_done += bdev->nchans;
346 static int bonding_dio_insn_config(comedi_device *dev, comedi_subdevice *s,
347 comedi_insn *insn, lsampl_t *data)
349 int chan = CR_CHAN(insn->chanspec), ret, io_bits = s->io_bits;
353 if (chan < 0 || chan >= devpriv->nchans)
355 bdev = devpriv->chanIdDevMap[chan];
357 /* The input or output configuration of each digital line is
358 * configured by a special insn_config instruction. chanspec
359 * contains the channel to be changed, and data[0] contains the
360 * value COMEDI_INPUT or COMEDI_OUTPUT. */
362 case INSN_CONFIG_DIO_OUTPUT:
363 io = COMEDI_OUTPUT; /* is this really necessary? */
364 io_bits |= 1 << chan;
366 case INSN_CONFIG_DIO_INPUT:
367 io = COMEDI_INPUT; /* is this really necessary? */
368 io_bits &= ~(1 << chan);
370 case INSN_CONFIG_DIO_QUERY:
372 (io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
379 /* 'real' channel id for this subdev.. */
380 chan -= bdev->chanid_offset;
381 ret = comedi_dio_config(bdev->dev, bdev->subdev, chan, io);
384 /* Finally, save the new io_bits values since we didn't get
386 s->io_bits = io_bits;
390 static void *Realloc(const void *oldmem, size_t newlen, size_t oldlen)
392 void *newmem = kmalloc(newlen, GFP_KERNEL);
394 if (newmem && oldmem)
395 memcpy(newmem, oldmem, min(oldlen, newlen));
400 static int doDevConfig(comedi_device *dev, comedi_devconfig *it)
403 comedi_t *devs_opened[COMEDI_NUM_BOARD_MINORS];
405 memset(devs_opened, 0, sizeof(devs_opened));
406 devpriv->name[0] = 0;;
407 /* Loop through all comedi devices specified on the command-line,
408 building our device list */
409 for (i = 0; i < COMEDI_NDEVCONFOPTS && (!i || it->options[i]); ++i) {
410 char file[] = "/dev/comediXXXXXX";
411 int minor = it->options[i];
413 int sdev = -1, nchans, tmp;
414 BondedDevice *bdev = 0;
416 if (minor < 0 || minor > COMEDI_NUM_BOARD_MINORS) {
417 ERROR("Minor %d is invalid!\n", minor);
420 if (minor == dev->minor) {
421 ERROR("Cannot bond this driver to itself!\n");
424 if (devs_opened[minor]) {
425 ERROR("Minor %d specified more than once!\n", minor);
429 snprintf(file, sizeof(file), "/dev/comedi%u", minor);
430 file[sizeof(file) - 1] = 0;
432 d = devs_opened[minor] = comedi_open(file);
435 ERROR("Minor %u could not be opened\n", minor);
439 /* Do DIO, as that's all we support now.. */
440 while ((sdev = comedi_find_subdevice_by_type(d, COMEDI_SUBD_DIO,
442 nchans = comedi_get_n_channels(d, sdev);
444 ERROR("comedi_get_n_channels() returned %d "
445 "on minor %u subdev %d!\n",
446 nchans, minor, sdev);
449 bdev = kmalloc(sizeof(*bdev), GFP_KERNEL);
451 ERROR("Out of memory.\n");
457 bdev->subdev_type = COMEDI_SUBD_DIO;
458 bdev->nchans = nchans;
459 bdev->chanid_offset = devpriv->nchans;
461 /* map channel id's to BondedDevice * pointer.. */
463 devpriv->chanIdDevMap[devpriv->nchans++] = bdev;
465 /* Now put bdev pointer at end of devpriv->devs array
468 /* ergh.. ugly.. we need to realloc :( */
469 tmp = devpriv->ndevs * sizeof(bdev);
471 Realloc(devpriv->devs,
472 ++devpriv->ndevs * sizeof(bdev), tmp);
473 if (!devpriv->devs) {
474 ERROR("Could not allocate memory. "
479 devpriv->devs[devpriv->ndevs - 1] = bdev;
481 /** Append dev:subdev to devpriv->name */
484 MAX_BOARD_NAME - strlen(devpriv->name) -
486 snprintf(buf, sizeof(buf), "%d:%d ", dev->minor,
488 buf[sizeof(buf) - 1] = 0;
489 strncat(devpriv->name, buf, left);
495 if (!devpriv->nchans) {
496 ERROR("No channels found!\n");
503 static void doDevUnconfig(comedi_device *dev)
505 unsigned long devs_closed = 0;
508 while (devpriv->ndevs-- && devpriv->devs) {
509 BondedDevice *bdev = devpriv->devs[devpriv->ndevs];
512 if (!(devs_closed & (0x1 << bdev->minor))) {
513 comedi_close(bdev->dev);
514 devs_closed |= (0x1 << bdev->minor);
518 kfree(devpriv->devs);
525 int __init init(void)
527 return comedi_driver_register(&driver_bonding);
530 void __exit cleanup(void)
532 comedi_driver_unregister(&driver_bonding);
536 module_exit(cleanup);