staging: comedi: propogate error code from comedi_alloc_subdevices
[deliverable/linux.git] / drivers / staging / comedi / drivers / comedi_bond.c
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 #ifndef STR
64 # define STR1(x) #x
65 # define STR(x) STR1(x)
66 #endif
67
68 static int debug;
69 module_param(debug, int, 0644);
70 MODULE_PARM_DESC(debug, "If true, print extra cryptic debugging output useful"
71 "only to developers.");
72
73 #define LOG_MSG(x...) printk(KERN_INFO MODULE_NAME": "x)
74 #define DEBUG(x...) \
75 do { \
76 if (debug) \
77 printk(KERN_DEBUG MODULE_NAME": DEBUG: "x); \
78 } while (0)
79 #define WARNING(x...) printk(KERN_WARNING MODULE_NAME ": WARNING: "x)
80 #define ERROR(x...) printk(KERN_ERR MODULE_NAME ": INTERNAL ERROR: "x)
81
82 /*
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.
86 */
87 struct BondingBoard {
88 const char *name;
89 };
90
91 /*
92 * Useful for shorthand access to the particular board structure
93 */
94 #define thisboard ((const struct BondingBoard *)dev->board_ptr)
95
96 struct BondedDevice {
97 struct comedi_device *dev;
98 unsigned minor;
99 unsigned subdev;
100 unsigned subdev_type;
101 unsigned nchans;
102 unsigned chanid_offset; /* The offset into our unified linear
103 channel-id's of chanid 0 on this
104 subdevice. */
105 };
106
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. */
110 struct Private {
111 # define MAX_BOARD_NAME 256
112 char name[MAX_BOARD_NAME];
113 struct BondedDevice **devs;
114 unsigned ndevs;
115 struct BondedDevice *chanIdDevMap[MAX_CHANS];
116 unsigned nchans;
117 };
118
119 /*
120 * most drivers define the following macro to make it easy to
121 * access the private structure.
122 */
123 #define devpriv ((struct Private *)dev->private)
124
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)
133 {
134 #define LSAMPL_BITS (sizeof(unsigned int)*8)
135 unsigned nchans = LSAMPL_BITS, num_done = 0, i;
136 if (insn->n != 2)
137 return -EINVAL;
138
139 if (devpriv->nchans < nchans)
140 nchans = devpriv->nchans;
141
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
148 course. */
149 /* Bits corresponding to this subdev. */
150 unsigned int subdevMask = ((1 << bdev->nchans) - 1);
151 unsigned int writeMask, dataBits;
152
153 /* Argh, we have >= LSAMPL_BITS chans.. take all bits */
154 if (bdev->nchans >= LSAMPL_BITS)
155 subdevMask = (unsigned int)(-1);
156
157 writeMask = (data[0] >> num_done) & subdevMask;
158 dataBits = (data[1] >> num_done) & subdevMask;
159
160 /* Read/Write the new digital lines */
161 if (comedi_dio_bitfield(bdev->dev, bdev->subdev, writeMask,
162 &dataBits) != 2)
163 return -EINVAL;
164
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.. */
170 s->state = data[1];
171
172 num_done += bdev->nchans;
173 }
174
175 return insn->n;
176 }
177
178 static int bonding_dio_insn_config(struct comedi_device *dev,
179 struct comedi_subdevice *s,
180 struct comedi_insn *insn, unsigned int *data)
181 {
182 int chan = CR_CHAN(insn->chanspec), ret, io_bits = s->io_bits;
183 unsigned int io;
184 struct BondedDevice *bdev;
185
186 if (chan < 0 || chan >= devpriv->nchans)
187 return -EINVAL;
188 bdev = devpriv->chanIdDevMap[chan];
189
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. */
194 switch (data[0]) {
195 case INSN_CONFIG_DIO_OUTPUT:
196 io = COMEDI_OUTPUT; /* is this really necessary? */
197 io_bits |= 1 << chan;
198 break;
199 case INSN_CONFIG_DIO_INPUT:
200 io = COMEDI_INPUT; /* is this really necessary? */
201 io_bits &= ~(1 << chan);
202 break;
203 case INSN_CONFIG_DIO_QUERY:
204 data[1] =
205 (io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
206 return insn->n;
207 break;
208 default:
209 return -EINVAL;
210 break;
211 }
212 /* 'real' channel id for this subdev.. */
213 chan -= bdev->chanid_offset;
214 ret = comedi_dio_config(bdev->dev, bdev->subdev, chan, io);
215 if (ret != 1)
216 return -EINVAL;
217 /* Finally, save the new io_bits values since we didn't get
218 an error above. */
219 s->io_bits = io_bits;
220 return insn->n;
221 }
222
223 static void *Realloc(const void *oldmem, size_t newlen, size_t oldlen)
224 {
225 void *newmem = kmalloc(newlen, GFP_KERNEL);
226
227 if (newmem && oldmem)
228 memcpy(newmem, oldmem, min(oldlen, newlen));
229 kfree(oldmem);
230 return newmem;
231 }
232
233 static int doDevConfig(struct comedi_device *dev, struct comedi_devconfig *it)
234 {
235 int i;
236 struct comedi_device *devs_opened[COMEDI_NUM_BOARD_MINORS];
237
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;
248
249 if (minor < 0 || minor >= COMEDI_NUM_BOARD_MINORS) {
250 ERROR("Minor %d is invalid!\n", minor);
251 return 0;
252 }
253 if (minor == dev->minor) {
254 ERROR("Cannot bond this driver to itself!\n");
255 return 0;
256 }
257 if (devs_opened[minor]) {
258 ERROR("Minor %d specified more than once!\n", minor);
259 return 0;
260 }
261
262 snprintf(file, sizeof(file), "/dev/comedi%u", minor);
263 file[sizeof(file) - 1] = 0;
264
265 d = devs_opened[minor] = comedi_open(file);
266
267 if (!d) {
268 ERROR("Minor %u could not be opened\n", minor);
269 return 0;
270 }
271
272 /* Do DIO, as that's all we support now.. */
273 while ((sdev = comedi_find_subdevice_by_type(d, COMEDI_SUBD_DIO,
274 sdev + 1)) > -1) {
275 nchans = comedi_get_n_channels(d, sdev);
276 if (nchans <= 0) {
277 ERROR("comedi_get_n_channels() returned %d "
278 "on minor %u subdev %d!\n",
279 nchans, minor, sdev);
280 return 0;
281 }
282 bdev = kmalloc(sizeof(*bdev), GFP_KERNEL);
283 if (!bdev) {
284 ERROR("Out of memory.\n");
285 return 0;
286 }
287 bdev->dev = d;
288 bdev->minor = minor;
289 bdev->subdev = sdev;
290 bdev->subdev_type = COMEDI_SUBD_DIO;
291 bdev->nchans = nchans;
292 bdev->chanid_offset = devpriv->nchans;
293
294 /* map channel id's to BondedDevice * pointer.. */
295 while (nchans--)
296 devpriv->chanIdDevMap[devpriv->nchans++] = bdev;
297
298 /* Now put bdev pointer at end of devpriv->devs array
299 * list.. */
300
301 /* ergh.. ugly.. we need to realloc :( */
302 tmp = devpriv->ndevs * sizeof(bdev);
303 devpriv->devs =
304 Realloc(devpriv->devs,
305 ++devpriv->ndevs * sizeof(bdev), tmp);
306 if (!devpriv->devs) {
307 ERROR("Could not allocate memory. "
308 "Out of memory?");
309 return 0;
310 }
311
312 devpriv->devs[devpriv->ndevs - 1] = bdev;
313 {
314 /** Append dev:subdev to devpriv->name */
315 char buf[20];
316 int left =
317 MAX_BOARD_NAME - strlen(devpriv->name) - 1;
318 snprintf(buf, sizeof(buf), "%d:%d ", dev->minor,
319 bdev->subdev);
320 buf[sizeof(buf) - 1] = 0;
321 strncat(devpriv->name, buf, left);
322 }
323
324 }
325 }
326
327 if (!devpriv->nchans) {
328 ERROR("No channels found!\n");
329 return 0;
330 }
331
332 return 1;
333 }
334
335 static int bonding_attach(struct comedi_device *dev,
336 struct comedi_devconfig *it)
337 {
338 struct comedi_subdevice *s;
339 int ret;
340
341 LOG_MSG("comedi%d\n", dev->minor);
342
343 /*
344 * Allocate the private structure area. alloc_private() is a
345 * convenient macro defined in comedidev.h.
346 */
347 if (alloc_private(dev, sizeof(struct Private)) < 0)
348 return -ENOMEM;
349
350 /*
351 * Setup our bonding from config params.. sets up our Private struct..
352 */
353 if (!doDevConfig(dev, it))
354 return -EINVAL;
355
356 /*
357 * Initialize dev->board_name. Note that we can use the "thisboard"
358 * macro now, since we just initialized it in the last line.
359 */
360 dev->board_name = devpriv->name;
361
362 ret = comedi_alloc_subdevices(dev, 1);
363 if (ret)
364 return ret;
365
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;
370 s->maxdata = 1;
371 s->range_table = &range_digital;
372 s->insn_bits = bonding_dio_insn_bits;
373 s->insn_config = bonding_dio_insn_config;
374
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);
379
380 return 1;
381 }
382
383 static void bonding_detach(struct comedi_device *dev)
384 {
385 unsigned long devs_closed = 0;
386
387 if (devpriv) {
388 while (devpriv->ndevs-- && devpriv->devs) {
389 struct BondedDevice *bdev;
390
391 bdev = devpriv->devs[devpriv->ndevs];
392 if (!bdev)
393 continue;
394 if (!(devs_closed & (0x1 << bdev->minor))) {
395 comedi_close(bdev->dev);
396 devs_closed |= (0x1 << bdev->minor);
397 }
398 kfree(bdev);
399 }
400 kfree(devpriv->devs);
401 devpriv->devs = NULL;
402 kfree(devpriv);
403 dev->private = NULL;
404 }
405 }
406
407 static const struct BondingBoard bondingBoards[] = {
408 {
409 .name = "comedi_bond",
410 },
411 };
412
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),
421 };
422 module_comedi_driver(bonding_driver);
423
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");
This page took 0.04136 seconds and 5 git commands to generate.