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 devices together as one.
29 Updated: Mon, 10 Oct 00:18:25 -0500
32 This driver allows you to 'bond' (merge) multiple comedi subdevices
33 (coming from possibly difference boards and/or drivers) together. For
34 example, if you had a board with 2 different DIO subdevices, and
35 another with 1 DIO subdevice, you could 'bond' them with this driver
36 so that they look like one big fat DIO subdevice. This makes writing
37 applications slightly easier as you don't have to worry about managing
38 different subdevices in the application -- you just worry about
39 indexing one linear array of channel id's.
41 Right now only DIO subdevices are supported as that's the personal itch
42 I am scratching with this driver. If you want to add support for AI and AO
43 subdevs, go right on ahead and do so!
45 Commands aren't supported -- although it would be cool if they were.
47 Configuration Options:
48 List of comedi-minors to bond. All subdevices of the same type
49 within each minor will be concatenated together in the order given here.
53 * The previous block comment is used to automatically generate
54 * documentation in Comedi and Comedilib. The fields:
56 * Driver: the name of the driver
57 * Description: a short phrase describing the driver. Don't list boards.
58 * Devices: a full list of the boards that attempt to be supported by
59 * the driver. Format is "(manufacturer) board name [comedi name]",
60 * where comedi_name is the name that is used to configure the board.
61 * See the comment near board_name: in the comedi_driver structure
62 * below. If (manufacturer) or [comedi name] is missing, the previous
65 * Updated: date when the _documentation_ was last updated. Use 'date -R'
66 * to get a value for this.
67 * Status: a one-word description of the status. Valid values are:
68 * works - driver works correctly on most boards supported, and
70 * unknown - unknown. Usually put there by ds.
71 * experimental - may not work in any particular release. Author
72 * probably wants assistance testing it.
73 * bitrotten - driver has not been update in a long time, probably
74 * doesn't work, and probably is missing support for significant
75 * Comedi interface features.
76 * untested - author probably wrote it "blind", and is believed to
77 * work, but no confirmation.
79 * These headers should be followed by a blank line, and any comments
80 * you wish to say about the driver. The comment area is the place
81 * to put any known bugs, limitations, unsupported features, supported
82 * command triggers, whether or not commands are supported on particular
85 * Somewhere in the comment should be information about configuration
86 * options that are used with comedi_config.
89 #include "../comedilib.h"
90 #include "../comedidev.h"
91 #include <linux/string.h>
93 /* The maxiumum number of channels per subdevice. */
96 #define MODULE_NAME "comedi_bond"
98 MODULE_LICENSE("GPL");
102 # define STR(x) STR1(x)
106 module_param(debug, int, 0644);
107 MODULE_PARM_DESC(debug,
108 "If true, print extra cryptic debugging output useful only to developers probably.");
110 #define LOG_MSG(x...) printk(KERN_INFO MODULE_NAME": "x)
111 #define DEBUG(x...) do { if(debug) printk(KERN_DEBUG MODULE_NAME": DEBUG: "x); } while(0)
112 #define WARNING(x...) printk(KERN_WARNING MODULE_NAME ": WARNING: "x)
113 #define ERROR(x...) printk(KERN_ERR MODULE_NAME ": INTERNAL ERROR: "x)
114 MODULE_AUTHOR("Calin A. Culianu");
115 MODULE_DESCRIPTION(MODULE_NAME
116 ": A driver for COMEDI to bond multiple COMEDI devices together as one. In the words of John Lennon: 'And the world will live as one...'");
119 * Board descriptions for two imaginary boards. Describing the
120 * boards in this way is optional, and completely driver-dependent.
121 * Some drivers use arrays such as this, other do not.
123 struct BondingBoard {
126 typedef struct BondingBoard BondingBoard;
128 static const BondingBoard bondingBoards[] = {
135 * Useful for shorthand access to the particular board structure
137 #define thisboard ((const BondingBoard *)dev->board_ptr)
139 struct BondedDevice {
143 unsigned subdev_type;
145 unsigned chanid_offset; /* The offset into our unified linear channel-id's
146 of chanid 0 on this subdevice. */
148 typedef struct BondedDevice BondedDevice;
150 /* this structure is for data unique to this hardware driver. If
151 several hardware drivers keep similar information in this structure,
152 feel free to suggest moving the variable to the comedi_device struct. */
154 # define MAX_BOARD_NAME 256
155 char name[MAX_BOARD_NAME];
156 struct BondedDevice **devs;
158 struct BondedDevice *chanIdDevMap[MAX_CHANS];
161 typedef struct Private Private;
164 * most drivers define the following macro to make it easy to
165 * access the private structure.
167 #define devpriv ((Private *)dev->private)
170 * The comedi_driver structure tells the Comedi core module
171 * which functions to call to configure/deconfigure (attach/detach)
172 * the board, and also about the kernel module that contains
175 static int bonding_attach(comedi_device * dev, comedi_devconfig * it);
176 static int bonding_detach(comedi_device * dev);
177 /** Build Private array of all devices.. */
178 static int doDevConfig(comedi_device * dev, comedi_devconfig * it);
179 static void doDevUnconfig(comedi_device * dev);
180 /* Ugly implementation of realloc that always copies memory around -- I'm lazy, what can I say? I like to do wasteful memcopies.. :) */
181 static void *Realloc(const void *ptr, size_t len, size_t old_len);
183 static comedi_driver driver_bonding = {
184 driver_name:MODULE_NAME,
186 attach:bonding_attach,
187 detach:bonding_detach,
188 /* It is not necessary to implement the following members if you are
189 * writing a driver for a ISA PnP or PCI card */
190 /* Most drivers will support multiple types of boards by
191 * having an array of board structures. These were defined
192 * in skel_boards[] above. Note that the element 'name'
193 * was first in the structure -- Comedi uses this fact to
194 * extract the name of the board without knowing any details
195 * about the structure except for its length.
196 * When a device is attached (by comedi_config), the name
197 * of the device is given to Comedi, and Comedi tries to
198 * match it by going through the list of board names. If
199 * there is a match, the address of the pointer is put
200 * into dev->board_ptr and driver->attach() is called.
202 * Note that these are not necessary if you can determine
203 * the type of board in software. ISA PnP, PCI, and PCMCIA
204 * devices are such boards.
206 board_name:&bondingBoards[0].name,
207 offset:sizeof(BondingBoard),
208 num_names:sizeof(bondingBoards) / sizeof(BondingBoard),
211 static int bonding_dio_insn_bits(comedi_device * dev, comedi_subdevice * s,
212 comedi_insn * insn, lsampl_t * data);
213 static int bonding_dio_insn_config(comedi_device * dev, comedi_subdevice * s,
214 comedi_insn * insn, lsampl_t * data);
217 * Attach is called by the Comedi core to configure the driver
218 * for a particular board. If you specified a board_name array
219 * in the driver structure, dev->board_ptr contains that
222 static int bonding_attach(comedi_device * dev, comedi_devconfig * it)
226 LOG_MSG("comedi%d\n", dev->minor);
229 * Allocate the private structure area. alloc_private() is a
230 * convenient macro defined in comedidev.h.
232 if (alloc_private(dev, sizeof(Private)) < 0)
236 * Setup our bonding from config params.. sets up our Private struct..
238 if (!doDevConfig(dev, it))
242 * Initialize dev->board_name. Note that we can use the "thisboard"
243 * macro now, since we just initialized it in the last line.
245 dev->board_name = devpriv->name;
248 * Allocate the subdevice structures. alloc_subdevice() is a
249 * convenient macro defined in comedidev.h.
251 if (alloc_subdevices(dev, 1) < 0)
254 s = dev->subdevices + 0;
255 s->type = COMEDI_SUBD_DIO;
256 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
257 s->n_chan = devpriv->nchans;
259 s->range_table = &range_digital;
260 s->insn_bits = bonding_dio_insn_bits;
261 s->insn_config = bonding_dio_insn_config;
263 LOG_MSG("attached with %u DIO channels coming from %u different subdevices all bonded together. John Lennon would be proud!\n", devpriv->nchans, devpriv->ndevs);
269 * _detach is called to deconfigure a device. It should deallocate
271 * This function is also called when _attach() fails, so it should be
272 * careful not to release resources that were not necessarily
273 * allocated by _attach(). dev->private and dev->subdevices are
274 * deallocated automatically by the core.
276 static int bonding_detach(comedi_device * dev)
278 LOG_MSG("comedi%d: remove\n", dev->minor);
283 /* DIO devices are slightly special. Although it is possible to
284 * implement the insn_read/insn_write interface, it is much more
285 * useful to applications if you implement the insn_bits interface.
286 * This allows packed reading/writing of the DIO channels. The
287 * comedi core can convert between insn_bits and insn_read/write */
288 static int bonding_dio_insn_bits(comedi_device * dev, comedi_subdevice * s,
289 comedi_insn * insn, lsampl_t * data)
291 #define LSAMPL_BITS (sizeof(lsampl_t)*8)
292 unsigned nchans = LSAMPL_BITS, num_done = 0, i;
296 if (devpriv->nchans < nchans)
297 nchans = devpriv->nchans;
299 /* The insn data is a mask in data[0] and the new data
300 * in data[1], each channel cooresponding to a bit. */
301 for (i = 0; num_done < nchans && i < devpriv->ndevs; ++i) {
302 BondedDevice *bdev = devpriv->devs[i];
303 /* Grab the channel mask and data of only the bits corresponding
304 to this subdevice.. need to shift them to zero position of
306 lsampl_t subdevMask = ((1 << bdev->nchans) - 1); /* Bits corresponding
308 lsampl_t writeMask, dataBits;
310 /* Argh, we have >= LSAMPL_BITS chans.. take all bits */
311 if (bdev->nchans >= LSAMPL_BITS)
312 subdevMask = (lsampl_t) (-1);
314 writeMask = (data[0] >> num_done) & subdevMask;
315 dataBits = (data[1] >> num_done) & subdevMask;
317 /* Read/Write the new digital lines */
318 if (comedi_dio_bitfield(bdev->dev, bdev->subdev, writeMask,
322 /* Make room for the new bits in data[1], the return value */
323 data[1] &= ~(subdevMask << num_done);
324 /* Put the bits in the return value */
325 data[1] |= (dataBits & subdevMask) << num_done;
326 /* Save the new bits to the saved state.. */
329 num_done += bdev->nchans;
335 static int bonding_dio_insn_config(comedi_device * dev, comedi_subdevice * s,
336 comedi_insn * insn, lsampl_t * data)
338 int chan = CR_CHAN(insn->chanspec), ret, io_bits = s->io_bits;
342 if (chan < 0 || chan >= devpriv->nchans)
344 bdev = devpriv->chanIdDevMap[chan];
346 /* The input or output configuration of each digital line is
347 * configured by a special insn_config instruction. chanspec
348 * contains the channel to be changed, and data[0] contains the
349 * value COMEDI_INPUT or COMEDI_OUTPUT. */
351 case INSN_CONFIG_DIO_OUTPUT:
352 io = COMEDI_OUTPUT; /* is this really necessary? */
353 io_bits |= 1 << chan;
355 case INSN_CONFIG_DIO_INPUT:
356 io = COMEDI_INPUT; /* is this really necessary? */
357 io_bits &= ~(1 << chan);
359 case INSN_CONFIG_DIO_QUERY:
361 (io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
368 chan -= bdev->chanid_offset; /* 'real' channel id for this subdev.. */
369 ret = comedi_dio_config(bdev->dev, bdev->subdev, chan, io);
372 /* Finally, save the new io_bits values since we didn't get
374 s->io_bits = io_bits;
378 static void *Realloc(const void *oldmem, size_t newlen, size_t oldlen)
380 #define MIN(a,b) (a < b ? a : b)
381 void *newmem = kmalloc(newlen, GFP_KERNEL);
382 if (newmem && oldmem)
383 memcpy(newmem, oldmem, MIN(oldlen, newlen));
389 static int doDevConfig(comedi_device * dev, comedi_devconfig * it)
392 comedi_t *devs_opened[COMEDI_NUM_BOARD_MINORS];
394 memset(devs_opened, 0, sizeof(devs_opened));
395 devpriv->name[0] = 0;;
396 /* Loop through all comedi devices specified on the command-line,
397 building our device list */
398 for (i = 0; i < COMEDI_NDEVCONFOPTS && (!i || it->options[i]); ++i) {
399 char file[] = "/dev/comediXXXXXX";
400 int minor = it->options[i];
402 int sdev = -1, nchans, tmp;
403 BondedDevice *bdev = 0;
405 if (minor < 0 || minor > COMEDI_NUM_BOARD_MINORS) {
406 ERROR("Minor %d is invalid!\n", minor);
409 if (minor == dev->minor) {
410 ERROR("Cannot bond this driver to itself!\n");
413 if (devs_opened[minor]) {
414 ERROR("Minor %d specified more than once!\n", minor);
418 snprintf(file, sizeof(file), "/dev/comedi%u", minor);
419 file[sizeof(file) - 1] = 0;
421 d = devs_opened[minor] = comedi_open(file);
424 ERROR("Minor %u could not be opened\n", minor);
428 /* Do DIO, as that's all we support now.. */
429 while ((sdev = comedi_find_subdevice_by_type(d, COMEDI_SUBD_DIO,
431 if ((nchans = comedi_get_n_channels(d, sdev)) <= 0) {
432 ERROR("comedi_get_n_channels() returned %d on minor %u subdev %d!\n", nchans, minor, sdev);
435 bdev = kmalloc(sizeof(*bdev), GFP_KERNEL);
437 ERROR("Out of memory.\n");
443 bdev->subdev_type = COMEDI_SUBD_DIO;
444 bdev->nchans = nchans;
445 bdev->chanid_offset = devpriv->nchans;
447 /* map channel id's to BondedDevice * pointer.. */
449 devpriv->chanIdDevMap[devpriv->nchans++] = bdev;
451 /* Now put bdev pointer at end of devpriv->devs array list.. */
453 /* ergh.. ugly.. we need to realloc :( */
454 tmp = devpriv->ndevs * sizeof(bdev);
456 Realloc(devpriv->devs,
457 ++devpriv->ndevs * sizeof(bdev), tmp);
458 if (!devpriv->devs) {
459 ERROR("Could not allocate memory. Out of memory?");
463 devpriv->devs[devpriv->ndevs - 1] = bdev;
465 /** Append dev:subdev to devpriv->name */
468 MAX_BOARD_NAME - strlen(devpriv->name) -
470 snprintf(buf, sizeof(buf), "%d:%d ", dev->minor,
472 buf[sizeof(buf) - 1] = 0;
473 strncat(devpriv->name, buf, left);
479 if (!devpriv->nchans) {
480 ERROR("No channels found!\n");
487 static void doDevUnconfig(comedi_device * dev)
489 unsigned long devs_closed = 0;
492 while (devpriv->ndevs-- && devpriv->devs) {
493 BondedDevice *bdev = devpriv->devs[devpriv->ndevs];
496 if (!(devs_closed & (0x1 << bdev->minor))) {
497 comedi_close(bdev->dev);
498 devs_closed |= (0x1 << bdev->minor);
503 kfree(devpriv->devs);
511 int __init init(void)
513 return comedi_driver_register(&driver_bonding);
516 void __exit cleanup(void)
518 comedi_driver_unregister(&driver_bonding);
522 module_exit(cleanup);