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