drivers.c revision f375ac5f197d32aeffe5436e5864525cc14ce44a
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 89static void cleanup_device(struct comedi_device *dev) 90{ 91 int i; 92 struct comedi_subdevice *s; 93 94 if (dev->subdevices) { 95 for (i = 0; i < dev->n_subdevices; i++) { 96 s = &dev->subdevices[i]; 97 comedi_free_subdevice_minor(s); 98 if (s->async) { 99 comedi_buf_alloc(dev, s, 0); 100 kfree(s->async); 101 } 102 } 103 kfree(dev->subdevices); 104 dev->subdevices = NULL; 105 dev->n_subdevices = 0; 106 } 107 kfree(dev->private); 108 dev->private = NULL; 109 dev->driver = NULL; 110 dev->board_name = NULL; 111 dev->board_ptr = NULL; 112 dev->iobase = 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_request_region() - Request an I/O reqion for a legacy driver. 342 * @dev: comedi_device struct 343 * @start: base address of the I/O reqion 344 * @len: length of the I/O region 345 */ 346int comedi_request_region(struct comedi_device *dev, 347 unsigned long start, unsigned long len) 348{ 349 if (!start) { 350 dev_warn(dev->class_dev, 351 "%s: a I/O base address must be specified\n", 352 dev->board_name); 353 return -EINVAL; 354 } 355 356 if (!request_region(start, len, dev->board_name)) { 357 dev_warn(dev->class_dev, "%s: I/O port conflict (%#lx,%lu)\n", 358 dev->board_name, start, len); 359 return -EIO; 360 } 361 dev->iobase = start; 362 363 return 0; 364} 365EXPORT_SYMBOL_GPL(comedi_request_region); 366 367int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it) 368{ 369 struct comedi_driver *driv; 370 int ret; 371 372 if (dev->attached) 373 return -EBUSY; 374 375 for (driv = comedi_drivers; driv; driv = driv->next) { 376 if (!try_module_get(driv->module)) 377 continue; 378 if (driv->num_names) { 379 dev->board_ptr = comedi_recognize(driv, it->board_name); 380 if (dev->board_ptr) 381 break; 382 } else if (strcmp(driv->driver_name, it->board_name) == 0) 383 break; 384 module_put(driv->module); 385 } 386 if (driv == NULL) { 387 /* recognize has failed if we get here */ 388 /* report valid board names before returning error */ 389 for (driv = comedi_drivers; driv; driv = driv->next) { 390 if (!try_module_get(driv->module)) 391 continue; 392 comedi_report_boards(driv); 393 module_put(driv->module); 394 } 395 return -EIO; 396 } 397 if (driv->attach == NULL) { 398 /* driver does not support manual configuration */ 399 dev_warn(dev->class_dev, 400 "driver '%s' does not support attach using comedi_config\n", 401 driv->driver_name); 402 module_put(driv->module); 403 return -ENOSYS; 404 } 405 /* initialize dev->driver here so 406 * comedi_error() can be called from attach */ 407 dev->driver = driv; 408 dev->board_name = dev->board_ptr ? *(const char **)dev->board_ptr 409 : dev->driver->driver_name; 410 ret = driv->attach(dev, it); 411 if (ret >= 0) 412 ret = comedi_device_postconfig(dev); 413 if (ret < 0) { 414 comedi_device_detach(dev); 415 module_put(dev->driver->module); 416 } 417 /* On success, the driver module count has been incremented. */ 418 return ret; 419} 420 421int comedi_auto_config(struct device *hardware_device, 422 struct comedi_driver *driver, unsigned long context) 423{ 424 struct comedi_device *dev; 425 int ret; 426 427 if (!hardware_device) { 428 pr_warn("BUG! comedi_auto_config called with NULL hardware_device\n"); 429 return -EINVAL; 430 } 431 if (!driver) { 432 dev_warn(hardware_device, 433 "BUG! comedi_auto_config called with NULL comedi driver\n"); 434 return -EINVAL; 435 } 436 437 if (!driver->auto_attach) { 438 dev_warn(hardware_device, 439 "BUG! comedi driver '%s' has no auto_attach handler\n", 440 driver->driver_name); 441 return -EINVAL; 442 } 443 444 dev = comedi_alloc_board_minor(hardware_device); 445 if (IS_ERR(dev)) 446 return PTR_ERR(dev); 447 /* Note: comedi_alloc_board_minor() locked dev->mutex. */ 448 449 dev->driver = driver; 450 dev->board_name = dev->driver->driver_name; 451 ret = driver->auto_attach(dev, context); 452 if (ret >= 0) 453 ret = comedi_device_postconfig(dev); 454 if (ret < 0) 455 comedi_device_detach(dev); 456 mutex_unlock(&dev->mutex); 457 458 if (ret < 0) 459 comedi_release_hardware_device(hardware_device); 460 return ret; 461} 462EXPORT_SYMBOL_GPL(comedi_auto_config); 463 464void comedi_auto_unconfig(struct device *hardware_device) 465{ 466 if (hardware_device == NULL) 467 return; 468 comedi_release_hardware_device(hardware_device); 469} 470EXPORT_SYMBOL_GPL(comedi_auto_unconfig); 471 472int comedi_driver_register(struct comedi_driver *driver) 473{ 474 driver->next = comedi_drivers; 475 comedi_drivers = driver; 476 477 return 0; 478} 479EXPORT_SYMBOL(comedi_driver_register); 480 481int comedi_driver_unregister(struct comedi_driver *driver) 482{ 483 struct comedi_driver *prev; 484 int i; 485 486 /* check for devices using this driver */ 487 for (i = 0; i < COMEDI_NUM_BOARD_MINORS; i++) { 488 struct comedi_device *dev = comedi_dev_from_minor(i); 489 490 if (!dev) 491 continue; 492 493 mutex_lock(&dev->mutex); 494 if (dev->attached && dev->driver == driver) { 495 if (dev->use_count) 496 dev_warn(dev->class_dev, 497 "BUG! detaching device with use_count=%d\n", 498 dev->use_count); 499 comedi_device_detach(dev); 500 } 501 mutex_unlock(&dev->mutex); 502 } 503 504 if (comedi_drivers == driver) { 505 comedi_drivers = driver->next; 506 return 0; 507 } 508 509 for (prev = comedi_drivers; prev->next; prev = prev->next) { 510 if (prev->next == driver) { 511 prev->next = driver->next; 512 return 0; 513 } 514 } 515 return -EINVAL; 516} 517EXPORT_SYMBOL(comedi_driver_unregister); 518