blob: cfcbd9b8f39376bc9de300a86104c198db2a4d2c [file] [log] [blame]
David Schleef3cf840f2008-11-14 15:00:43 -08001/*
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
Greg Kroah-Hartmane7f2aa32008-11-19 09:58:28 -080026Description: A driver to 'bond' (merge) multiple subdevices from multiple
27 devices together as one.
David Schleef3cf840f2008-11-14 15:00:43 -080028Devices:
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
Greg Kroah-Hartman3b6b25b2010-05-03 15:50:09 -070053#include <linux/string.h>
54#include <linux/slab.h>
Greg Kroah-Hartmane2a0eab2010-05-01 13:44:56 -070055#include "../comedi.h"
David Schleef3cf840f2008-11-14 15:00:43 -080056#include "../comedilib.h"
57#include "../comedidev.h"
David Schleef3cf840f2008-11-14 15:00:43 -080058
59/* The maxiumum number of channels per subdevice. */
60#define MAX_CHANS 256
61
62#define MODULE_NAME "comedi_bond"
David Schleef3cf840f2008-11-14 15:00:43 -080063MODULE_LICENSE("GPL");
David Schleef3cf840f2008-11-14 15:00:43 -080064#ifndef STR
65# define STR1(x) #x
66# define STR(x) STR1(x)
67#endif
68
Greg Kroah-Hartman246c5412008-11-19 10:08:14 -080069static int debug;
David Schleef3cf840f2008-11-14 15:00:43 -080070module_param(debug, int, 0644);
Greg Kroah-Hartmane7f2aa32008-11-19 09:58:28 -080071MODULE_PARM_DESC(debug, "If true, print extra cryptic debugging output useful"
72 "only to developers.");
David Schleef3cf840f2008-11-14 15:00:43 -080073
74#define LOG_MSG(x...) printk(KERN_INFO MODULE_NAME": "x)
Greg Kroah-Hartmane7f2aa32008-11-19 09:58:28 -080075#define DEBUG(x...) \
76 do { \
77 if (debug) \
78 printk(KERN_DEBUG MODULE_NAME": DEBUG: "x); \
79 } while (0)
David Schleef3cf840f2008-11-14 15:00:43 -080080#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");
Greg Kroah-Hartmane7f2aa32008-11-19 09:58:28 -080083MODULE_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...'");
David Schleef3cf840f2008-11-14 15:00:43 -080086
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};
David Schleef3cf840f2008-11-14 15:00:43 -080095
Greg Kroah-Hartmanff534762008-11-19 10:04:21 -080096static const struct BondingBoard bondingBoards[] = {
David Schleef3cf840f2008-11-14 15:00:43 -080097 {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +053098 .name = MODULE_NAME,
99 },
David Schleef3cf840f2008-11-14 15:00:43 -0800100};
101
102/*
103 * Useful for shorthand access to the particular board structure
104 */
Greg Kroah-Hartmanff534762008-11-19 10:04:21 -0800105#define thisboard ((const struct BondingBoard *)dev->board_ptr)
David Schleef3cf840f2008-11-14 15:00:43 -0800106
107struct BondedDevice {
Greg Kroah-Hartman472dfe72010-05-03 15:01:50 -0700108 struct comedi_device *dev;
David Schleef3cf840f2008-11-14 15:00:43 -0800109 unsigned minor;
110 unsigned subdev;
111 unsigned subdev_type;
112 unsigned nchans;
Greg Kroah-Hartmane7f2aa32008-11-19 09:58:28 -0800113 unsigned chanid_offset; /* The offset into our unified linear
114 channel-id's of chanid 0 on this
115 subdevice. */
David Schleef3cf840f2008-11-14 15:00:43 -0800116};
David Schleef3cf840f2008-11-14 15:00:43 -0800117
118/* this structure is for data unique to this hardware driver. If
119 several hardware drivers keep similar information in this structure,
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400120 feel free to suggest moving the variable to the struct comedi_device struct. */
David Schleef3cf840f2008-11-14 15:00:43 -0800121struct 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};
David Schleef3cf840f2008-11-14 15:00:43 -0800129
130/*
131 * most drivers define the following macro to make it easy to
132 * access the private structure.
133 */
Greg Kroah-Hartmanff534762008-11-19 10:04:21 -0800134#define devpriv ((struct Private *)dev->private)
David Schleef3cf840f2008-11-14 15:00:43 -0800135
136/*
Bill Pemberton139dfbd2009-03-16 22:05:25 -0400137 * The struct comedi_driver structure tells the Comedi core module
David Schleef3cf840f2008-11-14 15:00:43 -0800138 * 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 */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530142static int bonding_attach(struct comedi_device *dev,
143 struct comedi_devconfig *it);
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400144static int bonding_detach(struct comedi_device *dev);
David Schleef3cf840f2008-11-14 15:00:43 -0800145/** Build Private array of all devices.. */
Bill Pemberton0707bb02009-03-16 22:06:20 -0400146static int doDevConfig(struct comedi_device *dev, struct comedi_devconfig *it);
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400147static void doDevUnconfig(struct comedi_device *dev);
Greg Kroah-Hartmane7f2aa32008-11-19 09:58:28 -0800148/* Ugly implementation of realloc that always copies memory around -- I'm lazy,
149 * what can I say? I like to do wasteful memcopies.. :) */
David Schleef3cf840f2008-11-14 15:00:43 -0800150static void *Realloc(const void *ptr, size_t len, size_t old_len);
151
Bill Pemberton139dfbd2009-03-16 22:05:25 -0400152static struct comedi_driver driver_bonding = {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530153 .driver_name = MODULE_NAME,
154 .module = THIS_MODULE,
155 .attach = bonding_attach,
156 .detach = bonding_detach,
David Schleef3cf840f2008-11-14 15:00:43 -0800157 /* 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 */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530175 .board_name = &bondingBoards[0].name,
176 .offset = sizeof(struct BondingBoard),
177 .num_names = ARRAY_SIZE(bondingBoards),
David Schleef3cf840f2008-11-14 15:00:43 -0800178};
179
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530180static int bonding_dio_insn_bits(struct comedi_device *dev,
181 struct comedi_subdevice *s,
Bill Pemberton90035c02009-03-16 22:05:53 -0400182 struct comedi_insn *insn, unsigned int *data);
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530183static int bonding_dio_insn_config(struct comedi_device *dev,
184 struct comedi_subdevice *s,
185 struct comedi_insn *insn,
186 unsigned int *data);
David Schleef3cf840f2008-11-14 15:00:43 -0800187
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 */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530194static int bonding_attach(struct comedi_device *dev,
195 struct comedi_devconfig *it)
David Schleef3cf840f2008-11-14 15:00:43 -0800196{
Bill Pemberton34c43922009-03-16 22:05:14 -0400197 struct comedi_subdevice *s;
David Schleef3cf840f2008-11-14 15:00:43 -0800198
199 LOG_MSG("comedi%d\n", dev->minor);
200
Greg Kroah-Hartmane7f2aa32008-11-19 09:58:28 -0800201 /*
202 * Allocate the private structure area. alloc_private() is a
203 * convenient macro defined in comedidev.h.
204 */
Greg Kroah-Hartmanff534762008-11-19 10:04:21 -0800205 if (alloc_private(dev, sizeof(struct Private)) < 0)
David Schleef3cf840f2008-11-14 15:00:43 -0800206 return -ENOMEM;
207
Greg Kroah-Hartmane7f2aa32008-11-19 09:58:28 -0800208 /*
209 * Setup our bonding from config params.. sets up our Private struct..
210 */
David Schleef3cf840f2008-11-14 15:00:43 -0800211 if (!doDevConfig(dev, it))
212 return -EINVAL;
213
Greg Kroah-Hartmane7f2aa32008-11-19 09:58:28 -0800214 /*
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 */
David Schleef3cf840f2008-11-14 15:00:43 -0800218 dev->board_name = devpriv->name;
219
Greg Kroah-Hartmane7f2aa32008-11-19 09:58:28 -0800220 /*
221 * Allocate the subdevice structures. alloc_subdevice() is a
222 * convenient macro defined in comedidev.h.
223 */
David Schleef3cf840f2008-11-14 15:00:43 -0800224 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
Greg Kroah-Hartmane7f2aa32008-11-19 09:58:28 -0800236 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);
David Schleef3cf840f2008-11-14 15:00:43 -0800240
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 */
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400252static int bonding_detach(struct comedi_device *dev)
David Schleef3cf840f2008-11-14 15:00:43 -0800253{
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 */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530264static int bonding_dio_insn_bits(struct comedi_device *dev,
265 struct comedi_subdevice *s,
Bill Pemberton90035c02009-03-16 22:05:53 -0400266 struct comedi_insn *insn, unsigned int *data)
David Schleef3cf840f2008-11-14 15:00:43 -0800267{
Bill Pemberton790c5542009-03-16 22:05:02 -0400268#define LSAMPL_BITS (sizeof(unsigned int)*8)
David Schleef3cf840f2008-11-14 15:00:43 -0800269 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) {
Greg Kroah-Hartmanff534762008-11-19 10:04:21 -0800279 struct BondedDevice *bdev = devpriv->devs[i];
David Schleef3cf840f2008-11-14 15:00:43 -0800280 /* 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. */
Greg Kroah-Hartmane7f2aa32008-11-19 09:58:28 -0800283 /* Bits corresponding to this subdev. */
Bill Pemberton790c5542009-03-16 22:05:02 -0400284 unsigned int subdevMask = ((1 << bdev->nchans) - 1);
285 unsigned int writeMask, dataBits;
David Schleef3cf840f2008-11-14 15:00:43 -0800286
287 /* Argh, we have >= LSAMPL_BITS chans.. take all bits */
288 if (bdev->nchans >= LSAMPL_BITS)
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530289 subdevMask = (unsigned int)(-1);
David Schleef3cf840f2008-11-14 15:00:43 -0800290
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,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530296 &dataBits) != 2)
David Schleef3cf840f2008-11-14 15:00:43 -0800297 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
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530312static int bonding_dio_insn_config(struct comedi_device *dev,
313 struct comedi_subdevice *s,
Bill Pemberton90035c02009-03-16 22:05:53 -0400314 struct comedi_insn *insn, unsigned int *data)
David Schleef3cf840f2008-11-14 15:00:43 -0800315{
316 int chan = CR_CHAN(insn->chanspec), ret, io_bits = s->io_bits;
317 unsigned int io;
Greg Kroah-Hartmanff534762008-11-19 10:04:21 -0800318 struct BondedDevice *bdev;
David Schleef3cf840f2008-11-14 15:00:43 -0800319
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] =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530339 (io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
David Schleef3cf840f2008-11-14 15:00:43 -0800340 return insn->n;
341 break;
342 default:
343 return -EINVAL;
344 break;
345 }
Greg Kroah-Hartmane7f2aa32008-11-19 09:58:28 -0800346 /* 'real' channel id for this subdev.. */
347 chan -= bdev->chanid_offset;
David Schleef3cf840f2008-11-14 15:00:43 -0800348 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{
David Schleef3cf840f2008-11-14 15:00:43 -0800359 void *newmem = kmalloc(newlen, GFP_KERNEL);
Greg Kroah-Hartmane7f2aa32008-11-19 09:58:28 -0800360
David Schleef3cf840f2008-11-14 15:00:43 -0800361 if (newmem && oldmem)
Greg Kroah-Hartmane7f2aa32008-11-19 09:58:28 -0800362 memcpy(newmem, oldmem, min(oldlen, newlen));
363 kfree(oldmem);
David Schleef3cf840f2008-11-14 15:00:43 -0800364 return newmem;
365}
366
Bill Pemberton0707bb02009-03-16 22:06:20 -0400367static int doDevConfig(struct comedi_device *dev, struct comedi_devconfig *it)
David Schleef3cf840f2008-11-14 15:00:43 -0800368{
369 int i;
Greg Kroah-Hartman472dfe72010-05-03 15:01:50 -0700370 struct comedi_device *devs_opened[COMEDI_NUM_BOARD_MINORS];
David Schleef3cf840f2008-11-14 15:00:43 -0800371
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];
Greg Kroah-Hartman472dfe72010-05-03 15:01:50 -0700379 struct comedi_device *d;
David Schleef3cf840f2008-11-14 15:00:43 -0800380 int sdev = -1, nchans, tmp;
Greg Kroah-Hartman246c5412008-11-19 10:08:14 -0800381 struct BondedDevice *bdev = NULL;
David Schleef3cf840f2008-11-14 15:00:43 -0800382
Dan Carpenter5d3aed72009-12-28 18:57:51 +0200383 if (minor < 0 || minor >= COMEDI_NUM_BOARD_MINORS) {
David Schleef3cf840f2008-11-14 15:00:43 -0800384 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,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530408 sdev + 1)) > -1) {
Greg Kroah-Hartmane7f2aa32008-11-19 09:58:28 -0800409 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);
David Schleef3cf840f2008-11-14 15:00:43 -0800414 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
Greg Kroah-Hartmane7f2aa32008-11-19 09:58:28 -0800432 /* Now put bdev pointer at end of devpriv->devs array
433 * list.. */
David Schleef3cf840f2008-11-14 15:00:43 -0800434
435 /* ergh.. ugly.. we need to realloc :( */
436 tmp = devpriv->ndevs * sizeof(bdev);
437 devpriv->devs =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530438 Realloc(devpriv->devs,
439 ++devpriv->ndevs * sizeof(bdev), tmp);
David Schleef3cf840f2008-11-14 15:00:43 -0800440 if (!devpriv->devs) {
Greg Kroah-Hartmane7f2aa32008-11-19 09:58:28 -0800441 ERROR("Could not allocate memory. "
442 "Out of memory?");
David Schleef3cf840f2008-11-14 15:00:43 -0800443 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 =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530451 MAX_BOARD_NAME - strlen(devpriv->name) - 1;
David Schleef3cf840f2008-11-14 15:00:43 -0800452 snprintf(buf, sizeof(buf), "%d:%d ", dev->minor,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530453 bdev->subdev);
David Schleef3cf840f2008-11-14 15:00:43 -0800454 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
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400469static void doDevUnconfig(struct comedi_device *dev)
David Schleef3cf840f2008-11-14 15:00:43 -0800470{
471 unsigned long devs_closed = 0;
472
473 if (devpriv) {
474 while (devpriv->ndevs-- && devpriv->devs) {
Greg Kroah-Hartmanff534762008-11-19 10:04:21 -0800475 struct BondedDevice *bdev;
476
477 bdev = devpriv->devs[devpriv->ndevs];
David Schleef3cf840f2008-11-14 15:00:43 -0800478 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 }
Greg Kroah-Hartmane7f2aa32008-11-19 09:58:28 -0800486 kfree(devpriv->devs);
Greg Kroah-Hartman246c5412008-11-19 10:08:14 -0800487 devpriv->devs = NULL;
David Schleef3cf840f2008-11-14 15:00:43 -0800488 kfree(devpriv);
Greg Kroah-Hartman246c5412008-11-19 10:08:14 -0800489 dev->private = NULL;
David Schleef3cf840f2008-11-14 15:00:43 -0800490 }
491}
492
Greg Kroah-Hartman246c5412008-11-19 10:08:14 -0800493static int __init init(void)
David Schleef3cf840f2008-11-14 15:00:43 -0800494{
495 return comedi_driver_register(&driver_bonding);
496}
497
Greg Kroah-Hartman246c5412008-11-19 10:08:14 -0800498static void __exit cleanup(void)
David Schleef3cf840f2008-11-14 15:00:43 -0800499{
500 comedi_driver_unregister(&driver_bonding);
501}
502
503module_init(init);
504module_exit(cleanup);