drivers.c revision 588ba6dc5fb4bdca47a3da38c2718fbb82d3eee1
1/* 2 module/drivers.c 3 functions for manipulating drivers 4 5 COMEDI - Linux Control and Measurement Device Interface 6 Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org> 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17*/ 18 19#include <linux/device.h> 20#include <linux/module.h> 21#include <linux/errno.h> 22#include <linux/kconfig.h> 23#include <linux/kernel.h> 24#include <linux/sched.h> 25#include <linux/fcntl.h> 26#include <linux/delay.h> 27#include <linux/ioport.h> 28#include <linux/mm.h> 29#include <linux/slab.h> 30#include <linux/highmem.h> /* for SuSE brokenness */ 31#include <linux/vmalloc.h> 32#include <linux/cdev.h> 33#include <linux/dma-mapping.h> 34#include <linux/io.h> 35#include <linux/interrupt.h> 36#include <linux/firmware.h> 37 38#include "comedidev.h" 39#include "comedi_internal.h" 40 41struct comedi_driver *comedi_drivers; 42 43int comedi_set_hw_dev(struct comedi_device *dev, struct device *hw_dev) 44{ 45 if (hw_dev == dev->hw_dev) 46 return 0; 47 if (dev->hw_dev != NULL) 48 return -EEXIST; 49 dev->hw_dev = get_device(hw_dev); 50 return 0; 51} 52EXPORT_SYMBOL_GPL(comedi_set_hw_dev); 53 54static void comedi_clear_hw_dev(struct comedi_device *dev) 55{ 56 put_device(dev->hw_dev); 57 dev->hw_dev = NULL; 58} 59 60int comedi_alloc_subdevices(struct comedi_device *dev, int num_subdevices) 61{ 62 struct comedi_subdevice *s; 63 int i; 64 65 if (num_subdevices < 1) 66 return -EINVAL; 67 68 s = kcalloc(num_subdevices, sizeof(*s), GFP_KERNEL); 69 if (!s) 70 return -ENOMEM; 71 dev->subdevices = s; 72 dev->n_subdevices = num_subdevices; 73 74 for (i = 0; i < num_subdevices; ++i) { 75 s = &dev->subdevices[i]; 76 s->device = dev; 77 s->index = i; 78 s->async_dma_dir = DMA_NONE; 79 spin_lock_init(&s->spin_lock); 80 s->minor = -1; 81 } 82 return 0; 83} 84EXPORT_SYMBOL_GPL(comedi_alloc_subdevices); 85 86static void cleanup_device(struct comedi_device *dev) 87{ 88 int i; 89 struct comedi_subdevice *s; 90 91 if (dev->subdevices) { 92 for (i = 0; i < dev->n_subdevices; i++) { 93 s = &dev->subdevices[i]; 94 if (s->runflags & SRF_FREE_SPRIV) 95 kfree(s->private); 96 comedi_free_subdevice_minor(s); 97 if (s->async) { 98 comedi_buf_alloc(dev, s, 0); 99 kfree(s->async); 100 } 101 } 102 kfree(dev->subdevices); 103 dev->subdevices = NULL; 104 dev->n_subdevices = 0; 105 } 106 kfree(dev->private); 107 dev->private = NULL; 108 dev->driver = NULL; 109 dev->board_name = NULL; 110 dev->board_ptr = NULL; 111 dev->iobase = 0; 112 dev->iolen = 0; 113 dev->ioenabled = false; 114 dev->irq = 0; 115 dev->read_subdev = NULL; 116 dev->write_subdev = NULL; 117 dev->open = NULL; 118 dev->close = NULL; 119 comedi_clear_hw_dev(dev); 120} 121 122void comedi_device_detach(struct comedi_device *dev) 123{ 124 dev->attached = false; 125 if (dev->driver) 126 dev->driver->detach(dev); 127 cleanup_device(dev); 128} 129 130static int poll_invalid(struct comedi_device *dev, struct comedi_subdevice *s) 131{ 132 return -EINVAL; 133} 134 135int insn_inval(struct comedi_device *dev, struct comedi_subdevice *s, 136 struct comedi_insn *insn, unsigned int *data) 137{ 138 return -EINVAL; 139} 140 141static int insn_rw_emulate_bits(struct comedi_device *dev, 142 struct comedi_subdevice *s, 143 struct comedi_insn *insn, unsigned int *data) 144{ 145 struct comedi_insn new_insn; 146 int ret; 147 static const unsigned channels_per_bitfield = 32; 148 149 unsigned chan = CR_CHAN(insn->chanspec); 150 const unsigned base_bitfield_channel = 151 (chan < channels_per_bitfield) ? 0 : chan; 152 unsigned int new_data[2]; 153 memset(new_data, 0, sizeof(new_data)); 154 memset(&new_insn, 0, sizeof(new_insn)); 155 new_insn.insn = INSN_BITS; 156 new_insn.chanspec = base_bitfield_channel; 157 new_insn.n = 2; 158 new_insn.subdev = insn->subdev; 159 160 if (insn->insn == INSN_WRITE) { 161 if (!(s->subdev_flags & SDF_WRITABLE)) 162 return -EINVAL; 163 new_data[0] = 1 << (chan - base_bitfield_channel); /* mask */ 164 new_data[1] = data[0] ? (1 << (chan - base_bitfield_channel)) 165 : 0; /* bits */ 166 } 167 168 ret = s->insn_bits(dev, s, &new_insn, new_data); 169 if (ret < 0) 170 return ret; 171 172 if (insn->insn == INSN_READ) 173 data[0] = (new_data[1] >> (chan - base_bitfield_channel)) & 1; 174 175 return 1; 176} 177 178static int __comedi_device_postconfig_async(struct comedi_device *dev, 179 struct comedi_subdevice *s) 180{ 181 struct comedi_async *async; 182 unsigned int buf_size; 183 int ret; 184 185 if ((s->subdev_flags & (SDF_CMD_READ | SDF_CMD_WRITE)) == 0) { 186 dev_warn(dev->class_dev, 187 "async subdevices must support SDF_CMD_READ or SDF_CMD_WRITE\n"); 188 return -EINVAL; 189 } 190 if (!s->do_cmdtest) { 191 dev_warn(dev->class_dev, 192 "async subdevices must have a do_cmdtest() function\n"); 193 return -EINVAL; 194 } 195 196 async = kzalloc(sizeof(*async), GFP_KERNEL); 197 if (!async) 198 return -ENOMEM; 199 200 init_waitqueue_head(&async->wait_head); 201 async->subdevice = s; 202 s->async = async; 203 204 async->max_bufsize = comedi_default_buf_maxsize_kb * 1024; 205 buf_size = comedi_default_buf_size_kb * 1024; 206 if (buf_size > async->max_bufsize) 207 buf_size = async->max_bufsize; 208 209 if (comedi_buf_alloc(dev, s, buf_size) < 0) { 210 dev_warn(dev->class_dev, "Buffer allocation failed\n"); 211 return -ENOMEM; 212 } 213 if (s->buf_change) { 214 ret = s->buf_change(dev, s, buf_size); 215 if (ret < 0) 216 return ret; 217 } 218 219 comedi_alloc_subdevice_minor(s); 220 221 return 0; 222} 223 224static int __comedi_device_postconfig(struct comedi_device *dev) 225{ 226 struct comedi_subdevice *s; 227 int ret; 228 int i; 229 230 for (i = 0; i < dev->n_subdevices; i++) { 231 s = &dev->subdevices[i]; 232 233 if (s->type == COMEDI_SUBD_UNUSED) 234 continue; 235 236 if (s->len_chanlist == 0) 237 s->len_chanlist = 1; 238 239 if (s->do_cmd) { 240 ret = __comedi_device_postconfig_async(dev, s); 241 if (ret) 242 return ret; 243 } 244 245 if (!s->range_table && !s->range_table_list) 246 s->range_table = &range_unknown; 247 248 if (!s->insn_read && s->insn_bits) 249 s->insn_read = insn_rw_emulate_bits; 250 if (!s->insn_write && s->insn_bits) 251 s->insn_write = insn_rw_emulate_bits; 252 253 if (!s->insn_read) 254 s->insn_read = insn_inval; 255 if (!s->insn_write) 256 s->insn_write = insn_inval; 257 if (!s->insn_bits) 258 s->insn_bits = insn_inval; 259 if (!s->insn_config) 260 s->insn_config = insn_inval; 261 262 if (!s->poll) 263 s->poll = poll_invalid; 264 } 265 266 return 0; 267} 268 269/* do a little post-config cleanup */ 270static int comedi_device_postconfig(struct comedi_device *dev) 271{ 272 int ret; 273 274 ret = __comedi_device_postconfig(dev); 275 if (ret < 0) 276 return ret; 277 smp_wmb(); 278 dev->attached = true; 279 return 0; 280} 281 282/* 283 * Generic recognize function for drivers that register their supported 284 * board names. 285 * 286 * 'driv->board_name' points to a 'const char *' member within the 287 * zeroth element of an array of some private board information 288 * structure, say 'struct foo_board' containing a member 'const char 289 * *board_name' that is initialized to point to a board name string that 290 * is one of the candidates matched against this function's 'name' 291 * parameter. 292 * 293 * 'driv->offset' is the size of the private board information 294 * structure, say 'sizeof(struct foo_board)', and 'driv->num_names' is 295 * the length of the array of private board information structures. 296 * 297 * If one of the board names in the array of private board information 298 * structures matches the name supplied to this function, the function 299 * returns a pointer to the pointer to the board name, otherwise it 300 * returns NULL. The return value ends up in the 'board_ptr' member of 301 * a 'struct comedi_device' that the low-level comedi driver's 302 * 'attach()' hook can convert to a point to a particular element of its 303 * array of private board information structures by subtracting the 304 * offset of the member that points to the board name. (No subtraction 305 * is required if the board name pointer is the first member of the 306 * private board information structure, which is generally the case.) 307 */ 308static void *comedi_recognize(struct comedi_driver *driv, const char *name) 309{ 310 char **name_ptr = (char **)driv->board_name; 311 int i; 312 313 for (i = 0; i < driv->num_names; i++) { 314 if (strcmp(*name_ptr, name) == 0) 315 return name_ptr; 316 name_ptr = (void *)name_ptr + driv->offset; 317 } 318 319 return NULL; 320} 321 322static void comedi_report_boards(struct comedi_driver *driv) 323{ 324 unsigned int i; 325 const char *const *name_ptr; 326 327 pr_info("comedi: valid board names for %s driver are:\n", 328 driv->driver_name); 329 330 name_ptr = driv->board_name; 331 for (i = 0; i < driv->num_names; i++) { 332 pr_info(" %s\n", *name_ptr); 333 name_ptr = (const char **)((char *)name_ptr + driv->offset); 334 } 335 336 if (driv->num_names == 0) 337 pr_info(" %s\n", driv->driver_name); 338} 339 340/** 341 * comedi_load_firmware() - Request and load firmware for a device. 342 * @dev: comedi_device struct 343 * @hw_device: device struct for the comedi_device 344 * @name: the name of the firmware image 345 * @cb: callback to the upload the firmware image 346 * @context: private context from the driver 347 */ 348int comedi_load_firmware(struct comedi_device *dev, 349 struct device *device, 350 const char *name, 351 int (*cb)(struct comedi_device *dev, 352 const u8 *data, size_t size, 353 unsigned long context), 354 unsigned long context) 355{ 356 const struct firmware *fw; 357 int ret; 358 359 if (!cb) 360 return -EINVAL; 361 362 ret = request_firmware(&fw, name, device); 363 if (ret == 0) { 364 ret = cb(dev, fw->data, fw->size, context); 365 release_firmware(fw); 366 } 367 368 return ret; 369} 370EXPORT_SYMBOL_GPL(comedi_load_firmware); 371 372/** 373 * __comedi_request_region() - Request an I/O reqion for a legacy driver. 374 * @dev: comedi_device struct 375 * @start: base address of the I/O reqion 376 * @len: length of the I/O region 377 */ 378int __comedi_request_region(struct comedi_device *dev, 379 unsigned long start, unsigned long len) 380{ 381 if (!start) { 382 dev_warn(dev->class_dev, 383 "%s: a I/O base address must be specified\n", 384 dev->board_name); 385 return -EINVAL; 386 } 387 388 if (!request_region(start, len, dev->board_name)) { 389 dev_warn(dev->class_dev, "%s: I/O port conflict (%#lx,%lu)\n", 390 dev->board_name, start, len); 391 return -EIO; 392 } 393 394 return 0; 395} 396EXPORT_SYMBOL_GPL(__comedi_request_region); 397 398/** 399 * comedi_request_region() - Request an I/O reqion for a legacy driver. 400 * @dev: comedi_device struct 401 * @start: base address of the I/O reqion 402 * @len: length of the I/O region 403 */ 404int comedi_request_region(struct comedi_device *dev, 405 unsigned long start, unsigned long len) 406{ 407 int ret; 408 409 ret = __comedi_request_region(dev, start, len); 410 if (ret == 0) { 411 dev->iobase = start; 412 dev->iolen = len; 413 } 414 415 return ret; 416} 417EXPORT_SYMBOL_GPL(comedi_request_region); 418 419/** 420 * comedi_legacy_detach() - A generic (*detach) function for legacy drivers. 421 * @dev: comedi_device struct 422 */ 423void comedi_legacy_detach(struct comedi_device *dev) 424{ 425 if (dev->irq) { 426 free_irq(dev->irq, dev); 427 dev->irq = 0; 428 } 429 if (dev->iobase && dev->iolen) { 430 release_region(dev->iobase, dev->iolen); 431 dev->iobase = 0; 432 dev->iolen = 0; 433 } 434} 435EXPORT_SYMBOL_GPL(comedi_legacy_detach); 436 437int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it) 438{ 439 struct comedi_driver *driv; 440 int ret; 441 442 if (dev->attached) 443 return -EBUSY; 444 445 for (driv = comedi_drivers; driv; driv = driv->next) { 446 if (!try_module_get(driv->module)) 447 continue; 448 if (driv->num_names) { 449 dev->board_ptr = comedi_recognize(driv, it->board_name); 450 if (dev->board_ptr) 451 break; 452 } else if (strcmp(driv->driver_name, it->board_name) == 0) 453 break; 454 module_put(driv->module); 455 } 456 if (driv == NULL) { 457 /* recognize has failed if we get here */ 458 /* report valid board names before returning error */ 459 for (driv = comedi_drivers; driv; driv = driv->next) { 460 if (!try_module_get(driv->module)) 461 continue; 462 comedi_report_boards(driv); 463 module_put(driv->module); 464 } 465 return -EIO; 466 } 467 if (driv->attach == NULL) { 468 /* driver does not support manual configuration */ 469 dev_warn(dev->class_dev, 470 "driver '%s' does not support attach using comedi_config\n", 471 driv->driver_name); 472 module_put(driv->module); 473 return -ENOSYS; 474 } 475 /* initialize dev->driver here so 476 * comedi_error() can be called from attach */ 477 dev->driver = driv; 478 dev->board_name = dev->board_ptr ? *(const char **)dev->board_ptr 479 : dev->driver->driver_name; 480 ret = driv->attach(dev, it); 481 if (ret >= 0) 482 ret = comedi_device_postconfig(dev); 483 if (ret < 0) { 484 comedi_device_detach(dev); 485 module_put(dev->driver->module); 486 } 487 /* On success, the driver module count has been incremented. */ 488 return ret; 489} 490 491int comedi_auto_config(struct device *hardware_device, 492 struct comedi_driver *driver, unsigned long context) 493{ 494 struct comedi_device *dev; 495 int ret; 496 497 if (!hardware_device) { 498 pr_warn("BUG! comedi_auto_config called with NULL hardware_device\n"); 499 return -EINVAL; 500 } 501 if (!driver) { 502 dev_warn(hardware_device, 503 "BUG! comedi_auto_config called with NULL comedi driver\n"); 504 return -EINVAL; 505 } 506 507 if (!driver->auto_attach) { 508 dev_warn(hardware_device, 509 "BUG! comedi driver '%s' has no auto_attach handler\n", 510 driver->driver_name); 511 return -EINVAL; 512 } 513 514 dev = comedi_alloc_board_minor(hardware_device); 515 if (IS_ERR(dev)) 516 return PTR_ERR(dev); 517 /* Note: comedi_alloc_board_minor() locked dev->mutex. */ 518 519 dev->driver = driver; 520 dev->board_name = dev->driver->driver_name; 521 ret = driver->auto_attach(dev, context); 522 if (ret >= 0) 523 ret = comedi_device_postconfig(dev); 524 if (ret < 0) 525 comedi_device_detach(dev); 526 mutex_unlock(&dev->mutex); 527 528 if (ret < 0) 529 comedi_release_hardware_device(hardware_device); 530 return ret; 531} 532EXPORT_SYMBOL_GPL(comedi_auto_config); 533 534void comedi_auto_unconfig(struct device *hardware_device) 535{ 536 if (hardware_device == NULL) 537 return; 538 comedi_release_hardware_device(hardware_device); 539} 540EXPORT_SYMBOL_GPL(comedi_auto_unconfig); 541 542int comedi_driver_register(struct comedi_driver *driver) 543{ 544 driver->next = comedi_drivers; 545 comedi_drivers = driver; 546 547 return 0; 548} 549EXPORT_SYMBOL_GPL(comedi_driver_register); 550 551int comedi_driver_unregister(struct comedi_driver *driver) 552{ 553 struct comedi_driver *prev; 554 int i; 555 556 /* check for devices using this driver */ 557 for (i = 0; i < COMEDI_NUM_BOARD_MINORS; i++) { 558 struct comedi_device *dev = comedi_dev_from_minor(i); 559 560 if (!dev) 561 continue; 562 563 mutex_lock(&dev->mutex); 564 if (dev->attached && dev->driver == driver) { 565 if (dev->use_count) 566 dev_warn(dev->class_dev, 567 "BUG! detaching device with use_count=%d\n", 568 dev->use_count); 569 comedi_device_detach(dev); 570 } 571 mutex_unlock(&dev->mutex); 572 } 573 574 if (comedi_drivers == driver) { 575 comedi_drivers = driver->next; 576 return 0; 577 } 578 579 for (prev = comedi_drivers; prev->next; prev = prev->next) { 580 if (prev->next == driver) { 581 prev->next = driver->next; 582 return 0; 583 } 584 } 585 return -EINVAL; 586} 587EXPORT_SYMBOL_GPL(comedi_driver_unregister); 588