drivers.c revision 6013a9a57bbe0fd147482297b04d3d848aa24b67
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 } 278 if (!dev->board_name) { 279 dev_warn(dev->class_dev, "BUG: dev->board_name=NULL\n"); 280 dev->board_name = "BUG"; 281 } 282 smp_wmb(); 283 dev->attached = true; 284 return 0; 285} 286 287/* 288 * Generic recognize function for drivers that register their supported 289 * board names. 290 * 291 * 'driv->board_name' points to a 'const char *' member within the 292 * zeroth element of an array of some private board information 293 * structure, say 'struct foo_board' containing a member 'const char 294 * *board_name' that is initialized to point to a board name string that 295 * is one of the candidates matched against this function's 'name' 296 * parameter. 297 * 298 * 'driv->offset' is the size of the private board information 299 * structure, say 'sizeof(struct foo_board)', and 'driv->num_names' is 300 * the length of the array of private board information structures. 301 * 302 * If one of the board names in the array of private board information 303 * structures matches the name supplied to this function, the function 304 * returns a pointer to the pointer to the board name, otherwise it 305 * returns NULL. The return value ends up in the 'board_ptr' member of 306 * a 'struct comedi_device' that the low-level comedi driver's 307 * 'attach()' hook can convert to a point to a particular element of its 308 * array of private board information structures by subtracting the 309 * offset of the member that points to the board name. (No subtraction 310 * is required if the board name pointer is the first member of the 311 * private board information structure, which is generally the case.) 312 */ 313static void *comedi_recognize(struct comedi_driver *driv, const char *name) 314{ 315 char **name_ptr = (char **)driv->board_name; 316 int i; 317 318 for (i = 0; i < driv->num_names; i++) { 319 if (strcmp(*name_ptr, name) == 0) 320 return name_ptr; 321 name_ptr = (void *)name_ptr + driv->offset; 322 } 323 324 return NULL; 325} 326 327static void comedi_report_boards(struct comedi_driver *driv) 328{ 329 unsigned int i; 330 const char *const *name_ptr; 331 332 pr_info("comedi: valid board names for %s driver are:\n", 333 driv->driver_name); 334 335 name_ptr = driv->board_name; 336 for (i = 0; i < driv->num_names; i++) { 337 pr_info(" %s\n", *name_ptr); 338 name_ptr = (const char **)((char *)name_ptr + driv->offset); 339 } 340 341 if (driv->num_names == 0) 342 pr_info(" %s\n", driv->driver_name); 343} 344 345int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it) 346{ 347 struct comedi_driver *driv; 348 int ret; 349 350 if (dev->attached) 351 return -EBUSY; 352 353 for (driv = comedi_drivers; driv; driv = driv->next) { 354 if (!try_module_get(driv->module)) 355 continue; 356 if (driv->num_names) { 357 dev->board_ptr = comedi_recognize(driv, it->board_name); 358 if (dev->board_ptr) 359 break; 360 } else if (strcmp(driv->driver_name, it->board_name) == 0) 361 break; 362 module_put(driv->module); 363 } 364 if (driv == NULL) { 365 /* recognize has failed if we get here */ 366 /* report valid board names before returning error */ 367 for (driv = comedi_drivers; driv; driv = driv->next) { 368 if (!try_module_get(driv->module)) 369 continue; 370 comedi_report_boards(driv); 371 module_put(driv->module); 372 } 373 return -EIO; 374 } 375 if (driv->attach == NULL) { 376 /* driver does not support manual configuration */ 377 dev_warn(dev->class_dev, 378 "driver '%s' does not support attach using comedi_config\n", 379 driv->driver_name); 380 module_put(driv->module); 381 return -ENOSYS; 382 } 383 /* initialize dev->driver here so 384 * comedi_error() can be called from attach */ 385 dev->driver = driv; 386 ret = driv->attach(dev, it); 387 if (ret >= 0) 388 ret = comedi_device_postconfig(dev); 389 if (ret < 0) { 390 comedi_device_detach(dev); 391 module_put(dev->driver->module); 392 } 393 /* On success, the driver module count has been incremented. */ 394 return ret; 395} 396 397int comedi_auto_config(struct device *hardware_device, 398 struct comedi_driver *driver, unsigned long context) 399{ 400 struct comedi_device *dev; 401 int ret; 402 403 if (!hardware_device) { 404 pr_warn("BUG! comedi_auto_config called with NULL hardware_device\n"); 405 return -EINVAL; 406 } 407 if (!driver) { 408 dev_warn(hardware_device, 409 "BUG! comedi_auto_config called with NULL comedi driver\n"); 410 return -EINVAL; 411 } 412 413 if (!driver->auto_attach) { 414 dev_warn(hardware_device, 415 "BUG! comedi driver '%s' has no auto_attach handler\n", 416 driver->driver_name); 417 return -EINVAL; 418 } 419 420 dev = comedi_alloc_board_minor(hardware_device); 421 if (IS_ERR(dev)) 422 return PTR_ERR(dev); 423 /* Note: comedi_alloc_board_minor() locked dev->mutex. */ 424 425 dev->driver = driver; 426 ret = driver->auto_attach(dev, context); 427 if (ret >= 0) 428 ret = comedi_device_postconfig(dev); 429 if (ret < 0) 430 comedi_device_detach(dev); 431 mutex_unlock(&dev->mutex); 432 433 if (ret < 0) 434 comedi_release_hardware_device(hardware_device); 435 return ret; 436} 437EXPORT_SYMBOL_GPL(comedi_auto_config); 438 439void comedi_auto_unconfig(struct device *hardware_device) 440{ 441 if (hardware_device == NULL) 442 return; 443 comedi_release_hardware_device(hardware_device); 444} 445EXPORT_SYMBOL_GPL(comedi_auto_unconfig); 446 447int comedi_driver_register(struct comedi_driver *driver) 448{ 449 driver->next = comedi_drivers; 450 comedi_drivers = driver; 451 452 return 0; 453} 454EXPORT_SYMBOL(comedi_driver_register); 455 456int comedi_driver_unregister(struct comedi_driver *driver) 457{ 458 struct comedi_driver *prev; 459 int i; 460 461 /* check for devices using this driver */ 462 for (i = 0; i < COMEDI_NUM_BOARD_MINORS; i++) { 463 struct comedi_device *dev = comedi_dev_from_minor(i); 464 465 if (!dev) 466 continue; 467 468 mutex_lock(&dev->mutex); 469 if (dev->attached && dev->driver == driver) { 470 if (dev->use_count) 471 dev_warn(dev->class_dev, 472 "BUG! detaching device with use_count=%d\n", 473 dev->use_count); 474 comedi_device_detach(dev); 475 } 476 mutex_unlock(&dev->mutex); 477 } 478 479 if (comedi_drivers == driver) { 480 comedi_drivers = driver->next; 481 return 0; 482 } 483 484 for (prev = comedi_drivers; prev->next; prev = prev->next) { 485 if (prev->next == driver) { 486 prev->next = driver->next; 487 return 0; 488 } 489 } 490 return -EINVAL; 491} 492EXPORT_SYMBOL(comedi_driver_unregister); 493