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