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