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