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