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