comedi_bond.c revision 787ae4e26f3600c7d30693a92e18174a2d6363fc
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
27	     devices together as one.
28Devices:
29Author: ds
30Updated: Mon, 10 Oct 00:18:25 -0500
31Status: works
32
33This driver allows you to 'bond' (merge) multiple comedi subdevices
34(coming from possibly difference boards and/or drivers) together.  For
35example, if you had a board with 2 different DIO subdevices, and
36another with 1 DIO subdevice, you could 'bond' them with this driver
37so that they look like one big fat DIO subdevice.  This makes writing
38applications slightly easier as you don't have to worry about managing
39different subdevices in the application -- you just worry about
40indexing one linear array of channel id's.
41
42Right now only DIO subdevices are supported as that's the personal itch
43I am scratching with this driver.  If you want to add support for AI and AO
44subdevs, go right on ahead and do so!
45
46Commands aren't supported -- although it would be cool if they were.
47
48Configuration 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.
51*/
52
53/*
54 * The previous block comment is used to automatically generate
55 * documentation in Comedi and Comedilib.  The fields:
56 *
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 struct comedi_driver structure
63 *   below.  If (manufacturer) or [comedi name] is missing, the previous
64 *   value is used.
65 * Author: you
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
70 *     passes comedi_test.
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.
79 *
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
84 * subdevices, etc.
85 *
86 * Somewhere in the comment should be information about configuration
87 * options that are used with comedi_config.
88 */
89
90#include "../comedilib.h"
91#include "../comedidev.h"
92#include <linux/string.h>
93#include <linux/slab.h>
94
95/* The maxiumum number of channels per subdevice. */
96#define MAX_CHANS 256
97
98#define MODULE_NAME "comedi_bond"
99MODULE_LICENSE("GPL");
100#ifndef STR
101#  define STR1(x) #x
102#  define STR(x) STR1(x)
103#endif
104
105static int debug;
106module_param(debug, int, 0644);
107MODULE_PARM_DESC(debug, "If true, print extra cryptic debugging output useful"
108		 "only to developers.");
109
110#define LOG_MSG(x...) printk(KERN_INFO MODULE_NAME": "x)
111#define DEBUG(x...)							\
112	do {								\
113		if (debug)						\
114			printk(KERN_DEBUG MODULE_NAME": DEBUG: "x);	\
115	} while (0)
116#define WARNING(x...)  printk(KERN_WARNING MODULE_NAME ": WARNING: "x)
117#define ERROR(x...)  printk(KERN_ERR MODULE_NAME ": INTERNAL ERROR: "x)
118MODULE_AUTHOR("Calin A. Culianu");
119MODULE_DESCRIPTION(MODULE_NAME "A driver for COMEDI to bond multiple COMEDI "
120		   "devices together as one.  In the words of John Lennon: "
121		   "'And the world will live as one...'");
122
123/*
124 * Board descriptions for two imaginary boards.  Describing the
125 * boards in this way is optional, and completely driver-dependent.
126 * Some drivers use arrays such as this, other do not.
127 */
128struct BondingBoard {
129	const char *name;
130};
131
132static const struct BondingBoard bondingBoards[] = {
133	{
134	 .name = MODULE_NAME,
135	 },
136};
137
138/*
139 * Useful for shorthand access to the particular board structure
140 */
141#define thisboard ((const struct BondingBoard *)dev->board_ptr)
142
143struct BondedDevice {
144	void *dev;
145	unsigned minor;
146	unsigned subdev;
147	unsigned subdev_type;
148	unsigned nchans;
149	unsigned chanid_offset;	/* The offset into our unified linear
150				   channel-id's of chanid 0 on this
151				   subdevice. */
152};
153
154/* this structure is for data unique to this hardware driver.  If
155   several hardware drivers keep similar information in this structure,
156   feel free to suggest moving the variable to the struct comedi_device struct.  */
157struct Private {
158# define MAX_BOARD_NAME 256
159	char name[MAX_BOARD_NAME];
160	struct BondedDevice **devs;
161	unsigned ndevs;
162	struct BondedDevice *chanIdDevMap[MAX_CHANS];
163	unsigned nchans;
164};
165
166/*
167 * most drivers define the following macro to make it easy to
168 * access the private structure.
169 */
170#define devpriv ((struct Private *)dev->private)
171
172/*
173 * The struct comedi_driver structure tells the Comedi core module
174 * which functions to call to configure/deconfigure (attach/detach)
175 * the board, and also about the kernel module that contains
176 * the device code.
177 */
178static int bonding_attach(struct comedi_device *dev,
179			  struct comedi_devconfig *it);
180static int bonding_detach(struct comedi_device *dev);
181/** Build Private array of all devices.. */
182static int doDevConfig(struct comedi_device *dev, struct comedi_devconfig *it);
183static void doDevUnconfig(struct comedi_device *dev);
184/* Ugly implementation of realloc that always copies memory around -- I'm lazy,
185 * what can I say?  I like to do wasteful memcopies.. :) */
186static void *Realloc(const void *ptr, size_t len, size_t old_len);
187
188static struct comedi_driver driver_bonding = {
189	.driver_name = MODULE_NAME,
190	.module = THIS_MODULE,
191	.attach = bonding_attach,
192	.detach = bonding_detach,
193	/* It is not necessary to implement the following members if you are
194	 * writing a driver for a ISA PnP or PCI card */
195	/* Most drivers will support multiple types of boards by
196	 * having an array of board structures.  These were defined
197	 * in skel_boards[] above.  Note that the element 'name'
198	 * was first in the structure -- Comedi uses this fact to
199	 * extract the name of the board without knowing any details
200	 * about the structure except for its length.
201	 * When a device is attached (by comedi_config), the name
202	 * of the device is given to Comedi, and Comedi tries to
203	 * match it by going through the list of board names.  If
204	 * there is a match, the address of the pointer is put
205	 * into dev->board_ptr and driver->attach() is called.
206	 *
207	 * Note that these are not necessary if you can determine
208	 * the type of board in software.  ISA PnP, PCI, and PCMCIA
209	 * devices are such boards.
210	 */
211	.board_name = &bondingBoards[0].name,
212	.offset = sizeof(struct BondingBoard),
213	.num_names = ARRAY_SIZE(bondingBoards),
214};
215
216static int bonding_dio_insn_bits(struct comedi_device *dev,
217				 struct comedi_subdevice *s,
218				 struct comedi_insn *insn, unsigned int *data);
219static int bonding_dio_insn_config(struct comedi_device *dev,
220				   struct comedi_subdevice *s,
221				   struct comedi_insn *insn,
222				   unsigned int *data);
223
224/*
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
228 * address.
229 */
230static int bonding_attach(struct comedi_device *dev,
231			  struct comedi_devconfig *it)
232{
233	struct comedi_subdevice *s;
234
235	LOG_MSG("comedi%d\n", dev->minor);
236
237	/*
238	 * Allocate the private structure area.  alloc_private() is a
239	 * convenient macro defined in comedidev.h.
240	 */
241	if (alloc_private(dev, sizeof(struct Private)) < 0)
242		return -ENOMEM;
243
244	/*
245	 * Setup our bonding from config params.. sets up our Private struct..
246	 */
247	if (!doDevConfig(dev, it))
248		return -EINVAL;
249
250	/*
251	 * Initialize dev->board_name.  Note that we can use the "thisboard"
252	 * macro now, since we just initialized it in the last line.
253	 */
254	dev->board_name = devpriv->name;
255
256	/*
257	 * Allocate the subdevice structures.  alloc_subdevice() is a
258	 * convenient macro defined in comedidev.h.
259	 */
260	if (alloc_subdevices(dev, 1) < 0)
261		return -ENOMEM;
262
263	s = dev->subdevices + 0;
264	s->type = COMEDI_SUBD_DIO;
265	s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
266	s->n_chan = devpriv->nchans;
267	s->maxdata = 1;
268	s->range_table = &range_digital;
269	s->insn_bits = bonding_dio_insn_bits;
270	s->insn_config = bonding_dio_insn_config;
271
272	LOG_MSG("attached with %u DIO channels coming from %u different "
273		"subdevices all bonded together.  "
274		"John Lennon would be proud!\n",
275		devpriv->nchans, devpriv->ndevs);
276
277	return 1;
278}
279
280/*
281 * _detach is called to deconfigure a device.  It should deallocate
282 * resources.
283 * This function is also called when _attach() fails, so it should be
284 * careful not to release resources that were not necessarily
285 * allocated by _attach().  dev->private and dev->subdevices are
286 * deallocated automatically by the core.
287 */
288static int bonding_detach(struct comedi_device *dev)
289{
290	LOG_MSG("comedi%d: remove\n", dev->minor);
291	doDevUnconfig(dev);
292	return 0;
293}
294
295/* DIO devices are slightly special.  Although it is possible to
296 * implement the insn_read/insn_write interface, it is much more
297 * useful to applications if you implement the insn_bits interface.
298 * This allows packed reading/writing of the DIO channels.  The
299 * comedi core can convert between insn_bits and insn_read/write */
300static int bonding_dio_insn_bits(struct comedi_device *dev,
301				 struct comedi_subdevice *s,
302				 struct comedi_insn *insn, unsigned int *data)
303{
304#define LSAMPL_BITS (sizeof(unsigned int)*8)
305	unsigned nchans = LSAMPL_BITS, num_done = 0, i;
306	if (insn->n != 2)
307		return -EINVAL;
308
309	if (devpriv->nchans < nchans)
310		nchans = devpriv->nchans;
311
312	/* The insn data is a mask in data[0] and the new data
313	 * in data[1], each channel cooresponding to a bit. */
314	for (i = 0; num_done < nchans && i < devpriv->ndevs; ++i) {
315		struct BondedDevice *bdev = devpriv->devs[i];
316		/* Grab the channel mask and data of only the bits corresponding
317		   to this subdevice.. need to shift them to zero position of
318		   course. */
319		/* Bits corresponding to this subdev. */
320		unsigned int subdevMask = ((1 << bdev->nchans) - 1);
321		unsigned int writeMask, dataBits;
322
323		/* Argh, we have >= LSAMPL_BITS chans.. take all bits */
324		if (bdev->nchans >= LSAMPL_BITS)
325			subdevMask = (unsigned int)(-1);
326
327		writeMask = (data[0] >> num_done) & subdevMask;
328		dataBits = (data[1] >> num_done) & subdevMask;
329
330		/* Read/Write the new digital lines */
331		if (comedi_dio_bitfield(bdev->dev, bdev->subdev, writeMask,
332					&dataBits) != 2)
333			return -EINVAL;
334
335		/* Make room for the new bits in data[1], the return value */
336		data[1] &= ~(subdevMask << num_done);
337		/* Put the bits in the return value */
338		data[1] |= (dataBits & subdevMask) << num_done;
339		/* Save the new bits to the saved state.. */
340		s->state = data[1];
341
342		num_done += bdev->nchans;
343	}
344
345	return insn->n;
346}
347
348static int bonding_dio_insn_config(struct comedi_device *dev,
349				   struct comedi_subdevice *s,
350				   struct comedi_insn *insn, unsigned int *data)
351{
352	int chan = CR_CHAN(insn->chanspec), ret, io_bits = s->io_bits;
353	unsigned int io;
354	struct BondedDevice *bdev;
355
356	if (chan < 0 || chan >= devpriv->nchans)
357		return -EINVAL;
358	bdev = devpriv->chanIdDevMap[chan];
359
360	/* The input or output configuration of each digital line is
361	 * configured by a special insn_config instruction.  chanspec
362	 * contains the channel to be changed, and data[0] contains the
363	 * value COMEDI_INPUT or COMEDI_OUTPUT. */
364	switch (data[0]) {
365	case INSN_CONFIG_DIO_OUTPUT:
366		io = COMEDI_OUTPUT;	/* is this really necessary? */
367		io_bits |= 1 << chan;
368		break;
369	case INSN_CONFIG_DIO_INPUT:
370		io = COMEDI_INPUT;	/* is this really necessary? */
371		io_bits &= ~(1 << chan);
372		break;
373	case INSN_CONFIG_DIO_QUERY:
374		data[1] =
375		    (io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
376		return insn->n;
377		break;
378	default:
379		return -EINVAL;
380		break;
381	}
382	/* 'real' channel id for this subdev.. */
383	chan -= bdev->chanid_offset;
384	ret = comedi_dio_config(bdev->dev, bdev->subdev, chan, io);
385	if (ret != 1)
386		return -EINVAL;
387	/* Finally, save the new io_bits values since we didn't get
388	   an error above. */
389	s->io_bits = io_bits;
390	return insn->n;
391}
392
393static void *Realloc(const void *oldmem, size_t newlen, size_t oldlen)
394{
395	void *newmem = kmalloc(newlen, GFP_KERNEL);
396
397	if (newmem && oldmem)
398		memcpy(newmem, oldmem, min(oldlen, newlen));
399	kfree(oldmem);
400	return newmem;
401}
402
403static int doDevConfig(struct comedi_device *dev, struct comedi_devconfig *it)
404{
405	int i;
406	void *devs_opened[COMEDI_NUM_BOARD_MINORS];
407
408	memset(devs_opened, 0, sizeof(devs_opened));
409	devpriv->name[0] = 0;;
410	/* Loop through all comedi devices specified on the command-line,
411	   building our device list */
412	for (i = 0; i < COMEDI_NDEVCONFOPTS && (!i || it->options[i]); ++i) {
413		char file[] = "/dev/comediXXXXXX";
414		int minor = it->options[i];
415		void *d;
416		int sdev = -1, nchans, tmp;
417		struct BondedDevice *bdev = NULL;
418
419		if (minor < 0 || minor >= COMEDI_NUM_BOARD_MINORS) {
420			ERROR("Minor %d is invalid!\n", minor);
421			return 0;
422		}
423		if (minor == dev->minor) {
424			ERROR("Cannot bond this driver to itself!\n");
425			return 0;
426		}
427		if (devs_opened[minor]) {
428			ERROR("Minor %d specified more than once!\n", minor);
429			return 0;
430		}
431
432		snprintf(file, sizeof(file), "/dev/comedi%u", minor);
433		file[sizeof(file) - 1] = 0;
434
435		d = devs_opened[minor] = comedi_open(file);
436
437		if (!d) {
438			ERROR("Minor %u could not be opened\n", minor);
439			return 0;
440		}
441
442		/* Do DIO, as that's all we support now.. */
443		while ((sdev = comedi_find_subdevice_by_type(d, COMEDI_SUBD_DIO,
444							     sdev + 1)) > -1) {
445			nchans = comedi_get_n_channels(d, sdev);
446			if (nchans <= 0) {
447				ERROR("comedi_get_n_channels() returned %d "
448				      "on minor %u subdev %d!\n",
449				      nchans, minor, sdev);
450				return 0;
451			}
452			bdev = kmalloc(sizeof(*bdev), GFP_KERNEL);
453			if (!bdev) {
454				ERROR("Out of memory.\n");
455				return 0;
456			}
457			bdev->dev = d;
458			bdev->minor = minor;
459			bdev->subdev = sdev;
460			bdev->subdev_type = COMEDI_SUBD_DIO;
461			bdev->nchans = nchans;
462			bdev->chanid_offset = devpriv->nchans;
463
464			/* map channel id's to BondedDevice * pointer.. */
465			while (nchans--)
466				devpriv->chanIdDevMap[devpriv->nchans++] = bdev;
467
468			/* Now put bdev pointer at end of devpriv->devs array
469			 * list.. */
470
471			/* ergh.. ugly.. we need to realloc :(  */
472			tmp = devpriv->ndevs * sizeof(bdev);
473			devpriv->devs =
474			    Realloc(devpriv->devs,
475				    ++devpriv->ndevs * sizeof(bdev), tmp);
476			if (!devpriv->devs) {
477				ERROR("Could not allocate memory. "
478				      "Out of memory?");
479				return 0;
480			}
481
482			devpriv->devs[devpriv->ndevs - 1] = bdev;
483			{
484	/** Append dev:subdev to devpriv->name */
485				char buf[20];
486				int left =
487				    MAX_BOARD_NAME - strlen(devpriv->name) - 1;
488				snprintf(buf, sizeof(buf), "%d:%d ", dev->minor,
489					 bdev->subdev);
490				buf[sizeof(buf) - 1] = 0;
491				strncat(devpriv->name, buf, left);
492			}
493
494		}
495	}
496
497	if (!devpriv->nchans) {
498		ERROR("No channels found!\n");
499		return 0;
500	}
501
502	return 1;
503}
504
505static void doDevUnconfig(struct comedi_device *dev)
506{
507	unsigned long devs_closed = 0;
508
509	if (devpriv) {
510		while (devpriv->ndevs-- && devpriv->devs) {
511			struct BondedDevice *bdev;
512
513			bdev = devpriv->devs[devpriv->ndevs];
514			if (!bdev)
515				continue;
516			if (!(devs_closed & (0x1 << bdev->minor))) {
517				comedi_close(bdev->dev);
518				devs_closed |= (0x1 << bdev->minor);
519			}
520			kfree(bdev);
521		}
522		kfree(devpriv->devs);
523		devpriv->devs = NULL;
524		kfree(devpriv);
525		dev->private = NULL;
526	}
527}
528
529static int __init init(void)
530{
531	return comedi_driver_register(&driver_bonding);
532}
533
534static void __exit cleanup(void)
535{
536	comedi_driver_unregister(&driver_bonding);
537}
538
539module_init(init);
540module_exit(cleanup);
541