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