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