comedi_bond.c revision 3cf840f65ad5684c91e4f55dc939e2b773f9a444
1/*
2    comedi/drivers/comedi_bond.c
3    A Comedi driver to 'bond' or merge multiple drivers and devices as one.
4
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>
8
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.
13
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.
18
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.
22
23*/
24/*
25Driver: comedi_bond
26Description: A driver to 'bond' (merge) multiple subdevices from multiple devices together as one.
27Devices:
28Author: ds
29Updated: Mon, 10 Oct 00:18:25 -0500
30Status: works
31
32This driver allows you to 'bond' (merge) multiple comedi subdevices
33(coming from possibly difference boards and/or drivers) together.  For
34example, if you had a board with 2 different DIO subdevices, and
35another with 1 DIO subdevice, you could 'bond' them with this driver
36so that they look like one big fat DIO subdevice.  This makes writing
37applications slightly easier as you don't have to worry about managing
38different subdevices in the application -- you just worry about
39indexing one linear array of channel id's.
40
41Right now only DIO subdevices are supported as that's the personal itch
42I am scratching with this driver.  If you want to add support for AI and AO
43subdevs, go right on ahead and do so!
44
45Commands aren't supported -- although it would be cool if they were.
46
47Configuration 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.
50*/
51
52/*
53 * The previous block comment is used to automatically generate
54 * documentation in Comedi and Comedilib.  The fields:
55 *
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
63 *   value is used.
64 * Author: you
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
69 *     passes comedi_test.
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.
78 *
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
83 * subdevices, etc.
84 *
85 * Somewhere in the comment should be information about configuration
86 * options that are used with comedi_config.
87 */
88
89#include "../comedilib.h"
90#include "../comedidev.h"
91#include <linux/string.h>
92
93/* The maxiumum number of channels per subdevice. */
94#define MAX_CHANS 256
95
96#define MODULE_NAME "comedi_bond"
97#ifdef MODULE_LICENSE
98MODULE_LICENSE("GPL");
99#endif
100#ifndef STR
101#  define STR1(x) #x
102#  define STR(x) STR1(x)
103#endif
104
105int debug = 0;
106module_param(debug, int, 0644);
107MODULE_PARM_DESC(debug,
108	"If true, print extra cryptic debugging output useful only to developers probably.");
109
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)
114MODULE_AUTHOR("Calin A. Culianu");
115MODULE_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...'");
117
118/*
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.
122 */
123struct BondingBoard {
124	const char *name;
125};
126typedef struct BondingBoard BondingBoard;
127
128static const BondingBoard bondingBoards[] = {
129	{
130	      name:	MODULE_NAME,
131		},
132};
133
134/*
135 * Useful for shorthand access to the particular board structure
136 */
137#define thisboard ((const BondingBoard *)dev->board_ptr)
138
139struct BondedDevice {
140	comedi_t *dev;
141	unsigned minor;
142	unsigned subdev;
143	unsigned subdev_type;
144	unsigned nchans;
145	unsigned chanid_offset;	/* The offset into our unified linear channel-id's
146				   of chanid 0 on this subdevice. */
147};
148typedef struct BondedDevice BondedDevice;
149
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.  */
153struct Private {
154# define MAX_BOARD_NAME 256
155	char name[MAX_BOARD_NAME];
156	struct BondedDevice **devs;
157	unsigned ndevs;
158	struct BondedDevice *chanIdDevMap[MAX_CHANS];
159	unsigned nchans;
160};
161typedef struct Private Private;
162
163/*
164 * most drivers define the following macro to make it easy to
165 * access the private structure.
166 */
167#define devpriv ((Private *)dev->private)
168
169/*
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
173 * the device code.
174 */
175static int bonding_attach(comedi_device * dev, comedi_devconfig * it);
176static int bonding_detach(comedi_device * dev);
177/** Build Private array of all devices.. */
178static int doDevConfig(comedi_device * dev, comedi_devconfig * it);
179static 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.. :) */
181static void *Realloc(const void *ptr, size_t len, size_t old_len);
182
183static comedi_driver driver_bonding = {
184      driver_name:MODULE_NAME,
185      module:THIS_MODULE,
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.
201	 *
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.
205	 */
206      board_name:&bondingBoards[0].name,
207      offset:sizeof(BondingBoard),
208      num_names:sizeof(bondingBoards) / sizeof(BondingBoard),
209};
210
211static int bonding_dio_insn_bits(comedi_device * dev, comedi_subdevice * s,
212	comedi_insn * insn, lsampl_t * data);
213static int bonding_dio_insn_config(comedi_device * dev, comedi_subdevice * s,
214	comedi_insn * insn, lsampl_t * data);
215
216/*
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
220 * address.
221 */
222static int bonding_attach(comedi_device * dev, comedi_devconfig * it)
223{
224	comedi_subdevice *s;
225
226	LOG_MSG("comedi%d\n", dev->minor);
227
228/*
229 * Allocate the private structure area.  alloc_private() is a
230 * convenient macro defined in comedidev.h.
231 */
232	if (alloc_private(dev, sizeof(Private)) < 0)
233		return -ENOMEM;
234
235/*
236 * Setup our bonding from config params.. sets up our Private struct..
237 */
238	if (!doDevConfig(dev, it))
239		return -EINVAL;
240
241/*
242 * Initialize dev->board_name.  Note that we can use the "thisboard"
243 * macro now, since we just initialized it in the last line.
244 */
245	dev->board_name = devpriv->name;
246
247/*
248 * Allocate the subdevice structures.  alloc_subdevice() is a
249 * convenient macro defined in comedidev.h.
250 */
251	if (alloc_subdevices(dev, 1) < 0)
252		return -ENOMEM;
253
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;
258	s->maxdata = 1;
259	s->range_table = &range_digital;
260	s->insn_bits = bonding_dio_insn_bits;
261	s->insn_config = bonding_dio_insn_config;
262
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);
264
265	return 1;
266}
267
268/*
269 * _detach is called to deconfigure a device.  It should deallocate
270 * resources.
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.
275 */
276static int bonding_detach(comedi_device * dev)
277{
278	LOG_MSG("comedi%d: remove\n", dev->minor);
279	doDevUnconfig(dev);
280	return 0;
281}
282
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 */
288static int bonding_dio_insn_bits(comedi_device * dev, comedi_subdevice * s,
289	comedi_insn * insn, lsampl_t * data)
290{
291#define LSAMPL_BITS (sizeof(lsampl_t)*8)
292	unsigned nchans = LSAMPL_BITS, num_done = 0, i;
293	if (insn->n != 2)
294		return -EINVAL;
295
296	if (devpriv->nchans < nchans)
297		nchans = devpriv->nchans;
298
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
305		   course. */
306		lsampl_t subdevMask = ((1 << bdev->nchans) - 1);	/* Bits corresponding
307									   to this subdev. */
308		lsampl_t writeMask, dataBits;
309
310		/* Argh, we have >= LSAMPL_BITS chans.. take all bits */
311		if (bdev->nchans >= LSAMPL_BITS)
312			subdevMask = (lsampl_t) (-1);
313
314		writeMask = (data[0] >> num_done) & subdevMask;
315		dataBits = (data[1] >> num_done) & subdevMask;
316
317		/* Read/Write the new digital lines */
318		if (comedi_dio_bitfield(bdev->dev, bdev->subdev, writeMask,
319				&dataBits) != 2)
320			return -EINVAL;
321
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.. */
327		s->state = data[1];
328
329		num_done += bdev->nchans;
330	}
331
332	return insn->n;
333}
334
335static int bonding_dio_insn_config(comedi_device * dev, comedi_subdevice * s,
336	comedi_insn * insn, lsampl_t * data)
337{
338	int chan = CR_CHAN(insn->chanspec), ret, io_bits = s->io_bits;
339	unsigned int io;
340	BondedDevice *bdev;
341
342	if (chan < 0 || chan >= devpriv->nchans)
343		return -EINVAL;
344	bdev = devpriv->chanIdDevMap[chan];
345
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. */
350	switch (data[0]) {
351	case INSN_CONFIG_DIO_OUTPUT:
352		io = COMEDI_OUTPUT;	/* is this really necessary? */
353		io_bits |= 1 << chan;
354		break;
355	case INSN_CONFIG_DIO_INPUT:
356		io = COMEDI_INPUT;	/* is this really necessary? */
357		io_bits &= ~(1 << chan);
358		break;
359	case INSN_CONFIG_DIO_QUERY:
360		data[1] =
361			(io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
362		return insn->n;
363		break;
364	default:
365		return -EINVAL;
366		break;
367	}
368	chan -= bdev->chanid_offset;	/* 'real' channel id for this subdev.. */
369	ret = comedi_dio_config(bdev->dev, bdev->subdev, chan, io);
370	if (ret != 1)
371		return -EINVAL;
372	/* Finally, save the new io_bits values since we didn't get
373	   an error above. */
374	s->io_bits = io_bits;
375	return insn->n;
376}
377
378static void *Realloc(const void *oldmem, size_t newlen, size_t oldlen)
379{
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));
384	if (oldmem)
385		kfree(oldmem);
386	return newmem;
387}
388
389static int doDevConfig(comedi_device * dev, comedi_devconfig * it)
390{
391	int i;
392	comedi_t *devs_opened[COMEDI_NUM_BOARD_MINORS];
393
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];
401		comedi_t *d;
402		int sdev = -1, nchans, tmp;
403		BondedDevice *bdev = 0;
404
405		if (minor < 0 || minor > COMEDI_NUM_BOARD_MINORS) {
406			ERROR("Minor %d is invalid!\n", minor);
407			return 0;
408		}
409		if (minor == dev->minor) {
410			ERROR("Cannot bond this driver to itself!\n");
411			return 0;
412		}
413		if (devs_opened[minor]) {
414			ERROR("Minor %d specified more than once!\n", minor);
415			return 0;
416		}
417
418		snprintf(file, sizeof(file), "/dev/comedi%u", minor);
419		file[sizeof(file) - 1] = 0;
420
421		d = devs_opened[minor] = comedi_open(file);
422
423		if (!d) {
424			ERROR("Minor %u could not be opened\n", minor);
425			return 0;
426		}
427
428		/* Do DIO, as that's all we support now.. */
429		while ((sdev = comedi_find_subdevice_by_type(d, COMEDI_SUBD_DIO,
430					sdev + 1)) > -1) {
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);
433				return 0;
434			}
435			bdev = kmalloc(sizeof(*bdev), GFP_KERNEL);
436			if (!bdev) {
437				ERROR("Out of memory.\n");
438				return 0;
439			}
440			bdev->dev = d;
441			bdev->minor = minor;
442			bdev->subdev = sdev;
443			bdev->subdev_type = COMEDI_SUBD_DIO;
444			bdev->nchans = nchans;
445			bdev->chanid_offset = devpriv->nchans;
446
447			/* map channel id's to BondedDevice * pointer.. */
448			while (nchans--)
449				devpriv->chanIdDevMap[devpriv->nchans++] = bdev;
450
451			/* Now put bdev pointer at end of devpriv->devs array list.. */
452
453			/* ergh.. ugly.. we need to realloc :(  */
454			tmp = devpriv->ndevs * sizeof(bdev);
455			devpriv->devs =
456				Realloc(devpriv->devs,
457				++devpriv->ndevs * sizeof(bdev), tmp);
458			if (!devpriv->devs) {
459				ERROR("Could not allocate memory. Out of memory?");
460				return 0;
461			}
462
463			devpriv->devs[devpriv->ndevs - 1] = bdev;
464			{
465	/** Append dev:subdev to devpriv->name */
466				char buf[20];
467				int left =
468					MAX_BOARD_NAME - strlen(devpriv->name) -
469					1;
470				snprintf(buf, sizeof(buf), "%d:%d ", dev->minor,
471					bdev->subdev);
472				buf[sizeof(buf) - 1] = 0;
473				strncat(devpriv->name, buf, left);
474			}
475
476		}
477	}
478
479	if (!devpriv->nchans) {
480		ERROR("No channels found!\n");
481		return 0;
482	}
483
484	return 1;
485}
486
487static void doDevUnconfig(comedi_device * dev)
488{
489	unsigned long devs_closed = 0;
490
491	if (devpriv) {
492		while (devpriv->ndevs-- && devpriv->devs) {
493			BondedDevice *bdev = devpriv->devs[devpriv->ndevs];
494			if (!bdev)
495				continue;
496			if (!(devs_closed & (0x1 << bdev->minor))) {
497				comedi_close(bdev->dev);
498				devs_closed |= (0x1 << bdev->minor);
499			}
500			kfree(bdev);
501		}
502		if (devpriv->devs) {
503			kfree(devpriv->devs);
504			devpriv->devs = 0;
505		}
506		kfree(devpriv);
507		dev->private = 0;
508	}
509}
510
511int __init init(void)
512{
513	return comedi_driver_register(&driver_bonding);
514}
515
516void __exit cleanup(void)
517{
518	comedi_driver_unregister(&driver_bonding);
519}
520
521module_init(init);
522module_exit(cleanup);
523