comedi_fops.c revision a3cb729ef4a192f04179f780122df78ef1ffe779
1/* 2 comedi/comedi_fops.c 3 comedi kernel module 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#undef DEBUG 25 26#define __NO_VERSION__ 27#include "comedi_fops.h" 28#include "comedi_compat32.h" 29 30#include <linux/module.h> 31#include <linux/errno.h> 32#include <linux/kernel.h> 33#include <linux/sched.h> 34#include <linux/fcntl.h> 35#include <linux/delay.h> 36#include <linux/ioport.h> 37#include <linux/mm.h> 38#include <linux/slab.h> 39#include <linux/kmod.h> 40#include <linux/poll.h> 41#include <linux/init.h> 42#include <linux/device.h> 43#include <linux/vmalloc.h> 44#include <linux/fs.h> 45#include "comedidev.h" 46#include <linux/cdev.h> 47 48#include <linux/io.h> 49#include <linux/uaccess.h> 50 51/* #include "kvmem.h" */ 52 53MODULE_AUTHOR("http://www.comedi.org"); 54MODULE_DESCRIPTION("Comedi core module"); 55MODULE_LICENSE("GPL"); 56 57#ifdef CONFIG_COMEDI_DEBUG 58int comedi_debug; 59module_param(comedi_debug, int, 0644); 60#endif 61 62int comedi_autoconfig = 1; 63module_param(comedi_autoconfig, bool, 0444); 64 65int comedi_num_legacy_minors = 0; 66module_param(comedi_num_legacy_minors, int, 0444); 67 68static DEFINE_SPINLOCK(comedi_file_info_table_lock); 69static struct comedi_device_file_info 70 *comedi_file_info_table[COMEDI_NUM_MINORS]; 71 72static int do_devconfig_ioctl(comedi_device *dev, comedi_devconfig *arg); 73static int do_bufconfig_ioctl(comedi_device *dev, void *arg); 74static int do_devinfo_ioctl(comedi_device *dev, comedi_devinfo *arg, 75 struct file *file); 76static int do_subdinfo_ioctl(comedi_device *dev, comedi_subdinfo *arg, 77 void *file); 78static int do_chaninfo_ioctl(comedi_device *dev, comedi_chaninfo *arg); 79static int do_bufinfo_ioctl(comedi_device *dev, void *arg); 80static int do_cmd_ioctl(comedi_device *dev, void *arg, void *file); 81static int do_lock_ioctl(comedi_device *dev, unsigned int arg, void *file); 82static int do_unlock_ioctl(comedi_device *dev, unsigned int arg, void *file); 83static int do_cancel_ioctl(comedi_device *dev, unsigned int arg, void *file); 84static int do_cmdtest_ioctl(comedi_device *dev, void *arg, void *file); 85static int do_insnlist_ioctl(comedi_device *dev, void *arg, void *file); 86static int do_insn_ioctl(comedi_device *dev, void *arg, void *file); 87static int do_poll_ioctl(comedi_device *dev, unsigned int subd, void *file); 88 89extern void do_become_nonbusy(comedi_device *dev, comedi_subdevice *s); 90static int do_cancel(comedi_device *dev, comedi_subdevice *s); 91 92static int comedi_fasync(int fd, struct file *file, int on); 93 94static int is_device_busy(comedi_device *dev); 95 96#ifdef HAVE_UNLOCKED_IOCTL 97static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd, 98 unsigned long arg) 99#else 100static int comedi_ioctl(struct inode *inode, struct file *file, 101 unsigned int cmd, unsigned long arg) 102#endif 103{ 104 const unsigned minor = iminor(file->f_dentry->d_inode); 105 struct comedi_device_file_info *dev_file_info = 106 comedi_get_device_file_info(minor); 107 comedi_device *dev = dev_file_info->device; 108 int rc; 109 110 mutex_lock(&dev->mutex); 111 112 /* Device config is special, because it must work on 113 * an unconfigured device. */ 114 if (cmd == COMEDI_DEVCONFIG) { 115 rc = do_devconfig_ioctl(dev, (void *)arg); 116 goto done; 117 } 118 119 if (!dev->attached) { 120 DPRINTK("no driver configured on /dev/comedi%i\n", dev->minor); 121 rc = -ENODEV; 122 goto done; 123 } 124 125 switch (cmd) { 126 case COMEDI_BUFCONFIG: 127 rc = do_bufconfig_ioctl(dev, (void *)arg); 128 break; 129 case COMEDI_DEVINFO: 130 rc = do_devinfo_ioctl(dev, (void *)arg, file); 131 break; 132 case COMEDI_SUBDINFO: 133 rc = do_subdinfo_ioctl(dev, (void *)arg, file); 134 break; 135 case COMEDI_CHANINFO: 136 rc = do_chaninfo_ioctl(dev, (void *)arg); 137 break; 138 case COMEDI_RANGEINFO: 139 rc = do_rangeinfo_ioctl(dev, (void *)arg); 140 break; 141 case COMEDI_BUFINFO: 142 rc = do_bufinfo_ioctl(dev, (void *)arg); 143 break; 144 case COMEDI_LOCK: 145 rc = do_lock_ioctl(dev, arg, file); 146 break; 147 case COMEDI_UNLOCK: 148 rc = do_unlock_ioctl(dev, arg, file); 149 break; 150 case COMEDI_CANCEL: 151 rc = do_cancel_ioctl(dev, arg, file); 152 break; 153 case COMEDI_CMD: 154 rc = do_cmd_ioctl(dev, (void *)arg, file); 155 break; 156 case COMEDI_CMDTEST: 157 rc = do_cmdtest_ioctl(dev, (void *)arg, file); 158 break; 159 case COMEDI_INSNLIST: 160 rc = do_insnlist_ioctl(dev, (void *)arg, file); 161 break; 162 case COMEDI_INSN: 163 rc = do_insn_ioctl(dev, (void *)arg, file); 164 break; 165 case COMEDI_POLL: 166 rc = do_poll_ioctl(dev, arg, file); 167 break; 168 default: 169 rc = -ENOTTY; 170 break; 171 } 172 173done: 174 mutex_unlock(&dev->mutex); 175 return rc; 176} 177 178/* 179 COMEDI_DEVCONFIG 180 device config ioctl 181 182 arg: 183 pointer to devconfig structure 184 185 reads: 186 devconfig structure at arg 187 188 writes: 189 none 190*/ 191static int do_devconfig_ioctl(comedi_device *dev, comedi_devconfig *arg) 192{ 193 comedi_devconfig it; 194 int ret; 195 unsigned char *aux_data = NULL; 196 int aux_len; 197 198 if (!capable(CAP_SYS_ADMIN)) 199 return -EPERM; 200 201 if (arg == NULL) { 202 if (is_device_busy(dev)) 203 return -EBUSY; 204 if (dev->attached) { 205 struct module *driver_module = dev->driver->module; 206 comedi_device_detach(dev); 207 module_put(driver_module); 208 } 209 return 0; 210 } 211 212 if (copy_from_user(&it, arg, sizeof(comedi_devconfig))) 213 return -EFAULT; 214 215 it.board_name[COMEDI_NAMELEN - 1] = 0; 216 217 if (comedi_aux_data(it.options, 0) && 218 it.options[COMEDI_DEVCONF_AUX_DATA_LENGTH]) { 219 int bit_shift; 220 aux_len = it.options[COMEDI_DEVCONF_AUX_DATA_LENGTH]; 221 if (aux_len < 0) 222 return -EFAULT; 223 224 aux_data = vmalloc(aux_len); 225 if (!aux_data) 226 return -ENOMEM; 227 228 if (copy_from_user(aux_data, 229 comedi_aux_data(it.options, 0), aux_len)) { 230 vfree(aux_data); 231 return -EFAULT; 232 } 233 it.options[COMEDI_DEVCONF_AUX_DATA_LO] = 234 (unsigned long)aux_data; 235 if (sizeof(void *) > sizeof(int)) { 236 bit_shift = sizeof(int) * 8; 237 it.options[COMEDI_DEVCONF_AUX_DATA_HI] = 238 ((unsigned long)aux_data) >> bit_shift; 239 } else 240 it.options[COMEDI_DEVCONF_AUX_DATA_HI] = 0; 241 } 242 243 ret = comedi_device_attach(dev, &it); 244 if (ret == 0) { 245 if (!try_module_get(dev->driver->module)) { 246 comedi_device_detach(dev); 247 return -ENOSYS; 248 } 249 } 250 251 if (aux_data) 252 vfree(aux_data); 253 254 return ret; 255} 256 257/* 258 COMEDI_BUFCONFIG 259 buffer configuration ioctl 260 261 arg: 262 pointer to bufconfig structure 263 264 reads: 265 bufconfig at arg 266 267 writes: 268 modified bufconfig at arg 269 270*/ 271static int do_bufconfig_ioctl(comedi_device *dev, void *arg) 272{ 273 comedi_bufconfig bc; 274 comedi_async *async; 275 comedi_subdevice *s; 276 int ret = 0; 277 278 if (copy_from_user(&bc, arg, sizeof(comedi_bufconfig))) 279 return -EFAULT; 280 281 if (bc.subdevice >= dev->n_subdevices || bc.subdevice < 0) 282 return -EINVAL; 283 284 s = dev->subdevices + bc.subdevice; 285 async = s->async; 286 287 if (!async) { 288 DPRINTK("subdevice does not have async capability\n"); 289 bc.size = 0; 290 bc.maximum_size = 0; 291 goto copyback; 292 } 293 294 if (bc.maximum_size) { 295 if (!capable(CAP_SYS_ADMIN)) 296 return -EPERM; 297 298 async->max_bufsize = bc.maximum_size; 299 } 300 301 if (bc.size) { 302 if (bc.size > async->max_bufsize) 303 return -EPERM; 304 305 if (s->busy) { 306 DPRINTK("subdevice is busy, cannot resize buffer\n"); 307 return -EBUSY; 308 } 309 if (async->mmap_count) { 310 DPRINTK("subdevice is mmapped, cannot resize buffer\n"); 311 return -EBUSY; 312 } 313 314 if (!async->prealloc_buf) 315 return -EINVAL; 316 317 /* make sure buffer is an integral number of pages 318 * (we round up) */ 319 bc.size = (bc.size + PAGE_SIZE - 1) & PAGE_MASK; 320 321 ret = comedi_buf_alloc(dev, s, bc.size); 322 if (ret < 0) 323 return ret; 324 325 if (s->buf_change) { 326 ret = s->buf_change(dev, s, bc.size); 327 if (ret < 0) 328 return ret; 329 } 330 331 DPRINTK("comedi%i subd %d buffer resized to %i bytes\n", 332 dev->minor, bc.subdevice, async->prealloc_bufsz); 333 } 334 335 bc.size = async->prealloc_bufsz; 336 bc.maximum_size = async->max_bufsize; 337 338copyback: 339 if (copy_to_user(arg, &bc, sizeof(comedi_bufconfig))) 340 return -EFAULT; 341 342 return 0; 343} 344 345/* 346 COMEDI_DEVINFO 347 device info ioctl 348 349 arg: 350 pointer to devinfo structure 351 352 reads: 353 none 354 355 writes: 356 devinfo structure 357 358*/ 359static int do_devinfo_ioctl(comedi_device *dev, comedi_devinfo *arg, 360 struct file *file) 361{ 362 comedi_devinfo devinfo; 363 const unsigned minor = iminor(file->f_dentry->d_inode); 364 struct comedi_device_file_info *dev_file_info = 365 comedi_get_device_file_info(minor); 366 comedi_subdevice *read_subdev = 367 comedi_get_read_subdevice(dev_file_info); 368 comedi_subdevice *write_subdev = 369 comedi_get_write_subdevice(dev_file_info); 370 371 memset(&devinfo, 0, sizeof(devinfo)); 372 373 /* fill devinfo structure */ 374 devinfo.version_code = COMEDI_VERSION_CODE; 375 devinfo.n_subdevs = dev->n_subdevices; 376 memcpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN); 377 memcpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN); 378 379 if (read_subdev) 380 devinfo.read_subdevice = read_subdev - dev->subdevices; 381 else 382 devinfo.read_subdevice = -1; 383 384 if (write_subdev) 385 devinfo.write_subdevice = write_subdev - dev->subdevices; 386 else 387 devinfo.write_subdevice = -1; 388 389 if (copy_to_user(arg, &devinfo, sizeof(comedi_devinfo))) 390 return -EFAULT; 391 392 return 0; 393} 394 395/* 396 COMEDI_SUBDINFO 397 subdevice info ioctl 398 399 arg: 400 pointer to array of subdevice info structures 401 402 reads: 403 none 404 405 writes: 406 array of subdevice info structures at arg 407 408*/ 409static int do_subdinfo_ioctl(comedi_device *dev, comedi_subdinfo *arg, 410 void *file) 411{ 412 int ret, i; 413 comedi_subdinfo *tmp, *us; 414 comedi_subdevice *s; 415 416 tmp = kcalloc(dev->n_subdevices, sizeof(comedi_subdinfo), GFP_KERNEL); 417 if (!tmp) 418 return -ENOMEM; 419 420 /* fill subdinfo structs */ 421 for (i = 0; i < dev->n_subdevices; i++) { 422 s = dev->subdevices + i; 423 us = tmp + i; 424 425 us->type = s->type; 426 us->n_chan = s->n_chan; 427 us->subd_flags = s->subdev_flags; 428 if (comedi_get_subdevice_runflags(s) & SRF_RUNNING) 429 us->subd_flags |= SDF_RUNNING; 430#define TIMER_nanosec 5 /* backwards compatibility */ 431 us->timer_type = TIMER_nanosec; 432 us->len_chanlist = s->len_chanlist; 433 us->maxdata = s->maxdata; 434 if (s->range_table) { 435 us->range_type = 436 (i << 24) | (0 << 16) | (s->range_table->length); 437 } else { 438 us->range_type = 0; /* XXX */ 439 } 440 us->flags = s->flags; 441 442 if (s->busy) 443 us->subd_flags |= SDF_BUSY; 444 if (s->busy == file) 445 us->subd_flags |= SDF_BUSY_OWNER; 446 if (s->lock) 447 us->subd_flags |= SDF_LOCKED; 448 if (s->lock == file) 449 us->subd_flags |= SDF_LOCK_OWNER; 450 if (!s->maxdata && s->maxdata_list) 451 us->subd_flags |= SDF_MAXDATA; 452 if (s->flaglist) 453 us->subd_flags |= SDF_FLAGS; 454 if (s->range_table_list) 455 us->subd_flags |= SDF_RANGETYPE; 456 if (s->do_cmd) 457 us->subd_flags |= SDF_CMD; 458 459 if (s->insn_bits != &insn_inval) 460 us->insn_bits_support = COMEDI_SUPPORTED; 461 else 462 us->insn_bits_support = COMEDI_UNSUPPORTED; 463 464 us->settling_time_0 = s->settling_time_0; 465 } 466 467 ret = copy_to_user(arg, tmp, 468 dev->n_subdevices * sizeof(comedi_subdinfo)); 469 470 kfree(tmp); 471 472 return ret ? -EFAULT : 0; 473} 474 475/* 476 COMEDI_CHANINFO 477 subdevice info ioctl 478 479 arg: 480 pointer to chaninfo structure 481 482 reads: 483 chaninfo structure at arg 484 485 writes: 486 arrays at elements of chaninfo structure 487 488*/ 489static int do_chaninfo_ioctl(comedi_device *dev, comedi_chaninfo *arg) 490{ 491 comedi_subdevice *s; 492 comedi_chaninfo it; 493 494 if (copy_from_user(&it, arg, sizeof(comedi_chaninfo))) 495 return -EFAULT; 496 497 if (it.subdev >= dev->n_subdevices) 498 return -EINVAL; 499 s = dev->subdevices + it.subdev; 500 501 if (it.maxdata_list) { 502 if (s->maxdata || !s->maxdata_list) 503 return -EINVAL; 504 if (copy_to_user(it.maxdata_list, s->maxdata_list, 505 s->n_chan * sizeof(lsampl_t))) 506 return -EFAULT; 507 } 508 509 if (it.flaglist) { 510 if (!s->flaglist) 511 return -EINVAL; 512 if (copy_to_user(it.flaglist, s->flaglist, 513 s->n_chan * sizeof(unsigned int))) 514 return -EFAULT; 515 } 516 517 if (it.rangelist) { 518 int i; 519 520 if (!s->range_table_list) 521 return -EINVAL; 522 for (i = 0; i < s->n_chan; i++) { 523 int x; 524 525 x = (dev->minor << 28) | (it.subdev << 24) | (i << 16) | 526 (s->range_table_list[i]->length); 527 put_user(x, it.rangelist + i); 528 } 529#if 0 530 if (copy_to_user(it.rangelist, s->range_type_list, 531 s->n_chan*sizeof(unsigned int))) 532 return -EFAULT; 533#endif 534 } 535 536 return 0; 537} 538 539 /* 540 COMEDI_BUFINFO 541 buffer information ioctl 542 543 arg: 544 pointer to bufinfo structure 545 546 reads: 547 bufinfo at arg 548 549 writes: 550 modified bufinfo at arg 551 552 */ 553static int do_bufinfo_ioctl(comedi_device *dev, void *arg) 554{ 555 comedi_bufinfo bi; 556 comedi_subdevice *s; 557 comedi_async *async; 558 559 if (copy_from_user(&bi, arg, sizeof(comedi_bufinfo))) 560 return -EFAULT; 561 562 if (bi.subdevice >= dev->n_subdevices || bi.subdevice < 0) 563 return -EINVAL; 564 565 s = dev->subdevices + bi.subdevice; 566 async = s->async; 567 568 if (!async) { 569 DPRINTK("subdevice does not have async capability\n"); 570 bi.buf_write_ptr = 0; 571 bi.buf_read_ptr = 0; 572 bi.buf_write_count = 0; 573 bi.buf_read_count = 0; 574 goto copyback; 575 } 576 577 if (bi.bytes_read && (s->subdev_flags & SDF_CMD_READ)) { 578 bi.bytes_read = comedi_buf_read_alloc(async, bi.bytes_read); 579 comedi_buf_read_free(async, bi.bytes_read); 580 581 if (!(comedi_get_subdevice_runflags(s) & (SRF_ERROR | 582 SRF_RUNNING)) 583 && async->buf_write_count == async->buf_read_count) { 584 do_become_nonbusy(dev, s); 585 } 586 } 587 588 if (bi.bytes_written && (s->subdev_flags & SDF_CMD_WRITE)) { 589 bi.bytes_written = 590 comedi_buf_write_alloc(async, bi.bytes_written); 591 comedi_buf_write_free(async, bi.bytes_written); 592 } 593 594 bi.buf_write_count = async->buf_write_count; 595 bi.buf_write_ptr = async->buf_write_ptr; 596 bi.buf_read_count = async->buf_read_count; 597 bi.buf_read_ptr = async->buf_read_ptr; 598 599copyback: 600 if (copy_to_user(arg, &bi, sizeof(comedi_bufinfo))) 601 return -EFAULT; 602 603 return 0; 604} 605 606static int parse_insn(comedi_device *dev, comedi_insn *insn, lsampl_t *data, 607 void *file); 608/* 609 * COMEDI_INSNLIST 610 * synchronous instructions 611 * 612 * arg: 613 * pointer to sync cmd structure 614 * 615 * reads: 616 * sync cmd struct at arg 617 * instruction list 618 * data (for writes) 619 * 620 * writes: 621 * data (for reads) 622 */ 623/* arbitrary limits */ 624#define MAX_SAMPLES 256 625static int do_insnlist_ioctl(comedi_device *dev, void *arg, void *file) 626{ 627 comedi_insnlist insnlist; 628 comedi_insn *insns = NULL; 629 lsampl_t *data = NULL; 630 int i = 0; 631 int ret = 0; 632 633 if (copy_from_user(&insnlist, arg, sizeof(comedi_insnlist))) 634 return -EFAULT; 635 636 data = kmalloc(sizeof(lsampl_t) * MAX_SAMPLES, GFP_KERNEL); 637 if (!data) { 638 DPRINTK("kmalloc failed\n"); 639 ret = -ENOMEM; 640 goto error; 641 } 642 643 insns = kmalloc(sizeof(comedi_insn) * insnlist.n_insns, GFP_KERNEL); 644 if (!insns) { 645 DPRINTK("kmalloc failed\n"); 646 ret = -ENOMEM; 647 goto error; 648 } 649 650 if (copy_from_user(insns, insnlist.insns, 651 sizeof(comedi_insn) * insnlist.n_insns)) { 652 DPRINTK("copy_from_user failed\n"); 653 ret = -EFAULT; 654 goto error; 655 } 656 657 for (i = 0; i < insnlist.n_insns; i++) { 658 if (insns[i].n > MAX_SAMPLES) { 659 DPRINTK("number of samples too large\n"); 660 ret = -EINVAL; 661 goto error; 662 } 663 if (insns[i].insn & INSN_MASK_WRITE) { 664 if (copy_from_user(data, insns[i].data, 665 insns[i].n * sizeof(lsampl_t))) { 666 DPRINTK("copy_from_user failed\n"); 667 ret = -EFAULT; 668 goto error; 669 } 670 } 671 ret = parse_insn(dev, insns + i, data, file); 672 if (ret < 0) 673 goto error; 674 if (insns[i].insn & INSN_MASK_READ) { 675 if (copy_to_user(insns[i].data, data, 676 insns[i].n * sizeof(lsampl_t))) { 677 DPRINTK("copy_to_user failed\n"); 678 ret = -EFAULT; 679 goto error; 680 } 681 } 682 if (need_resched()) 683 schedule(); 684 } 685 686error: 687 kfree(insns); 688 kfree(data); 689 690 if (ret < 0) 691 return ret; 692 return i; 693} 694 695static int check_insn_config_length(comedi_insn *insn, lsampl_t *data) 696{ 697 if (insn->n < 1) 698 return -EINVAL; 699 700 switch (data[0]) { 701 case INSN_CONFIG_DIO_OUTPUT: 702 case INSN_CONFIG_DIO_INPUT: 703 case INSN_CONFIG_DISARM: 704 case INSN_CONFIG_RESET: 705 if (insn->n == 1) 706 return 0; 707 break; 708 case INSN_CONFIG_ARM: 709 case INSN_CONFIG_DIO_QUERY: 710 case INSN_CONFIG_BLOCK_SIZE: 711 case INSN_CONFIG_FILTER: 712 case INSN_CONFIG_SERIAL_CLOCK: 713 case INSN_CONFIG_BIDIRECTIONAL_DATA: 714 case INSN_CONFIG_ALT_SOURCE: 715 case INSN_CONFIG_SET_COUNTER_MODE: 716 case INSN_CONFIG_8254_READ_STATUS: 717 case INSN_CONFIG_SET_ROUTING: 718 case INSN_CONFIG_GET_ROUTING: 719 case INSN_CONFIG_GET_PWM_STATUS: 720 case INSN_CONFIG_PWM_SET_PERIOD: 721 case INSN_CONFIG_PWM_GET_PERIOD: 722 if (insn->n == 2) 723 return 0; 724 break; 725 case INSN_CONFIG_SET_GATE_SRC: 726 case INSN_CONFIG_GET_GATE_SRC: 727 case INSN_CONFIG_SET_CLOCK_SRC: 728 case INSN_CONFIG_GET_CLOCK_SRC: 729 case INSN_CONFIG_SET_OTHER_SRC: 730 case INSN_CONFIG_GET_COUNTER_STATUS: 731 case INSN_CONFIG_PWM_SET_H_BRIDGE: 732 case INSN_CONFIG_PWM_GET_H_BRIDGE: 733 case INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE: 734 if (insn->n == 3) 735 return 0; 736 break; 737 case INSN_CONFIG_PWM_OUTPUT: 738 case INSN_CONFIG_ANALOG_TRIG: 739 if (insn->n == 5) 740 return 0; 741 break; 742 /* by default we allow the insn since we don't have checks for 743 * all possible cases yet */ 744 default: 745 rt_printk("comedi: no check for data length of config insn id " 746 "%i is implemented.\n" 747 " Add a check to %s in %s.\n" 748 " Assuming n=%i is correct.\n", data[0], __func__, 749 __FILE__, insn->n); 750 return 0; 751 break; 752 } 753 return -EINVAL; 754} 755 756static int parse_insn(comedi_device *dev, comedi_insn *insn, lsampl_t *data, 757 void *file) 758{ 759 comedi_subdevice *s; 760 int ret = 0; 761 int i; 762 763 if (insn->insn & INSN_MASK_SPECIAL) { 764 /* a non-subdevice instruction */ 765 766 switch (insn->insn) { 767 case INSN_GTOD: 768 { 769 struct timeval tv; 770 771 if (insn->n != 2) { 772 ret = -EINVAL; 773 break; 774 } 775 776 do_gettimeofday(&tv); 777 data[0] = tv.tv_sec; 778 data[1] = tv.tv_usec; 779 ret = 2; 780 781 break; 782 } 783 case INSN_WAIT: 784 if (insn->n != 1 || data[0] >= 100000) { 785 ret = -EINVAL; 786 break; 787 } 788 udelay(data[0] / 1000); 789 ret = 1; 790 break; 791 case INSN_INTTRIG: 792 if (insn->n != 1) { 793 ret = -EINVAL; 794 break; 795 } 796 if (insn->subdev >= dev->n_subdevices) { 797 DPRINTK("%d not usable subdevice\n", 798 insn->subdev); 799 ret = -EINVAL; 800 break; 801 } 802 s = dev->subdevices + insn->subdev; 803 if (!s->async) { 804 DPRINTK("no async\n"); 805 ret = -EINVAL; 806 break; 807 } 808 if (!s->async->inttrig) { 809 DPRINTK("no inttrig\n"); 810 ret = -EAGAIN; 811 break; 812 } 813 ret = s->async->inttrig(dev, s, insn->data[0]); 814 if (ret >= 0) 815 ret = 1; 816 break; 817 default: 818 DPRINTK("invalid insn\n"); 819 ret = -EINVAL; 820 break; 821 } 822 } else { 823 /* a subdevice instruction */ 824 lsampl_t maxdata; 825 826 if (insn->subdev >= dev->n_subdevices) { 827 DPRINTK("subdevice %d out of range\n", insn->subdev); 828 ret = -EINVAL; 829 goto out; 830 } 831 s = dev->subdevices + insn->subdev; 832 833 if (s->type == COMEDI_SUBD_UNUSED) { 834 DPRINTK("%d not usable subdevice\n", insn->subdev); 835 ret = -EIO; 836 goto out; 837 } 838 839 /* are we locked? (ioctl lock) */ 840 if (s->lock && s->lock != file) { 841 DPRINTK("device locked\n"); 842 ret = -EACCES; 843 goto out; 844 } 845 846 ret = check_chanlist(s, 1, &insn->chanspec); 847 if (ret < 0) { 848 ret = -EINVAL; 849 DPRINTK("bad chanspec\n"); 850 goto out; 851 } 852 853 if (s->busy) { 854 ret = -EBUSY; 855 goto out; 856 } 857 /* This looks arbitrary. It is. */ 858 s->busy = &parse_insn; 859 switch (insn->insn) { 860 case INSN_READ: 861 ret = s->insn_read(dev, s, insn, data); 862 break; 863 case INSN_WRITE: 864 maxdata = s->maxdata_list 865 ? s->maxdata_list[CR_CHAN(insn->chanspec)] 866 : s->maxdata; 867 for (i = 0; i < insn->n; ++i) { 868 if (data[i] > maxdata) { 869 ret = -EINVAL; 870 DPRINTK("bad data value(s)\n"); 871 break; 872 } 873 } 874 if (ret == 0) 875 ret = s->insn_write(dev, s, insn, data); 876 break; 877 case INSN_BITS: 878 if (insn->n != 2) { 879 ret = -EINVAL; 880 break; 881 } 882 ret = s->insn_bits(dev, s, insn, data); 883 break; 884 case INSN_CONFIG: 885 ret = check_insn_config_length(insn, data); 886 if (ret) 887 break; 888 ret = s->insn_config(dev, s, insn, data); 889 break; 890 default: 891 ret = -EINVAL; 892 break; 893 } 894 895 s->busy = NULL; 896 } 897 898out: 899 return ret; 900} 901 902/* 903 * COMEDI_INSN 904 * synchronous instructions 905 * 906 * arg: 907 * pointer to insn 908 * 909 * reads: 910 * comedi_insn struct at arg 911 * data (for writes) 912 * 913 * writes: 914 * data (for reads) 915 */ 916static int do_insn_ioctl(comedi_device *dev, void *arg, void *file) 917{ 918 comedi_insn insn; 919 lsampl_t *data = NULL; 920 int ret = 0; 921 922 data = kmalloc(sizeof(lsampl_t) * MAX_SAMPLES, GFP_KERNEL); 923 if (!data) { 924 ret = -ENOMEM; 925 goto error; 926 } 927 928 if (copy_from_user(&insn, arg, sizeof(comedi_insn))) { 929 ret = -EFAULT; 930 goto error; 931 } 932 933 /* This is where the behavior of insn and insnlist deviate. */ 934 if (insn.n > MAX_SAMPLES) 935 insn.n = MAX_SAMPLES; 936 if (insn.insn & INSN_MASK_WRITE) { 937 if (copy_from_user(data, insn.data, insn.n * sizeof(lsampl_t))) { 938 ret = -EFAULT; 939 goto error; 940 } 941 } 942 ret = parse_insn(dev, &insn, data, file); 943 if (ret < 0) 944 goto error; 945 if (insn.insn & INSN_MASK_READ) { 946 if (copy_to_user(insn.data, data, insn.n * sizeof(lsampl_t))) { 947 ret = -EFAULT; 948 goto error; 949 } 950 } 951 ret = insn.n; 952 953error: 954 kfree(data); 955 956 return ret; 957} 958 959/* 960 COMEDI_CMD 961 command ioctl 962 963 arg: 964 pointer to cmd structure 965 966 reads: 967 cmd structure at arg 968 channel/range list 969 970 writes: 971 modified cmd structure at arg 972 973*/ 974static int do_cmd_ioctl(comedi_device *dev, void *arg, void *file) 975{ 976 comedi_cmd user_cmd; 977 comedi_subdevice *s; 978 comedi_async *async; 979 int ret = 0; 980 unsigned int *chanlist_saver = NULL; 981 982 if (copy_from_user(&user_cmd, arg, sizeof(comedi_cmd))) { 983 DPRINTK("bad cmd address\n"); 984 return -EFAULT; 985 } 986 /* save user's chanlist pointer so it can be restored later */ 987 chanlist_saver = user_cmd.chanlist; 988 989 if (user_cmd.subdev >= dev->n_subdevices) { 990 DPRINTK("%d no such subdevice\n", user_cmd.subdev); 991 return -ENODEV; 992 } 993 994 s = dev->subdevices + user_cmd.subdev; 995 async = s->async; 996 997 if (s->type == COMEDI_SUBD_UNUSED) { 998 DPRINTK("%d not valid subdevice\n", user_cmd.subdev); 999 return -EIO; 1000 } 1001 1002 if (!s->do_cmd || !s->do_cmdtest || !s->async) { 1003 DPRINTK("subdevice %i does not support commands\n", 1004 user_cmd.subdev); 1005 return -EIO; 1006 } 1007 1008 /* are we locked? (ioctl lock) */ 1009 if (s->lock && s->lock != file) { 1010 DPRINTK("subdevice locked\n"); 1011 return -EACCES; 1012 } 1013 1014 /* are we busy? */ 1015 if (s->busy) { 1016 DPRINTK("subdevice busy\n"); 1017 return -EBUSY; 1018 } 1019 s->busy = file; 1020 1021 /* make sure channel/gain list isn't too long */ 1022 if (user_cmd.chanlist_len > s->len_chanlist) { 1023 DPRINTK("channel/gain list too long %u > %d\n", 1024 user_cmd.chanlist_len, s->len_chanlist); 1025 ret = -EINVAL; 1026 goto cleanup; 1027 } 1028 1029 /* make sure channel/gain list isn't too short */ 1030 if (user_cmd.chanlist_len < 1) { 1031 DPRINTK("channel/gain list too short %u < 1\n", 1032 user_cmd.chanlist_len); 1033 ret = -EINVAL; 1034 goto cleanup; 1035 } 1036 1037 kfree(async->cmd.chanlist); 1038 async->cmd = user_cmd; 1039 async->cmd.data = NULL; 1040 /* load channel/gain list */ 1041 async->cmd.chanlist = 1042 kmalloc(async->cmd.chanlist_len * sizeof(int), GFP_KERNEL); 1043 if (!async->cmd.chanlist) { 1044 DPRINTK("allocation failed\n"); 1045 ret = -ENOMEM; 1046 goto cleanup; 1047 } 1048 1049 if (copy_from_user(async->cmd.chanlist, user_cmd.chanlist, 1050 async->cmd.chanlist_len * sizeof(int))) { 1051 DPRINTK("fault reading chanlist\n"); 1052 ret = -EFAULT; 1053 goto cleanup; 1054 } 1055 1056 /* make sure each element in channel/gain list is valid */ 1057 ret = check_chanlist(s, async->cmd.chanlist_len, async->cmd.chanlist); 1058 if (ret < 0) { 1059 DPRINTK("bad chanlist\n"); 1060 goto cleanup; 1061 } 1062 1063 ret = s->do_cmdtest(dev, s, &async->cmd); 1064 1065 if (async->cmd.flags & TRIG_BOGUS || ret) { 1066 DPRINTK("test returned %d\n", ret); 1067 user_cmd = async->cmd; 1068 /* restore chanlist pointer before copying back */ 1069 user_cmd.chanlist = chanlist_saver; 1070 user_cmd.data = NULL; 1071 if (copy_to_user(arg, &user_cmd, sizeof(comedi_cmd))) { 1072 DPRINTK("fault writing cmd\n"); 1073 ret = -EFAULT; 1074 goto cleanup; 1075 } 1076 ret = -EAGAIN; 1077 goto cleanup; 1078 } 1079 1080 if (!async->prealloc_bufsz) { 1081 ret = -ENOMEM; 1082 DPRINTK("no buffer (?)\n"); 1083 goto cleanup; 1084 } 1085 1086 comedi_reset_async_buf(async); 1087 1088 async->cb_mask = 1089 COMEDI_CB_EOA | COMEDI_CB_BLOCK | COMEDI_CB_ERROR | 1090 COMEDI_CB_OVERFLOW; 1091 if (async->cmd.flags & TRIG_WAKE_EOS) 1092 async->cb_mask |= COMEDI_CB_EOS; 1093 1094 comedi_set_subdevice_runflags(s, ~0, SRF_USER | SRF_RUNNING); 1095 1096#ifdef CONFIG_COMEDI_RT 1097 if (async->cmd.flags & TRIG_RT) { 1098 if (comedi_switch_to_rt(dev) == 0) 1099 comedi_set_subdevice_runflags(s, SRF_RT, SRF_RT); 1100 } 1101#endif 1102 1103 ret = s->do_cmd(dev, s); 1104 if (ret == 0) 1105 return 0; 1106 1107cleanup: 1108 do_become_nonbusy(dev, s); 1109 1110 return ret; 1111} 1112 1113/* 1114 COMEDI_CMDTEST 1115 command testing ioctl 1116 1117 arg: 1118 pointer to cmd structure 1119 1120 reads: 1121 cmd structure at arg 1122 channel/range list 1123 1124 writes: 1125 modified cmd structure at arg 1126 1127*/ 1128static int do_cmdtest_ioctl(comedi_device *dev, void *arg, void *file) 1129{ 1130 comedi_cmd user_cmd; 1131 comedi_subdevice *s; 1132 int ret = 0; 1133 unsigned int *chanlist = NULL; 1134 unsigned int *chanlist_saver = NULL; 1135 1136 if (copy_from_user(&user_cmd, arg, sizeof(comedi_cmd))) { 1137 DPRINTK("bad cmd address\n"); 1138 return -EFAULT; 1139 } 1140 /* save user's chanlist pointer so it can be restored later */ 1141 chanlist_saver = user_cmd.chanlist; 1142 1143 if (user_cmd.subdev >= dev->n_subdevices) { 1144 DPRINTK("%d no such subdevice\n", user_cmd.subdev); 1145 return -ENODEV; 1146 } 1147 1148 s = dev->subdevices + user_cmd.subdev; 1149 if (s->type == COMEDI_SUBD_UNUSED) { 1150 DPRINTK("%d not valid subdevice\n", user_cmd.subdev); 1151 return -EIO; 1152 } 1153 1154 if (!s->do_cmd || !s->do_cmdtest) { 1155 DPRINTK("subdevice %i does not support commands\n", 1156 user_cmd.subdev); 1157 return -EIO; 1158 } 1159 1160 /* make sure channel/gain list isn't too long */ 1161 if (user_cmd.chanlist_len > s->len_chanlist) { 1162 DPRINTK("channel/gain list too long %d > %d\n", 1163 user_cmd.chanlist_len, s->len_chanlist); 1164 ret = -EINVAL; 1165 goto cleanup; 1166 } 1167 1168 /* load channel/gain list */ 1169 if (user_cmd.chanlist) { 1170 chanlist = 1171 kmalloc(user_cmd.chanlist_len * sizeof(int), GFP_KERNEL); 1172 if (!chanlist) { 1173 DPRINTK("allocation failed\n"); 1174 ret = -ENOMEM; 1175 goto cleanup; 1176 } 1177 1178 if (copy_from_user(chanlist, user_cmd.chanlist, 1179 user_cmd.chanlist_len * sizeof(int))) { 1180 DPRINTK("fault reading chanlist\n"); 1181 ret = -EFAULT; 1182 goto cleanup; 1183 } 1184 1185 /* make sure each element in channel/gain list is valid */ 1186 ret = check_chanlist(s, user_cmd.chanlist_len, chanlist); 1187 if (ret < 0) { 1188 DPRINTK("bad chanlist\n"); 1189 goto cleanup; 1190 } 1191 1192 user_cmd.chanlist = chanlist; 1193 } 1194 1195 ret = s->do_cmdtest(dev, s, &user_cmd); 1196 1197 /* restore chanlist pointer before copying back */ 1198 user_cmd.chanlist = chanlist_saver; 1199 1200 if (copy_to_user(arg, &user_cmd, sizeof(comedi_cmd))) { 1201 DPRINTK("bad cmd address\n"); 1202 ret = -EFAULT; 1203 goto cleanup; 1204 } 1205cleanup: 1206 kfree(chanlist); 1207 1208 return ret; 1209} 1210 1211/* 1212 COMEDI_LOCK 1213 lock subdevice 1214 1215 arg: 1216 subdevice number 1217 1218 reads: 1219 none 1220 1221 writes: 1222 none 1223 1224*/ 1225 1226static int do_lock_ioctl(comedi_device *dev, unsigned int arg, void *file) 1227{ 1228 int ret = 0; 1229 unsigned long flags; 1230 comedi_subdevice *s; 1231 1232 if (arg >= dev->n_subdevices) 1233 return -EINVAL; 1234 s = dev->subdevices + arg; 1235 1236 comedi_spin_lock_irqsave(&s->spin_lock, flags); 1237 if (s->busy || s->lock) 1238 ret = -EBUSY; 1239 else 1240 s->lock = file; 1241 comedi_spin_unlock_irqrestore(&s->spin_lock, flags); 1242 1243 if (ret < 0) 1244 return ret; 1245 1246#if 0 1247 if (s->lock_f) 1248 ret = s->lock_f(dev, s); 1249#endif 1250 1251 return ret; 1252} 1253 1254/* 1255 COMEDI_UNLOCK 1256 unlock subdevice 1257 1258 arg: 1259 subdevice number 1260 1261 reads: 1262 none 1263 1264 writes: 1265 none 1266 1267 This function isn't protected by the semaphore, since 1268 we already own the lock. 1269*/ 1270static int do_unlock_ioctl(comedi_device *dev, unsigned int arg, void *file) 1271{ 1272 comedi_subdevice *s; 1273 1274 if (arg >= dev->n_subdevices) 1275 return -EINVAL; 1276 s = dev->subdevices + arg; 1277 1278 if (s->busy) 1279 return -EBUSY; 1280 1281 if (s->lock && s->lock != file) 1282 return -EACCES; 1283 1284 if (s->lock == file) { 1285#if 0 1286 if (s->unlock) 1287 s->unlock(dev, s); 1288#endif 1289 1290 s->lock = NULL; 1291 } 1292 1293 return 0; 1294} 1295 1296/* 1297 COMEDI_CANCEL 1298 cancel acquisition ioctl 1299 1300 arg: 1301 subdevice number 1302 1303 reads: 1304 nothing 1305 1306 writes: 1307 nothing 1308 1309*/ 1310static int do_cancel_ioctl(comedi_device *dev, unsigned int arg, void *file) 1311{ 1312 comedi_subdevice *s; 1313 1314 if (arg >= dev->n_subdevices) 1315 return -EINVAL; 1316 s = dev->subdevices + arg; 1317 if (s->async == NULL) 1318 return -EINVAL; 1319 1320 if (s->lock && s->lock != file) 1321 return -EACCES; 1322 1323 if (!s->busy) 1324 return 0; 1325 1326 if (s->busy != file) 1327 return -EBUSY; 1328 1329 return do_cancel(dev, s); 1330} 1331 1332/* 1333 COMEDI_POLL ioctl 1334 instructs driver to synchronize buffers 1335 1336 arg: 1337 subdevice number 1338 1339 reads: 1340 nothing 1341 1342 writes: 1343 nothing 1344 1345*/ 1346static int do_poll_ioctl(comedi_device *dev, unsigned int arg, void *file) 1347{ 1348 comedi_subdevice *s; 1349 1350 if (arg >= dev->n_subdevices) 1351 return -EINVAL; 1352 s = dev->subdevices + arg; 1353 1354 if (s->lock && s->lock != file) 1355 return -EACCES; 1356 1357 if (!s->busy) 1358 return 0; 1359 1360 if (s->busy != file) 1361 return -EBUSY; 1362 1363 if (s->poll) 1364 return s->poll(dev, s); 1365 1366 return -EINVAL; 1367} 1368 1369static int do_cancel(comedi_device *dev, comedi_subdevice *s) 1370{ 1371 int ret = 0; 1372 1373 if ((comedi_get_subdevice_runflags(s) & SRF_RUNNING) && s->cancel) 1374 ret = s->cancel(dev, s); 1375 1376 do_become_nonbusy(dev, s); 1377 1378 return ret; 1379} 1380 1381void comedi_unmap(struct vm_area_struct *area) 1382{ 1383 comedi_async *async; 1384 comedi_device *dev; 1385 1386 async = area->vm_private_data; 1387 dev = async->subdevice->device; 1388 1389 mutex_lock(&dev->mutex); 1390 async->mmap_count--; 1391 mutex_unlock(&dev->mutex); 1392} 1393 1394static struct vm_operations_struct comedi_vm_ops = { 1395 .close = comedi_unmap, 1396}; 1397 1398static int comedi_mmap(struct file *file, struct vm_area_struct *vma) 1399{ 1400 const unsigned minor = iminor(file->f_dentry->d_inode); 1401 struct comedi_device_file_info *dev_file_info = 1402 comedi_get_device_file_info(minor); 1403 comedi_device *dev = dev_file_info->device; 1404 comedi_async *async = NULL; 1405 unsigned long start = vma->vm_start; 1406 unsigned long size; 1407 int n_pages; 1408 int i; 1409 int retval; 1410 comedi_subdevice *s; 1411 1412 mutex_lock(&dev->mutex); 1413 if (!dev->attached) { 1414 DPRINTK("no driver configured on comedi%i\n", dev->minor); 1415 retval = -ENODEV; 1416 goto done; 1417 } 1418 if (vma->vm_flags & VM_WRITE) 1419 s = comedi_get_write_subdevice(dev_file_info); 1420 else 1421 s = comedi_get_read_subdevice(dev_file_info); 1422 1423 if (s == NULL) { 1424 retval = -EINVAL; 1425 goto done; 1426 } 1427 async = s->async; 1428 if (async == NULL) { 1429 retval = -EINVAL; 1430 goto done; 1431 } 1432 1433 if (vma->vm_pgoff != 0) { 1434 DPRINTK("comedi: mmap() offset must be 0.\n"); 1435 retval = -EINVAL; 1436 goto done; 1437 } 1438 1439 size = vma->vm_end - vma->vm_start; 1440 if (size > async->prealloc_bufsz) { 1441 retval = -EFAULT; 1442 goto done; 1443 } 1444 if (size & (~PAGE_MASK)) { 1445 retval = -EFAULT; 1446 goto done; 1447 } 1448 1449 n_pages = size >> PAGE_SHIFT; 1450 for (i = 0; i < n_pages; ++i) { 1451 if (remap_pfn_range(vma, start, 1452 page_to_pfn(virt_to_page(async-> 1453 buf_page_list[i]. 1454 virt_addr)), 1455 PAGE_SIZE, PAGE_SHARED)) { 1456 retval = -EAGAIN; 1457 goto done; 1458 } 1459 start += PAGE_SIZE; 1460 } 1461 1462 vma->vm_ops = &comedi_vm_ops; 1463 vma->vm_private_data = async; 1464 1465 async->mmap_count++; 1466 1467 retval = 0; 1468done: 1469 mutex_unlock(&dev->mutex); 1470 return retval; 1471} 1472 1473static unsigned int comedi_poll(struct file *file, poll_table *wait) 1474{ 1475 unsigned int mask = 0; 1476 const unsigned minor = iminor(file->f_dentry->d_inode); 1477 struct comedi_device_file_info *dev_file_info = 1478 comedi_get_device_file_info(minor); 1479 comedi_device *dev = dev_file_info->device; 1480 comedi_subdevice *read_subdev; 1481 comedi_subdevice *write_subdev; 1482 1483 mutex_lock(&dev->mutex); 1484 if (!dev->attached) { 1485 DPRINTK("no driver configured on comedi%i\n", dev->minor); 1486 mutex_unlock(&dev->mutex); 1487 return 0; 1488 } 1489 1490 mask = 0; 1491 read_subdev = comedi_get_read_subdevice(dev_file_info); 1492 if (read_subdev) { 1493 poll_wait(file, &read_subdev->async->wait_head, wait); 1494 if (!read_subdev->busy 1495 || comedi_buf_read_n_available(read_subdev->async) > 0 1496 || !(comedi_get_subdevice_runflags(read_subdev) & 1497 SRF_RUNNING)) { 1498 mask |= POLLIN | POLLRDNORM; 1499 } 1500 } 1501 write_subdev = comedi_get_write_subdevice(dev_file_info); 1502 if (write_subdev) { 1503 poll_wait(file, &write_subdev->async->wait_head, wait); 1504 comedi_buf_write_alloc(write_subdev->async, 1505 write_subdev->async->prealloc_bufsz); 1506 if (!write_subdev->busy 1507 || !(comedi_get_subdevice_runflags(write_subdev) & 1508 SRF_RUNNING) 1509 || comedi_buf_write_n_allocated(write_subdev->async) >= 1510 bytes_per_sample(write_subdev->async->subdevice)) { 1511 mask |= POLLOUT | POLLWRNORM; 1512 } 1513 } 1514 1515 mutex_unlock(&dev->mutex); 1516 return mask; 1517} 1518 1519static ssize_t comedi_write(struct file *file, const char *buf, size_t nbytes, 1520 loff_t *offset) 1521{ 1522 comedi_subdevice *s; 1523 comedi_async *async; 1524 int n, m, count = 0, retval = 0; 1525 DECLARE_WAITQUEUE(wait, current); 1526 const unsigned minor = iminor(file->f_dentry->d_inode); 1527 struct comedi_device_file_info *dev_file_info = 1528 comedi_get_device_file_info(minor); 1529 comedi_device *dev = dev_file_info->device; 1530 1531 if (!dev->attached) { 1532 DPRINTK("no driver configured on comedi%i\n", dev->minor); 1533 retval = -ENODEV; 1534 goto done; 1535 } 1536 1537 s = comedi_get_write_subdevice(dev_file_info); 1538 if (s == NULL) { 1539 retval = -EIO; 1540 goto done; 1541 } 1542 async = s->async; 1543 1544 if (!nbytes) { 1545 retval = 0; 1546 goto done; 1547 } 1548 if (!s->busy) { 1549 retval = 0; 1550 goto done; 1551 } 1552 if (s->busy != file) { 1553 retval = -EACCES; 1554 goto done; 1555 } 1556 add_wait_queue(&async->wait_head, &wait); 1557 while (nbytes > 0 && !retval) { 1558 set_current_state(TASK_INTERRUPTIBLE); 1559 1560 n = nbytes; 1561 1562 m = n; 1563 if (async->buf_write_ptr + m > async->prealloc_bufsz) 1564 m = async->prealloc_bufsz - async->buf_write_ptr; 1565 comedi_buf_write_alloc(async, async->prealloc_bufsz); 1566 if (m > comedi_buf_write_n_allocated(async)) 1567 m = comedi_buf_write_n_allocated(async); 1568 if (m < n) 1569 n = m; 1570 1571 if (n == 0) { 1572 if (!(comedi_get_subdevice_runflags(s) & SRF_RUNNING)) { 1573 if (comedi_get_subdevice_runflags(s) & 1574 SRF_ERROR) { 1575 retval = -EPIPE; 1576 } else { 1577 retval = 0; 1578 } 1579 do_become_nonbusy(dev, s); 1580 break; 1581 } 1582 if (file->f_flags & O_NONBLOCK) { 1583 retval = -EAGAIN; 1584 break; 1585 } 1586 if (signal_pending(current)) { 1587 retval = -ERESTARTSYS; 1588 break; 1589 } 1590 schedule(); 1591 if (!s->busy) 1592 break; 1593 if (s->busy != file) { 1594 retval = -EACCES; 1595 break; 1596 } 1597 continue; 1598 } 1599 1600 m = copy_from_user(async->prealloc_buf + async->buf_write_ptr, 1601 buf, n); 1602 if (m) { 1603 n -= m; 1604 retval = -EFAULT; 1605 } 1606 comedi_buf_write_free(async, n); 1607 1608 count += n; 1609 nbytes -= n; 1610 1611 buf += n; 1612 break; /* makes device work like a pipe */ 1613 } 1614 set_current_state(TASK_RUNNING); 1615 remove_wait_queue(&async->wait_head, &wait); 1616 1617done: 1618 return count ? count : retval; 1619} 1620 1621static ssize_t comedi_read(struct file *file, char *buf, size_t nbytes, 1622 loff_t *offset) 1623{ 1624 comedi_subdevice *s; 1625 comedi_async *async; 1626 int n, m, count = 0, retval = 0; 1627 DECLARE_WAITQUEUE(wait, current); 1628 const unsigned minor = iminor(file->f_dentry->d_inode); 1629 struct comedi_device_file_info *dev_file_info = 1630 comedi_get_device_file_info(minor); 1631 comedi_device *dev = dev_file_info->device; 1632 1633 if (!dev->attached) { 1634 DPRINTK("no driver configured on comedi%i\n", dev->minor); 1635 retval = -ENODEV; 1636 goto done; 1637 } 1638 1639 s = comedi_get_read_subdevice(dev_file_info); 1640 if (s == NULL) { 1641 retval = -EIO; 1642 goto done; 1643 } 1644 async = s->async; 1645 if (!nbytes) { 1646 retval = 0; 1647 goto done; 1648 } 1649 if (!s->busy) { 1650 retval = 0; 1651 goto done; 1652 } 1653 if (s->busy != file) { 1654 retval = -EACCES; 1655 goto done; 1656 } 1657 1658 add_wait_queue(&async->wait_head, &wait); 1659 while (nbytes > 0 && !retval) { 1660 set_current_state(TASK_INTERRUPTIBLE); 1661 1662 n = nbytes; 1663 1664 m = comedi_buf_read_n_available(async); 1665 /* printk("%d available\n",m); */ 1666 if (async->buf_read_ptr + m > async->prealloc_bufsz) 1667 m = async->prealloc_bufsz - async->buf_read_ptr; 1668 /* printk("%d contiguous\n",m); */ 1669 if (m < n) 1670 n = m; 1671 1672 if (n == 0) { 1673 if (!(comedi_get_subdevice_runflags(s) & SRF_RUNNING)) { 1674 do_become_nonbusy(dev, s); 1675 if (comedi_get_subdevice_runflags(s) & 1676 SRF_ERROR) { 1677 retval = -EPIPE; 1678 } else { 1679 retval = 0; 1680 } 1681 break; 1682 } 1683 if (file->f_flags & O_NONBLOCK) { 1684 retval = -EAGAIN; 1685 break; 1686 } 1687 if (signal_pending(current)) { 1688 retval = -ERESTARTSYS; 1689 break; 1690 } 1691 schedule(); 1692 if (!s->busy) { 1693 retval = 0; 1694 break; 1695 } 1696 if (s->busy != file) { 1697 retval = -EACCES; 1698 break; 1699 } 1700 continue; 1701 } 1702 m = copy_to_user(buf, async->prealloc_buf + 1703 async->buf_read_ptr, n); 1704 if (m) { 1705 n -= m; 1706 retval = -EFAULT; 1707 } 1708 1709 comedi_buf_read_alloc(async, n); 1710 comedi_buf_read_free(async, n); 1711 1712 count += n; 1713 nbytes -= n; 1714 1715 buf += n; 1716 break; /* makes device work like a pipe */ 1717 } 1718 if (!(comedi_get_subdevice_runflags(s) & (SRF_ERROR | SRF_RUNNING)) && 1719 async->buf_read_count - async->buf_write_count == 0) { 1720 do_become_nonbusy(dev, s); 1721 } 1722 set_current_state(TASK_RUNNING); 1723 remove_wait_queue(&async->wait_head, &wait); 1724 1725done: 1726 return count ? count : retval; 1727} 1728 1729/* 1730 This function restores a subdevice to an idle state. 1731 */ 1732void do_become_nonbusy(comedi_device *dev, comedi_subdevice *s) 1733{ 1734 comedi_async *async = s->async; 1735 1736 comedi_set_subdevice_runflags(s, SRF_RUNNING, 0); 1737#ifdef CONFIG_COMEDI_RT 1738 if (comedi_get_subdevice_runflags(s) & SRF_RT) { 1739 comedi_switch_to_non_rt(dev); 1740 comedi_set_subdevice_runflags(s, SRF_RT, 0); 1741 } 1742#endif 1743 if (async) { 1744 comedi_reset_async_buf(async); 1745 async->inttrig = NULL; 1746 } else { 1747 printk(KERN_ERR 1748 "BUG: (?) do_become_nonbusy called with async=0\n"); 1749 } 1750 1751 s->busy = NULL; 1752} 1753 1754static int comedi_open(struct inode *inode, struct file *file) 1755{ 1756 char mod[32]; 1757 const unsigned minor = iminor(inode); 1758 struct comedi_device_file_info *dev_file_info = 1759 comedi_get_device_file_info(minor); 1760 comedi_device *dev = dev_file_info->device; 1761 if (dev == NULL) { 1762 DPRINTK("invalid minor number\n"); 1763 return -ENODEV; 1764 } 1765 1766 /* This is slightly hacky, but we want module autoloading 1767 * to work for root. 1768 * case: user opens device, attached -> ok 1769 * case: user opens device, unattached, in_request_module=0 -> autoload 1770 * case: user opens device, unattached, in_request_module=1 -> fail 1771 * case: root opens device, attached -> ok 1772 * case: root opens device, unattached, in_request_module=1 -> ok 1773 * (typically called from modprobe) 1774 * case: root opens device, unattached, in_request_module=0 -> autoload 1775 * 1776 * The last could be changed to "-> ok", which would deny root 1777 * autoloading. 1778 */ 1779 mutex_lock(&dev->mutex); 1780 if (dev->attached) 1781 goto ok; 1782 if (!capable(CAP_SYS_MODULE) && dev->in_request_module) { 1783 DPRINTK("in request module\n"); 1784 mutex_unlock(&dev->mutex); 1785 return -ENODEV; 1786 } 1787 if (capable(CAP_SYS_MODULE) && dev->in_request_module) 1788 goto ok; 1789 1790 dev->in_request_module = 1; 1791 1792 sprintf(mod, "char-major-%i-%i", COMEDI_MAJOR, dev->minor); 1793#ifdef CONFIG_KMOD 1794 mutex_unlock(&dev->mutex); 1795 request_module(mod); 1796 mutex_lock(&dev->mutex); 1797#endif 1798 1799 dev->in_request_module = 0; 1800 1801 if (!dev->attached && !capable(CAP_SYS_MODULE)) { 1802 DPRINTK("not attached and not CAP_SYS_MODULE\n"); 1803 mutex_unlock(&dev->mutex); 1804 return -ENODEV; 1805 } 1806ok: 1807 __module_get(THIS_MODULE); 1808 1809 if (dev->attached) { 1810 if (!try_module_get(dev->driver->module)) { 1811 module_put(THIS_MODULE); 1812 mutex_unlock(&dev->mutex); 1813 return -ENOSYS; 1814 } 1815 } 1816 1817 if (dev->attached && dev->use_count == 0 && dev->open) 1818 dev->open(dev); 1819 1820 dev->use_count++; 1821 1822 mutex_unlock(&dev->mutex); 1823 1824 return 0; 1825} 1826 1827static int comedi_close(struct inode *inode, struct file *file) 1828{ 1829 const unsigned minor = iminor(inode); 1830 struct comedi_device_file_info *dev_file_info = 1831 comedi_get_device_file_info(minor); 1832 comedi_device *dev = dev_file_info->device; 1833 comedi_subdevice *s = NULL; 1834 int i; 1835 1836 mutex_lock(&dev->mutex); 1837 1838 if (dev->subdevices) { 1839 for (i = 0; i < dev->n_subdevices; i++) { 1840 s = dev->subdevices + i; 1841 1842 if (s->busy == file) 1843 do_cancel(dev, s); 1844 if (s->lock == file) 1845 s->lock = NULL; 1846 } 1847 } 1848 if (dev->attached && dev->use_count == 1 && dev->close) 1849 dev->close(dev); 1850 1851 module_put(THIS_MODULE); 1852 if (dev->attached) 1853 module_put(dev->driver->module); 1854 1855 dev->use_count--; 1856 1857 mutex_unlock(&dev->mutex); 1858 1859 if (file->f_flags & FASYNC) 1860 comedi_fasync(-1, file, 0); 1861 1862 return 0; 1863} 1864 1865static int comedi_fasync(int fd, struct file *file, int on) 1866{ 1867 const unsigned minor = iminor(file->f_dentry->d_inode); 1868 struct comedi_device_file_info *dev_file_info = 1869 comedi_get_device_file_info(minor); 1870 1871 comedi_device *dev = dev_file_info->device; 1872 1873 return fasync_helper(fd, file, on, &dev->async_queue); 1874} 1875 1876const struct file_operations comedi_fops = { 1877 .owner = THIS_MODULE, 1878#ifdef HAVE_UNLOCKED_IOCTL 1879 .unlocked_ioctl = comedi_unlocked_ioctl, 1880#else 1881 .ioctl = comedi_ioctl, 1882#endif 1883#ifdef HAVE_COMPAT_IOCTL 1884 .compat_ioctl = comedi_compat_ioctl, 1885#endif 1886 .open = comedi_open, 1887 .release = comedi_close, 1888 .read = comedi_read, 1889 .write = comedi_write, 1890 .mmap = comedi_mmap, 1891 .poll = comedi_poll, 1892 .fasync = comedi_fasync, 1893}; 1894 1895struct class *comedi_class; 1896static struct cdev comedi_cdev; 1897 1898static void comedi_cleanup_legacy_minors(void) 1899{ 1900 unsigned i; 1901 1902 for (i = 0; i < comedi_num_legacy_minors; i++) 1903 comedi_free_board_minor(i); 1904} 1905 1906static int __init comedi_init(void) 1907{ 1908 int i; 1909 int retval; 1910 1911 printk(KERN_INFO "comedi: version " COMEDI_RELEASE 1912 " - http://www.comedi.org\n"); 1913 1914 if (comedi_num_legacy_minors < 0 || 1915 comedi_num_legacy_minors > COMEDI_NUM_BOARD_MINORS) { 1916 printk(KERN_ERR "comedi: error: invalid value for module " 1917 "parameter \"comedi_num_legacy_minors\". Valid values " 1918 "are 0 through %i.\n", COMEDI_NUM_BOARD_MINORS); 1919 return -EINVAL; 1920 } 1921 1922 /* 1923 * comedi is unusable if both comedi_autoconfig and 1924 * comedi_num_legacy_minors are zero, so we might as well adjust the 1925 * defaults in that case 1926 */ 1927 if (comedi_autoconfig == 0 && comedi_num_legacy_minors == 0) 1928 comedi_num_legacy_minors = 16; 1929 1930 memset(comedi_file_info_table, 0, 1931 sizeof(struct comedi_device_file_info *) * COMEDI_NUM_MINORS); 1932 1933 retval = register_chrdev_region(MKDEV(COMEDI_MAJOR, 0), 1934 COMEDI_NUM_MINORS, "comedi"); 1935 if (retval) 1936 return -EIO; 1937 cdev_init(&comedi_cdev, &comedi_fops); 1938 comedi_cdev.owner = THIS_MODULE; 1939 kobject_set_name(&comedi_cdev.kobj, "comedi"); 1940 if (cdev_add(&comedi_cdev, MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS)) { 1941 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0), 1942 COMEDI_NUM_MINORS); 1943 return -EIO; 1944 } 1945 comedi_class = class_create(THIS_MODULE, "comedi"); 1946 if (IS_ERR(comedi_class)) { 1947 printk("comedi: failed to create class"); 1948 cdev_del(&comedi_cdev); 1949 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0), 1950 COMEDI_NUM_MINORS); 1951 return PTR_ERR(comedi_class); 1952 } 1953 1954 /* XXX requires /proc interface */ 1955 comedi_proc_init(); 1956 1957 /* create devices files for legacy/manual use */ 1958 for (i = 0; i < comedi_num_legacy_minors; i++) { 1959 int minor; 1960 minor = comedi_alloc_board_minor(NULL); 1961 if (minor < 0) { 1962 comedi_cleanup_legacy_minors(); 1963 cdev_del(&comedi_cdev); 1964 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0), 1965 COMEDI_NUM_MINORS); 1966 return minor; 1967 } 1968 } 1969 1970 comedi_rt_init(); 1971 1972 comedi_register_ioctl32(); 1973 1974 return 0; 1975} 1976 1977static void __exit comedi_cleanup(void) 1978{ 1979 int i; 1980 1981 comedi_cleanup_legacy_minors(); 1982 for (i = 0; i < COMEDI_NUM_MINORS; ++i) 1983 BUG_ON(comedi_file_info_table[i]); 1984 1985 1986 class_destroy(comedi_class); 1987 cdev_del(&comedi_cdev); 1988 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS); 1989 1990 comedi_proc_cleanup(); 1991 1992 comedi_rt_cleanup(); 1993 1994 comedi_unregister_ioctl32(); 1995} 1996 1997module_init(comedi_init); 1998module_exit(comedi_cleanup); 1999 2000void comedi_error(const comedi_device *dev, const char *s) 2001{ 2002 rt_printk("comedi%d: %s: %s\n", dev->minor, dev->driver->driver_name, 2003 s); 2004} 2005 2006void comedi_event(comedi_device *dev, comedi_subdevice *s) 2007{ 2008 comedi_async *async = s->async; 2009 unsigned runflags = 0; 2010 unsigned runflags_mask = 0; 2011 2012 /* DPRINTK("comedi_event 0x%x\n",mask); */ 2013 2014 if ((comedi_get_subdevice_runflags(s) & SRF_RUNNING) == 0) 2015 return; 2016 2017 if (s->async-> 2018 events & (COMEDI_CB_EOA | COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)) { 2019 runflags_mask |= SRF_RUNNING; 2020 } 2021 /* remember if an error event has occured, so an error 2022 * can be returned the next time the user does a read() */ 2023 if (s->async->events & (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)) { 2024 runflags_mask |= SRF_ERROR; 2025 runflags |= SRF_ERROR; 2026 } 2027 if (runflags_mask) { 2028 /*sets SRF_ERROR and SRF_RUNNING together atomically */ 2029 comedi_set_subdevice_runflags(s, runflags_mask, runflags); 2030 } 2031 2032 if (async->cb_mask & s->async->events) { 2033 if (comedi_get_subdevice_runflags(s) & SRF_USER) { 2034 2035 if (dev->rt) { 2036#ifdef CONFIG_COMEDI_RT 2037 /* pend wake up */ 2038 comedi_rt_pend_wakeup(&async->wait_head); 2039#else 2040 printk 2041 ("BUG: comedi_event() code unreachable\n"); 2042#endif 2043 } else { 2044 wake_up_interruptible(&async->wait_head); 2045 if (s->subdev_flags & SDF_CMD_READ) { 2046 kill_fasync(&dev->async_queue, SIGIO, 2047 POLL_IN); 2048 } 2049 if (s->subdev_flags & SDF_CMD_WRITE) { 2050 kill_fasync(&dev->async_queue, SIGIO, 2051 POLL_OUT); 2052 } 2053 } 2054 } else { 2055 if (async->cb_func) 2056 async->cb_func(s->async->events, async->cb_arg); 2057 /* XXX bug here. If subdevice A is rt, and 2058 * subdevice B tries to callback to a normal 2059 * linux kernel function, it will be at the 2060 * wrong priority. Since this isn't very 2061 * common, I'm not going to worry about it. */ 2062 } 2063 } 2064 s->async->events = 0; 2065} 2066 2067void comedi_set_subdevice_runflags(comedi_subdevice *s, unsigned mask, 2068 unsigned bits) 2069{ 2070 unsigned long flags; 2071 2072 comedi_spin_lock_irqsave(&s->spin_lock, flags); 2073 s->runflags &= ~mask; 2074 s->runflags |= (bits & mask); 2075 comedi_spin_unlock_irqrestore(&s->spin_lock, flags); 2076} 2077 2078unsigned comedi_get_subdevice_runflags(comedi_subdevice *s) 2079{ 2080 unsigned long flags; 2081 unsigned runflags; 2082 2083 comedi_spin_lock_irqsave(&s->spin_lock, flags); 2084 runflags = s->runflags; 2085 comedi_spin_unlock_irqrestore(&s->spin_lock, flags); 2086 return runflags; 2087} 2088 2089static int is_device_busy(comedi_device *dev) 2090{ 2091 comedi_subdevice *s; 2092 int i; 2093 2094 if (!dev->attached) 2095 return 0; 2096 2097 for (i = 0; i < dev->n_subdevices; i++) { 2098 s = dev->subdevices + i; 2099 if (s->busy) 2100 return 1; 2101 if (s->async && s->async->mmap_count) 2102 return 1; 2103 } 2104 2105 return 0; 2106} 2107 2108void comedi_device_init(comedi_device *dev) 2109{ 2110 memset(dev, 0, sizeof(comedi_device)); 2111 spin_lock_init(&dev->spinlock); 2112 mutex_init(&dev->mutex); 2113 dev->minor = -1; 2114} 2115 2116void comedi_device_cleanup(comedi_device *dev) 2117{ 2118 if (dev == NULL) 2119 return; 2120 mutex_lock(&dev->mutex); 2121 comedi_device_detach(dev); 2122 mutex_unlock(&dev->mutex); 2123 mutex_destroy(&dev->mutex); 2124} 2125 2126int comedi_alloc_board_minor(struct device *hardware_device) 2127{ 2128 unsigned long flags; 2129 struct comedi_device_file_info *info; 2130 device_create_result_type *csdev; 2131 unsigned i; 2132 2133 info = kzalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL); 2134 if (info == NULL) 2135 return -ENOMEM; 2136 info->device = kzalloc(sizeof(comedi_device), GFP_KERNEL); 2137 if (info->device == NULL) { 2138 kfree(info); 2139 return -ENOMEM; 2140 } 2141 comedi_device_init(info->device); 2142 comedi_spin_lock_irqsave(&comedi_file_info_table_lock, flags); 2143 for (i = 0; i < COMEDI_NUM_BOARD_MINORS; ++i) { 2144 if (comedi_file_info_table[i] == NULL) { 2145 comedi_file_info_table[i] = info; 2146 break; 2147 } 2148 } 2149 comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags); 2150 if (i == COMEDI_NUM_BOARD_MINORS) { 2151 comedi_device_cleanup(info->device); 2152 kfree(info->device); 2153 kfree(info); 2154 rt_printk 2155 ("comedi: error: ran out of minor numbers for board device files.\n"); 2156 return -EBUSY; 2157 } 2158 info->device->minor = i; 2159 csdev = COMEDI_DEVICE_CREATE(comedi_class, NULL, 2160 MKDEV(COMEDI_MAJOR, i), NULL, 2161 hardware_device, "comedi%i", i); 2162 if (!IS_ERR(csdev)) 2163 info->device->class_dev = csdev; 2164 2165 return i; 2166} 2167 2168void comedi_free_board_minor(unsigned minor) 2169{ 2170 unsigned long flags; 2171 struct comedi_device_file_info *info; 2172 2173 BUG_ON(minor >= COMEDI_NUM_BOARD_MINORS); 2174 comedi_spin_lock_irqsave(&comedi_file_info_table_lock, flags); 2175 info = comedi_file_info_table[minor]; 2176 comedi_file_info_table[minor] = NULL; 2177 comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags); 2178 2179 if (info) { 2180 comedi_device *dev = info->device; 2181 if (dev) { 2182 if (dev->class_dev) { 2183 device_destroy(comedi_class, 2184 MKDEV(COMEDI_MAJOR, dev->minor)); 2185 } 2186 comedi_device_cleanup(dev); 2187 kfree(dev); 2188 } 2189 kfree(info); 2190 } 2191} 2192 2193int comedi_alloc_subdevice_minor(comedi_device *dev, comedi_subdevice *s) 2194{ 2195 unsigned long flags; 2196 struct comedi_device_file_info *info; 2197 device_create_result_type *csdev; 2198 unsigned i; 2199 2200 info = kmalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL); 2201 if (info == NULL) 2202 return -ENOMEM; 2203 info->device = dev; 2204 info->read_subdevice = s; 2205 info->write_subdevice = s; 2206 comedi_spin_lock_irqsave(&comedi_file_info_table_lock, flags); 2207 for (i = COMEDI_FIRST_SUBDEVICE_MINOR; i < COMEDI_NUM_MINORS; ++i) { 2208 if (comedi_file_info_table[i] == NULL) { 2209 comedi_file_info_table[i] = info; 2210 break; 2211 } 2212 } 2213 comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags); 2214 if (i == COMEDI_NUM_MINORS) { 2215 kfree(info); 2216 rt_printk 2217 ("comedi: error: ran out of minor numbers for board device files.\n"); 2218 return -EBUSY; 2219 } 2220 s->minor = i; 2221 csdev = COMEDI_DEVICE_CREATE(comedi_class, dev->class_dev, 2222 MKDEV(COMEDI_MAJOR, i), NULL, NULL, 2223 "comedi%i_subd%i", dev->minor, 2224 (int)(s - dev->subdevices)); 2225 if (!IS_ERR(csdev)) 2226 s->class_dev = csdev; 2227 2228 return i; 2229} 2230 2231void comedi_free_subdevice_minor(comedi_subdevice *s) 2232{ 2233 unsigned long flags; 2234 struct comedi_device_file_info *info; 2235 2236 if (s == NULL) 2237 return; 2238 if (s->minor < 0) 2239 return; 2240 2241 BUG_ON(s->minor >= COMEDI_NUM_MINORS); 2242 BUG_ON(s->minor < COMEDI_FIRST_SUBDEVICE_MINOR); 2243 2244 comedi_spin_lock_irqsave(&comedi_file_info_table_lock, flags); 2245 info = comedi_file_info_table[s->minor]; 2246 comedi_file_info_table[s->minor] = NULL; 2247 comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags); 2248 2249 if (s->class_dev) { 2250 device_destroy(comedi_class, MKDEV(COMEDI_MAJOR, s->minor)); 2251 s->class_dev = NULL; 2252 } 2253 kfree(info); 2254} 2255 2256struct comedi_device_file_info *comedi_get_device_file_info(unsigned minor) 2257{ 2258 unsigned long flags; 2259 struct comedi_device_file_info *info; 2260 2261 BUG_ON(minor >= COMEDI_NUM_MINORS); 2262 comedi_spin_lock_irqsave(&comedi_file_info_table_lock, flags); 2263 info = comedi_file_info_table[minor]; 2264 comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags); 2265 return info; 2266} 2267