Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

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