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