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