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 /*
25 Driver: comedi_bond
26 Description: A driver to 'bond' (merge) multiple subdevices from multiple
27 	     devices together as one.
28 Devices:
29 Author: ds
30 Updated: Mon, 10 Oct 00:18:25 -0500
31 Status: works
32 
33 This driver allows you to 'bond' (merge) multiple comedi subdevices
34 (coming from possibly difference boards and/or drivers) together.  For
35 example, if you had a board with 2 different DIO subdevices, and
36 another with 1 DIO subdevice, you could 'bond' them with this driver
37 so that they look like one big fat DIO subdevice.  This makes writing
38 applications slightly easier as you don't have to worry about managing
39 different subdevices in the application -- you just worry about
40 indexing one linear array of channel id's.
41 
42 Right now only DIO subdevices are supported as that's the personal itch
43 I am scratching with this driver.  If you want to add support for AI and AO
44 subdevs, go right on ahead and do so!
45 
46 Commands aren't supported -- although it would be cool if they were.
47 
48 Configuration 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"
63 MODULE_LICENSE("GPL");
64 #ifndef STR
65 #  define STR1(x) #x
66 #  define STR(x) STR1(x)
67 #endif
68 
69 static int debug;
70 module_param(debug, int, 0644);
71 MODULE_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)
82 MODULE_AUTHOR("Calin A. Culianu");
83 MODULE_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  */
92 struct BondingBoard {
93 	const char *name;
94 };
95 
96 static 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 
107 struct 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.  */
121 struct 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  */
142 static int bonding_attach(struct comedi_device *dev,
143 			  struct comedi_devconfig *it);
144 static int bonding_detach(struct comedi_device *dev);
145 /** Build Private array of all devices.. */
146 static int doDevConfig(struct comedi_device *dev, struct comedi_devconfig *it);
147 static 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.. :) */
150 static void *Realloc(const void *ptr, size_t len, size_t old_len);
151 
152 static 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 
180 static int bonding_dio_insn_bits(struct comedi_device *dev,
181 				 struct comedi_subdevice *s,
182 				 struct comedi_insn *insn, unsigned int *data);
183 static 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  */
bonding_attach(struct comedi_device * dev,struct comedi_devconfig * it)194 static 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  */
bonding_detach(struct comedi_device * dev)252 static 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 */
bonding_dio_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)264 static 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 
bonding_dio_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)312 static 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 
Realloc(const void * oldmem,size_t newlen,size_t oldlen)357 static 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 
doDevConfig(struct comedi_device * dev,struct comedi_devconfig * it)367 static 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 
doDevUnconfig(struct comedi_device * dev)469 static 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 
init(void)493 static int __init init(void)
494 {
495 	return comedi_driver_register(&driver_bonding);
496 }
497 
cleanup(void)498 static void __exit cleanup(void)
499 {
500 	comedi_driver_unregister(&driver_bonding);
501 }
502 
503 module_init(init);
504 module_exit(cleanup);
505