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