2 comedi/drivers/comedi_bond.c
3 A Comedi driver to 'bond' or merge multiple drivers and devices as one.
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>
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.
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.
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.
26 Description: A driver to 'bond' (merge) multiple subdevices from multiple
27 devices together as one.
30 Updated: Mon, 10 Oct 00:18:25 -0500
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.
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!
46 Commands aren't supported -- although it would be cool if they were.
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.
53 #include <linux/string.h>
54 #include <linux/slab.h>
55 #include "../comedi.h"
56 #include "../comedilib.h"
57 #include "../comedidev.h"
59 /* The maxiumum number of channels per subdevice. */
62 #define MODULE_NAME "comedi_bond"
65 # define STR(x) STR1(x)
69 module_param(debug
, int, 0644);
70 MODULE_PARM_DESC(debug
, "If true, print extra cryptic debugging output useful"
71 "only to developers.");
73 #define LOG_MSG(x...) printk(KERN_INFO MODULE_NAME": "x)
77 printk(KERN_DEBUG MODULE_NAME": DEBUG: "x); \
79 #define WARNING(x...) printk(KERN_WARNING MODULE_NAME ": WARNING: "x)
80 #define ERROR(x...) printk(KERN_ERR MODULE_NAME ": INTERNAL ERROR: "x)
83 * Board descriptions for two imaginary boards. Describing the
84 * boards in this way is optional, and completely driver-dependent.
85 * Some drivers use arrays such as this, other do not.
92 * Useful for shorthand access to the particular board structure
94 #define thisboard ((const struct BondingBoard *)dev->board_ptr)
97 struct comedi_device
*dev
;
100 unsigned subdev_type
;
102 unsigned chanid_offset
; /* The offset into our unified linear
103 channel-id's of chanid 0 on this
107 /* this structure is for data unique to this hardware driver. If
108 several hardware drivers keep similar information in this structure,
109 feel free to suggest moving the variable to the struct comedi_device struct. */
111 # define MAX_BOARD_NAME 256
112 char name
[MAX_BOARD_NAME
];
113 struct BondedDevice
**devs
;
115 struct BondedDevice
*chanIdDevMap
[MAX_CHANS
];
120 * most drivers define the following macro to make it easy to
121 * access the private structure.
123 #define devpriv ((struct Private *)dev->private)
125 /* DIO devices are slightly special. Although it is possible to
126 * implement the insn_read/insn_write interface, it is much more
127 * useful to applications if you implement the insn_bits interface.
128 * This allows packed reading/writing of the DIO channels. The
129 * comedi core can convert between insn_bits and insn_read/write */
130 static int bonding_dio_insn_bits(struct comedi_device
*dev
,
131 struct comedi_subdevice
*s
,
132 struct comedi_insn
*insn
, unsigned int *data
)
134 #define LSAMPL_BITS (sizeof(unsigned int)*8)
135 unsigned nchans
= LSAMPL_BITS
, num_done
= 0, i
;
139 if (devpriv
->nchans
< nchans
)
140 nchans
= devpriv
->nchans
;
142 /* The insn data is a mask in data[0] and the new data
143 * in data[1], each channel cooresponding to a bit. */
144 for (i
= 0; num_done
< nchans
&& i
< devpriv
->ndevs
; ++i
) {
145 struct BondedDevice
*bdev
= devpriv
->devs
[i
];
146 /* Grab the channel mask and data of only the bits corresponding
147 to this subdevice.. need to shift them to zero position of
149 /* Bits corresponding to this subdev. */
150 unsigned int subdevMask
= ((1 << bdev
->nchans
) - 1);
151 unsigned int writeMask
, dataBits
;
153 /* Argh, we have >= LSAMPL_BITS chans.. take all bits */
154 if (bdev
->nchans
>= LSAMPL_BITS
)
155 subdevMask
= (unsigned int)(-1);
157 writeMask
= (data
[0] >> num_done
) & subdevMask
;
158 dataBits
= (data
[1] >> num_done
) & subdevMask
;
160 /* Read/Write the new digital lines */
161 if (comedi_dio_bitfield(bdev
->dev
, bdev
->subdev
, writeMask
,
165 /* Make room for the new bits in data[1], the return value */
166 data
[1] &= ~(subdevMask
<< num_done
);
167 /* Put the bits in the return value */
168 data
[1] |= (dataBits
& subdevMask
) << num_done
;
169 /* Save the new bits to the saved state.. */
172 num_done
+= bdev
->nchans
;
178 static int bonding_dio_insn_config(struct comedi_device
*dev
,
179 struct comedi_subdevice
*s
,
180 struct comedi_insn
*insn
, unsigned int *data
)
182 int chan
= CR_CHAN(insn
->chanspec
), ret
, io_bits
= s
->io_bits
;
184 struct BondedDevice
*bdev
;
186 if (chan
< 0 || chan
>= devpriv
->nchans
)
188 bdev
= devpriv
->chanIdDevMap
[chan
];
190 /* The input or output configuration of each digital line is
191 * configured by a special insn_config instruction. chanspec
192 * contains the channel to be changed, and data[0] contains the
193 * value COMEDI_INPUT or COMEDI_OUTPUT. */
195 case INSN_CONFIG_DIO_OUTPUT
:
196 io
= COMEDI_OUTPUT
; /* is this really necessary? */
197 io_bits
|= 1 << chan
;
199 case INSN_CONFIG_DIO_INPUT
:
200 io
= COMEDI_INPUT
; /* is this really necessary? */
201 io_bits
&= ~(1 << chan
);
203 case INSN_CONFIG_DIO_QUERY
:
205 (io_bits
& (1 << chan
)) ? COMEDI_OUTPUT
: COMEDI_INPUT
;
212 /* 'real' channel id for this subdev.. */
213 chan
-= bdev
->chanid_offset
;
214 ret
= comedi_dio_config(bdev
->dev
, bdev
->subdev
, chan
, io
);
217 /* Finally, save the new io_bits values since we didn't get
219 s
->io_bits
= io_bits
;
223 static void *Realloc(const void *oldmem
, size_t newlen
, size_t oldlen
)
225 void *newmem
= kmalloc(newlen
, GFP_KERNEL
);
227 if (newmem
&& oldmem
)
228 memcpy(newmem
, oldmem
, min(oldlen
, newlen
));
233 static int doDevConfig(struct comedi_device
*dev
, struct comedi_devconfig
*it
)
236 struct comedi_device
*devs_opened
[COMEDI_NUM_BOARD_MINORS
];
238 memset(devs_opened
, 0, sizeof(devs_opened
));
239 devpriv
->name
[0] = 0;
240 /* Loop through all comedi devices specified on the command-line,
241 building our device list */
242 for (i
= 0; i
< COMEDI_NDEVCONFOPTS
&& (!i
|| it
->options
[i
]); ++i
) {
243 char file
[] = "/dev/comediXXXXXX";
244 int minor
= it
->options
[i
];
245 struct comedi_device
*d
;
246 int sdev
= -1, nchans
, tmp
;
247 struct BondedDevice
*bdev
= NULL
;
249 if (minor
< 0 || minor
>= COMEDI_NUM_BOARD_MINORS
) {
250 ERROR("Minor %d is invalid!\n", minor
);
253 if (minor
== dev
->minor
) {
254 ERROR("Cannot bond this driver to itself!\n");
257 if (devs_opened
[minor
]) {
258 ERROR("Minor %d specified more than once!\n", minor
);
262 snprintf(file
, sizeof(file
), "/dev/comedi%u", minor
);
263 file
[sizeof(file
) - 1] = 0;
265 d
= devs_opened
[minor
] = comedi_open(file
);
268 ERROR("Minor %u could not be opened\n", minor
);
272 /* Do DIO, as that's all we support now.. */
273 while ((sdev
= comedi_find_subdevice_by_type(d
, COMEDI_SUBD_DIO
,
275 nchans
= comedi_get_n_channels(d
, sdev
);
277 ERROR("comedi_get_n_channels() returned %d "
278 "on minor %u subdev %d!\n",
279 nchans
, minor
, sdev
);
282 bdev
= kmalloc(sizeof(*bdev
), GFP_KERNEL
);
284 ERROR("Out of memory.\n");
290 bdev
->subdev_type
= COMEDI_SUBD_DIO
;
291 bdev
->nchans
= nchans
;
292 bdev
->chanid_offset
= devpriv
->nchans
;
294 /* map channel id's to BondedDevice * pointer.. */
296 devpriv
->chanIdDevMap
[devpriv
->nchans
++] = bdev
;
298 /* Now put bdev pointer at end of devpriv->devs array
301 /* ergh.. ugly.. we need to realloc :( */
302 tmp
= devpriv
->ndevs
* sizeof(bdev
);
304 Realloc(devpriv
->devs
,
305 ++devpriv
->ndevs
* sizeof(bdev
), tmp
);
306 if (!devpriv
->devs
) {
307 ERROR("Could not allocate memory. "
312 devpriv
->devs
[devpriv
->ndevs
- 1] = bdev
;
314 /** Append dev:subdev to devpriv->name */
317 MAX_BOARD_NAME
- strlen(devpriv
->name
) - 1;
318 snprintf(buf
, sizeof(buf
), "%d:%d ", dev
->minor
,
320 buf
[sizeof(buf
) - 1] = 0;
321 strncat(devpriv
->name
, buf
, left
);
327 if (!devpriv
->nchans
) {
328 ERROR("No channels found!\n");
335 static int bonding_attach(struct comedi_device
*dev
,
336 struct comedi_devconfig
*it
)
338 struct comedi_subdevice
*s
;
341 LOG_MSG("comedi%d\n", dev
->minor
);
344 * Allocate the private structure area. alloc_private() is a
345 * convenient macro defined in comedidev.h.
347 if (alloc_private(dev
, sizeof(struct Private
)) < 0)
351 * Setup our bonding from config params.. sets up our Private struct..
353 if (!doDevConfig(dev
, it
))
357 * Initialize dev->board_name. Note that we can use the "thisboard"
358 * macro now, since we just initialized it in the last line.
360 dev
->board_name
= devpriv
->name
;
362 ret
= comedi_alloc_subdevices(dev
, 1);
366 s
= dev
->subdevices
+ 0;
367 s
->type
= COMEDI_SUBD_DIO
;
368 s
->subdev_flags
= SDF_READABLE
| SDF_WRITABLE
;
369 s
->n_chan
= devpriv
->nchans
;
371 s
->range_table
= &range_digital
;
372 s
->insn_bits
= bonding_dio_insn_bits
;
373 s
->insn_config
= bonding_dio_insn_config
;
375 LOG_MSG("attached with %u DIO channels coming from %u different "
376 "subdevices all bonded together. "
377 "John Lennon would be proud!\n",
378 devpriv
->nchans
, devpriv
->ndevs
);
383 static void bonding_detach(struct comedi_device
*dev
)
385 unsigned long devs_closed
= 0;
388 while (devpriv
->ndevs
-- && devpriv
->devs
) {
389 struct BondedDevice
*bdev
;
391 bdev
= devpriv
->devs
[devpriv
->ndevs
];
394 if (!(devs_closed
& (0x1 << bdev
->minor
))) {
395 comedi_close(bdev
->dev
);
396 devs_closed
|= (0x1 << bdev
->minor
);
400 kfree(devpriv
->devs
);
401 devpriv
->devs
= NULL
;
407 static const struct BondingBoard bondingBoards
[] = {
409 .name
= "comedi_bond",
413 static struct comedi_driver bonding_driver
= {
414 .driver_name
= "comedi_bond",
415 .module
= THIS_MODULE
,
416 .attach
= bonding_attach
,
417 .detach
= bonding_detach
,
418 .board_name
= &bondingBoards
[0].name
,
419 .offset
= sizeof(struct BondingBoard
),
420 .num_names
= ARRAY_SIZE(bondingBoards
),
422 module_comedi_driver(bonding_driver
);
424 MODULE_AUTHOR("Calin A. Culianu");
425 MODULE_DESCRIPTION(MODULE_NAME
"A driver for COMEDI to bond multiple COMEDI "
426 "devices together as one. In the words of John Lennon: "
427 "'And the world will live as one...'");
428 MODULE_LICENSE("GPL");