]> bbs.cooldavid.org Git - net-next-2.6.git/blame - drivers/staging/comedi/comedi_fops.c
Staging: comedi: Added some validation of comedi module parameter values.
[net-next-2.6.git] / drivers / staging / comedi / comedi_fops.c
CommitLineData
ed9eccbe
DS
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
476b8477
GKH
48#include <linux/io.h>
49#include <linux/uaccess.h>
ed9eccbe 50
476b8477 51/* #include "kvmem.h" */
ed9eccbe
DS
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
6a9d7a21
IA
62int comedi_autoconfig = 1;
63module_param(comedi_autoconfig, bool, 0444);
64
1dd33ab8
BP
65int comedi_num_legacy_minors = 0;
66module_param(comedi_num_legacy_minors, int, 0444);
67
ed9eccbe 68static DEFINE_SPINLOCK(comedi_file_info_table_lock);
476b8477
GKH
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);
ed9eccbe
DS
91
92static int comedi_fasync(int fd, struct file *file, int on);
93
476b8477 94static int is_device_busy(comedi_device *dev);
ed9eccbe
DS
95
96#ifdef HAVE_UNLOCKED_IOCTL
97static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
476b8477 98 unsigned long arg)
ed9eccbe
DS
99#else
100static int comedi_ioctl(struct inode *inode, struct file *file,
476b8477 101 unsigned int cmd, unsigned long arg)
ed9eccbe
DS
102#endif
103{
104 const unsigned minor = iminor(file->f_dentry->d_inode);
476b8477
GKH
105 struct comedi_device_file_info *dev_file_info =
106 comedi_get_device_file_info(minor);
ed9eccbe
DS
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
476b8477 173done:
ed9eccbe
DS
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*/
476b8477 191static int do_devconfig_ioctl(comedi_device *dev, comedi_devconfig *arg)
ed9eccbe
DS
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;
476b8477 204 if (dev->attached) {
ed9eccbe
DS
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) &&
476b8477 218 it.options[COMEDI_DEVCONF_AUX_DATA_LENGTH]) {
ed9eccbe
DS
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,
476b8477 229 comedi_aux_data(it.options, 0), aux_len)) {
ed9eccbe
DS
230 vfree(aux_data);
231 return -EFAULT;
232 }
233 it.options[COMEDI_DEVCONF_AUX_DATA_LO] =
476b8477 234 (unsigned long)aux_data;
ed9eccbe
DS
235 if (sizeof(void *) > sizeof(int)) {
236 bit_shift = sizeof(int) * 8;
237 it.options[COMEDI_DEVCONF_AUX_DATA_HI] =
476b8477 238 ((unsigned long)aux_data) >> bit_shift;
ed9eccbe
DS
239 } else
240 it.options[COMEDI_DEVCONF_AUX_DATA_HI] = 0;
241 }
242
243 ret = comedi_device_attach(dev, &it);
476b8477
GKH
244 if (ret == 0) {
245 if (!try_module_get(dev->driver->module)) {
ed9eccbe
DS
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*/
476b8477 271static int do_bufconfig_ioctl(comedi_device *dev, void *arg)
ed9eccbe
DS
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
476b8477 338copyback:
ed9eccbe
DS
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*/
476b8477
GKH
359static int do_devinfo_ioctl(comedi_device *dev, comedi_devinfo *arg,
360 struct file *file)
ed9eccbe
DS
361{
362 comedi_devinfo devinfo;
363 const unsigned minor = iminor(file->f_dentry->d_inode);
476b8477
GKH
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);
ed9eccbe
DS
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
476b8477 379 if (read_subdev)
ed9eccbe 380 devinfo.read_subdevice = read_subdev - dev->subdevices;
476b8477 381 else
ed9eccbe 382 devinfo.read_subdevice = -1;
476b8477
GKH
383
384 if (write_subdev)
ed9eccbe 385 devinfo.write_subdevice = write_subdev - dev->subdevices;
476b8477 386 else
ed9eccbe 387 devinfo.write_subdevice = -1;
ed9eccbe
DS
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*/
476b8477
GKH
409static int do_subdinfo_ioctl(comedi_device *dev, comedi_subdinfo *arg,
410 void *file)
ed9eccbe
DS
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 =
476b8477 436 (i << 24) | (0 << 16) | (s->range_table->length);
ed9eccbe
DS
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,
476b8477 468 dev->n_subdevices * sizeof(comedi_subdinfo));
ed9eccbe
DS
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*/
476b8477 489static int do_chaninfo_ioctl(comedi_device *dev, comedi_chaninfo *arg)
ed9eccbe
DS
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,
476b8477 505 s->n_chan * sizeof(lsampl_t)))
ed9eccbe
DS
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,
476b8477 513 s->n_chan * sizeof(unsigned int)))
ed9eccbe
DS
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) |
476b8477 526 (s->range_table_list[i]->length);
ed9eccbe
DS
527 put_user(x, it.rangelist + i);
528 }
476b8477
GKH
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
ed9eccbe
DS
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 */
476b8477 553static int do_bufinfo_ioctl(comedi_device *dev, void *arg)
ed9eccbe
DS
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 |
476b8477
GKH
582 SRF_RUNNING))
583 && async->buf_write_count == async->buf_read_count) {
ed9eccbe
DS
584 do_become_nonbusy(dev, s);
585 }
586 }
587
588 if (bi.bytes_written && (s->subdev_flags & SDF_CMD_WRITE)) {
589 bi.bytes_written =
476b8477 590 comedi_buf_write_alloc(async, bi.bytes_written);
ed9eccbe
DS
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
476b8477 599copyback:
ed9eccbe
DS
600 if (copy_to_user(arg, &bi, sizeof(comedi_bufinfo)))
601 return -EFAULT;
602
603 return 0;
604}
605
476b8477
GKH
606static int parse_insn(comedi_device *dev, comedi_insn *insn, lsampl_t *data,
607 void *file);
ed9eccbe
DS
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
476b8477 625static int do_insnlist_ioctl(comedi_device *dev, void *arg, void *file)
ed9eccbe
DS
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,
476b8477 651 sizeof(comedi_insn) * insnlist.n_insns)) {
ed9eccbe
DS
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,
476b8477 665 insns[i].n * sizeof(lsampl_t))) {
ed9eccbe
DS
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,
476b8477 676 insns[i].n * sizeof(lsampl_t))) {
ed9eccbe
DS
677 DPRINTK("copy_to_user failed\n");
678 ret = -EFAULT;
679 goto error;
680 }
681 }
682 if (need_resched())
683 schedule();
684 }
685
476b8477
GKH
686error:
687 kfree(insns);
688 kfree(data);
ed9eccbe
DS
689
690 if (ret < 0)
691 return ret;
692 return i;
693}
694
476b8477 695static int check_insn_config_length(comedi_insn *insn, lsampl_t *data)
ed9eccbe 696{
476b8477
GKH
697 if (insn->n < 1)
698 return -EINVAL;
ed9eccbe
DS
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;
476b8477
GKH
742 /* by default we allow the insn since we don't have checks for
743 * all possible cases yet */
ed9eccbe 744 default:
476b8477
GKH
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);
ed9eccbe
DS
750 return 0;
751 break;
752 }
753 return -EINVAL;
754}
755
476b8477
GKH
756static int parse_insn(comedi_device *dev, comedi_insn *insn, lsampl_t *data,
757 void *file)
ed9eccbe
DS
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
476b8477
GKH
846 ret = check_chanlist(s, 1, &insn->chanspec);
847 if (ret < 0) {
ed9eccbe
DS
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
476b8477
GKH
865 ? s->maxdata_list[CR_CHAN(insn->chanspec)]
866 : s->maxdata;
ed9eccbe
DS
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
476b8477 898out:
ed9eccbe
DS
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 */
476b8477 916static int do_insn_ioctl(comedi_device *dev, void *arg, void *file)
ed9eccbe
DS
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
476b8477
GKH
953error:
954 kfree(data);
ed9eccbe
DS
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*/
476b8477 974static int do_cmd_ioctl(comedi_device *dev, void *arg, void *file)
ed9eccbe
DS
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 }
476b8477 986 /* save user's chanlist pointer so it can be restored later */
ed9eccbe
DS
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
476b8477 1037 kfree(async->cmd.chanlist);
ed9eccbe
DS
1038 async->cmd = user_cmd;
1039 async->cmd.data = NULL;
1040 /* load channel/gain list */
1041 async->cmd.chanlist =
476b8477 1042 kmalloc(async->cmd.chanlist_len * sizeof(int), GFP_KERNEL);
ed9eccbe
DS
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,
476b8477 1050 async->cmd.chanlist_len * sizeof(int))) {
ed9eccbe
DS
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 */
476b8477
GKH
1057 ret = check_chanlist(s, async->cmd.chanlist_len, async->cmd.chanlist);
1058 if (ret < 0) {
ed9eccbe
DS
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;
476b8477 1068 /* restore chanlist pointer before copying back */
ed9eccbe
DS
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 =
476b8477
GKH
1089 COMEDI_CB_EOA | COMEDI_CB_BLOCK | COMEDI_CB_ERROR |
1090 COMEDI_CB_OVERFLOW;
1091 if (async->cmd.flags & TRIG_WAKE_EOS)
ed9eccbe 1092 async->cb_mask |= COMEDI_CB_EOS;
ed9eccbe
DS
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
476b8477 1107cleanup:
ed9eccbe
DS
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*/
476b8477 1128static int do_cmdtest_ioctl(comedi_device *dev, void *arg, void *file)
ed9eccbe
DS
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 }
476b8477 1140 /* save user's chanlist pointer so it can be restored later */
ed9eccbe
DS
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 =
476b8477 1171 kmalloc(user_cmd.chanlist_len * sizeof(int), GFP_KERNEL);
ed9eccbe
DS
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,
476b8477 1179 user_cmd.chanlist_len * sizeof(int))) {
ed9eccbe
DS
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 */
476b8477
GKH
1186 ret = check_chanlist(s, user_cmd.chanlist_len, chanlist);
1187 if (ret < 0) {
ed9eccbe
DS
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
476b8477 1197 /* restore chanlist pointer before copying back */
ed9eccbe
DS
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 }
476b8477
GKH
1205cleanup:
1206 kfree(chanlist);
ed9eccbe
DS
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
476b8477 1226static int do_lock_ioctl(comedi_device *dev, unsigned int arg, void *file)
ed9eccbe
DS
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);
476b8477 1237 if (s->busy || s->lock)
ed9eccbe 1238 ret = -EBUSY;
476b8477 1239 else
ed9eccbe 1240 s->lock = file;
ed9eccbe
DS
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*/
476b8477 1270static int do_unlock_ioctl(comedi_device *dev, unsigned int arg, void *file)
ed9eccbe
DS
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*/
476b8477 1310static int do_cancel_ioctl(comedi_device *dev, unsigned int arg, void *file)
ed9eccbe
DS
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*/
476b8477 1346static int do_poll_ioctl(comedi_device *dev, unsigned int arg, void *file)
ed9eccbe
DS
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
476b8477 1369static int do_cancel(comedi_device *dev, comedi_subdevice *s)
ed9eccbe
DS
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 = {
476b8477 1395 .close = comedi_unmap,
ed9eccbe
DS
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);
476b8477
GKH
1401 struct comedi_device_file_info *dev_file_info =
1402 comedi_get_device_file_info(minor);
ed9eccbe
DS
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 }
476b8477 1418 if (vma->vm_flags & VM_WRITE)
ed9eccbe 1419 s = comedi_get_write_subdevice(dev_file_info);
476b8477 1420 else
ed9eccbe 1421 s = comedi_get_read_subdevice(dev_file_info);
476b8477 1422
ed9eccbe
DS
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,
476b8477
GKH
1452 page_to_pfn(virt_to_page(async->
1453 buf_page_list[i].
1454 virt_addr)),
1455 PAGE_SIZE, PAGE_SHARED)) {
ed9eccbe
DS
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;
476b8477 1468done:
ed9eccbe
DS
1469 mutex_unlock(&dev->mutex);
1470 return retval;
1471}
1472
476b8477 1473static unsigned int comedi_poll(struct file *file, poll_table *wait)
ed9eccbe
DS
1474{
1475 unsigned int mask = 0;
1476 const unsigned minor = iminor(file->f_dentry->d_inode);
476b8477
GKH
1477 struct comedi_device_file_info *dev_file_info =
1478 comedi_get_device_file_info(minor);
ed9eccbe
DS
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
476b8477
GKH
1495 || comedi_buf_read_n_available(read_subdev->async) > 0
1496 || !(comedi_get_subdevice_runflags(read_subdev) &
1497 SRF_RUNNING)) {
ed9eccbe
DS
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);
476b8477
GKH
1504 comedi_buf_write_alloc(write_subdev->async,
1505 write_subdev->async->prealloc_bufsz);
ed9eccbe 1506 if (!write_subdev->busy
476b8477
GKH
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)) {
ed9eccbe
DS
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,
476b8477 1520 loff_t *offset)
ed9eccbe
DS
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);
476b8477
GKH
1527 struct comedi_device_file_info *dev_file_info =
1528 comedi_get_device_file_info(minor);
ed9eccbe
DS
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;
476b8477 1563 if (async->buf_write_ptr + m > async->prealloc_bufsz)
ed9eccbe 1564 m = async->prealloc_bufsz - async->buf_write_ptr;
ed9eccbe 1565 comedi_buf_write_alloc(async, async->prealloc_bufsz);
476b8477 1566 if (m > comedi_buf_write_n_allocated(async))
ed9eccbe 1567 m = comedi_buf_write_n_allocated(async);
ed9eccbe
DS
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) &
476b8477 1574 SRF_ERROR) {
ed9eccbe
DS
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();
476b8477 1591 if (!s->busy)
ed9eccbe 1592 break;
ed9eccbe
DS
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,
476b8477 1601 buf, n);
ed9eccbe
DS
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:
476b8477 1618 return count ? count : retval;
ed9eccbe
DS
1619}
1620
1621static ssize_t comedi_read(struct file *file, char *buf, size_t nbytes,
476b8477 1622 loff_t *offset)
ed9eccbe
DS
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);
476b8477
GKH
1629 struct comedi_device_file_info *dev_file_info =
1630 comedi_get_device_file_info(minor);
ed9eccbe
DS
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);
476b8477
GKH
1665 /* printk("%d available\n",m); */
1666 if (async->buf_read_ptr + m > async->prealloc_bufsz)
ed9eccbe 1667 m = async->prealloc_bufsz - async->buf_read_ptr;
476b8477 1668 /* printk("%d contiguous\n",m); */
ed9eccbe
DS
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) &
476b8477 1676 SRF_ERROR) {
ed9eccbe
DS
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 +
476b8477 1703 async->buf_read_ptr, n);
ed9eccbe
DS
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)) &&
476b8477 1719 async->buf_read_count - async->buf_write_count == 0) {
ed9eccbe
DS
1720 do_become_nonbusy(dev, s);
1721 }
1722 set_current_state(TASK_RUNNING);
1723 remove_wait_queue(&async->wait_head, &wait);
1724
1725done:
476b8477 1726 return count ? count : retval;
ed9eccbe
DS
1727}
1728
1729/*
1730 This function restores a subdevice to an idle state.
1731 */
476b8477 1732void do_become_nonbusy(comedi_device *dev, comedi_subdevice *s)
ed9eccbe
DS
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 {
476b8477
GKH
1747 printk(KERN_ERR
1748 "BUG: (?) do_become_nonbusy called with async=0\n");
ed9eccbe
DS
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);
476b8477
GKH
1758 struct comedi_device_file_info *dev_file_info =
1759 comedi_get_device_file_info(minor);
ed9eccbe
DS
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
476b8477 1817 if (dev->attached && dev->use_count == 0 && dev->open)
ed9eccbe 1818 dev->open(dev);
ed9eccbe
DS
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);
476b8477
GKH
1830 struct comedi_device_file_info *dev_file_info =
1831 comedi_get_device_file_info(minor);
ed9eccbe
DS
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
476b8477 1842 if (s->busy == file)
ed9eccbe 1843 do_cancel(dev, s);
476b8477 1844 if (s->lock == file)
ed9eccbe 1845 s->lock = NULL;
ed9eccbe
DS
1846 }
1847 }
476b8477 1848 if (dev->attached && dev->use_count == 1 && dev->close)
ed9eccbe 1849 dev->close(dev);
ed9eccbe
DS
1850
1851 module_put(THIS_MODULE);
476b8477 1852 if (dev->attached)
ed9eccbe 1853 module_put(dev->driver->module);
ed9eccbe
DS
1854
1855 dev->use_count--;
1856
1857 mutex_unlock(&dev->mutex);
1858
476b8477 1859 if (file->f_flags & FASYNC)
ed9eccbe 1860 comedi_fasync(-1, file, 0);
ed9eccbe
DS
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);
476b8477
GKH
1868 struct comedi_device_file_info *dev_file_info =
1869 comedi_get_device_file_info(minor);
1870
ed9eccbe
DS
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 = {
476b8477 1877 .owner = THIS_MODULE,
ed9eccbe 1878#ifdef HAVE_UNLOCKED_IOCTL
476b8477 1879 .unlocked_ioctl = comedi_unlocked_ioctl,
ed9eccbe 1880#else
476b8477 1881 .ioctl = comedi_ioctl,
ed9eccbe
DS
1882#endif
1883#ifdef HAVE_COMPAT_IOCTL
476b8477 1884 .compat_ioctl = comedi_compat_ioctl,
ed9eccbe 1885#endif
476b8477
GKH
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,
ed9eccbe
DS
1893};
1894
476b8477 1895struct class *comedi_class;
ed9eccbe
DS
1896static struct cdev comedi_cdev;
1897
1898static void comedi_cleanup_legacy_minors(void)
1899{
1900 unsigned i;
476b8477 1901
1dd33ab8 1902 for (i = 0; i < comedi_num_legacy_minors; i++)
ed9eccbe 1903 comedi_free_board_minor(i);
ed9eccbe
DS
1904}
1905
1906static int __init comedi_init(void)
1907{
1908 int i;
1909 int retval;
1910
476b8477
GKH
1911 printk(KERN_INFO "comedi: version " COMEDI_RELEASE
1912 " - http://www.comedi.org\n");
ed9eccbe 1913
a3cb729e
FMH
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
476b8477
GKH
1930 memset(comedi_file_info_table, 0,
1931 sizeof(struct comedi_device_file_info *) * COMEDI_NUM_MINORS);
ed9eccbe
DS
1932
1933 retval = register_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
476b8477 1934 COMEDI_NUM_MINORS, "comedi");
ed9eccbe
DS
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),
476b8477 1942 COMEDI_NUM_MINORS);
ed9eccbe
DS
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),
476b8477 1950 COMEDI_NUM_MINORS);
ed9eccbe
DS
1951 return PTR_ERR(comedi_class);
1952 }
1953
1954 /* XXX requires /proc interface */
1955 comedi_proc_init();
1956
476b8477 1957 /* create devices files for legacy/manual use */
1dd33ab8 1958 for (i = 0; i < comedi_num_legacy_minors; i++) {
ed9eccbe
DS
1959 int minor;
1960 minor = comedi_alloc_board_minor(NULL);
476b8477 1961 if (minor < 0) {
ed9eccbe
DS
1962 comedi_cleanup_legacy_minors();
1963 cdev_del(&comedi_cdev);
1964 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
476b8477 1965 COMEDI_NUM_MINORS);
ed9eccbe
DS
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();
476b8477 1982 for (i = 0; i < COMEDI_NUM_MINORS; ++i)
ed9eccbe 1983 BUG_ON(comedi_file_info_table[i]);
476b8477 1984
ed9eccbe
DS
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
476b8477 2000void comedi_error(const comedi_device *dev, const char *s)
ed9eccbe
DS
2001{
2002 rt_printk("comedi%d: %s: %s\n", dev->minor, dev->driver->driver_name,
476b8477 2003 s);
ed9eccbe
DS
2004}
2005
476b8477 2006void comedi_event(comedi_device *dev, comedi_subdevice *s)
ed9eccbe
DS
2007{
2008 comedi_async *async = s->async;
2009 unsigned runflags = 0;
2010 unsigned runflags_mask = 0;
2011
476b8477 2012 /* DPRINTK("comedi_event 0x%x\n",mask); */
ed9eccbe
DS
2013
2014 if ((comedi_get_subdevice_runflags(s) & SRF_RUNNING) == 0)
2015 return;
2016
2017 if (s->async->
476b8477 2018 events & (COMEDI_CB_EOA | COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)) {
ed9eccbe
DS
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
476b8477 2037 /* pend wake up */
ed9eccbe
DS
2038 comedi_rt_pend_wakeup(&async->wait_head);
2039#else
476b8477
GKH
2040 printk
2041 ("BUG: comedi_event() code unreachable\n");
ed9eccbe
DS
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,
476b8477 2047 POLL_IN);
ed9eccbe
DS
2048 }
2049 if (s->subdev_flags & SDF_CMD_WRITE) {
2050 kill_fasync(&dev->async_queue, SIGIO,
476b8477 2051 POLL_OUT);
ed9eccbe
DS
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
476b8477
GKH
2067void comedi_set_subdevice_runflags(comedi_subdevice *s, unsigned mask,
2068 unsigned bits)
ed9eccbe
DS
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
476b8477 2078unsigned comedi_get_subdevice_runflags(comedi_subdevice *s)
ed9eccbe
DS
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
476b8477 2089static int is_device_busy(comedi_device *dev)
ed9eccbe
DS
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{
476b8477
GKH
2118 if (dev == NULL)
2119 return;
ed9eccbe
DS
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);
476b8477
GKH
2134 if (info == NULL)
2135 return -ENOMEM;
ed9eccbe 2136 info->device = kzalloc(sizeof(comedi_device), GFP_KERNEL);
476b8477 2137 if (info->device == NULL) {
ed9eccbe
DS
2138 kfree(info);
2139 return -ENOMEM;
2140 }
2141 comedi_device_init(info->device);
2142 comedi_spin_lock_irqsave(&comedi_file_info_table_lock, flags);
476b8477
GKH
2143 for (i = 0; i < COMEDI_NUM_BOARD_MINORS; ++i) {
2144 if (comedi_file_info_table[i] == NULL) {
ed9eccbe
DS
2145 comedi_file_info_table[i] = info;
2146 break;
2147 }
2148 }
2149 comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
476b8477 2150 if (i == COMEDI_NUM_BOARD_MINORS) {
ed9eccbe
DS
2151 comedi_device_cleanup(info->device);
2152 kfree(info->device);
2153 kfree(info);
476b8477
GKH
2154 rt_printk
2155 ("comedi: error: ran out of minor numbers for board device files.\n");
ed9eccbe
DS
2156 return -EBUSY;
2157 }
2158 info->device->minor = i;
2159 csdev = COMEDI_DEVICE_CREATE(comedi_class, NULL,
476b8477
GKH
2160 MKDEV(COMEDI_MAJOR, i), NULL,
2161 hardware_device, "comedi%i", i);
2162 if (!IS_ERR(csdev))
ed9eccbe 2163 info->device->class_dev = csdev;
476b8477 2164
ed9eccbe
DS
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
476b8477 2179 if (info) {
ed9eccbe 2180 comedi_device *dev = info->device;
476b8477
GKH
2181 if (dev) {
2182 if (dev->class_dev) {
2183 device_destroy(comedi_class,
2184 MKDEV(COMEDI_MAJOR, dev->minor));
ed9eccbe
DS
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);
476b8477
GKH
2201 if (info == NULL)
2202 return -ENOMEM;
ed9eccbe
DS
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);
4c41f3ae 2207 for (i = COMEDI_FIRST_SUBDEVICE_MINOR; i < COMEDI_NUM_MINORS; ++i) {
476b8477 2208 if (comedi_file_info_table[i] == NULL) {
ed9eccbe
DS
2209 comedi_file_info_table[i] = info;
2210 break;
2211 }
2212 }
2213 comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
476b8477 2214 if (i == COMEDI_NUM_MINORS) {
ed9eccbe 2215 kfree(info);
476b8477
GKH
2216 rt_printk
2217 ("comedi: error: ran out of minor numbers for board device files.\n");
ed9eccbe
DS
2218 return -EBUSY;
2219 }
2220 s->minor = i;
2221 csdev = COMEDI_DEVICE_CREATE(comedi_class, dev->class_dev,
476b8477
GKH
2222 MKDEV(COMEDI_MAJOR, i), NULL, NULL,
2223 "comedi%i_subd%i", dev->minor,
2224 (int)(s - dev->subdevices));
2225 if (!IS_ERR(csdev))
ed9eccbe 2226 s->class_dev = csdev;
476b8477 2227
ed9eccbe
DS
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
476b8477
GKH
2236 if (s == NULL)
2237 return;
2238 if (s->minor < 0)
2239 return;
ed9eccbe
DS
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
476b8477 2249 if (s->class_dev) {
ed9eccbe
DS
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}