comedi_bond.c revision 139dfbdfacb02e3ef3df936d2fabd1ad5f14ea88
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, 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, 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 = sizeof(bondingBoards) / sizeof(struct BondingBoard), 214}; 215 216static int bonding_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, 217 comedi_insn *insn, unsigned int *data); 218static int bonding_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, 219 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 */ 227static int bonding_attach(struct comedi_device *dev, 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 */ 284static 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 */ 296static int bonding_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, 297 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 343static int bonding_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, 344 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 387static 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 397static int doDevConfig(struct comedi_device *dev, 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 500static 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 524static int __init init(void) 525{ 526 return comedi_driver_register(&driver_bonding); 527} 528 529static void __exit cleanup(void) 530{ 531 comedi_driver_unregister(&driver_bonding); 532} 533 534module_init(init); 535module_exit(cleanup); 536