1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#include "comedi_compat32.h"
20
21#include <linux/module.h>
22#include <linux/errno.h>
23#include <linux/kernel.h>
24#include <linux/sched.h>
25#include <linux/fcntl.h>
26#include <linux/delay.h>
27#include <linux/mm.h>
28#include <linux/slab.h>
29#include <linux/kmod.h>
30#include <linux/poll.h>
31#include <linux/init.h>
32#include <linux/device.h>
33#include <linux/vmalloc.h>
34#include <linux/fs.h>
35#include "comedidev.h"
36#include <linux/cdev.h>
37#include <linux/stat.h>
38
39#include <linux/io.h>
40#include <linux/uaccess.h>
41
42#include "comedi_internal.h"
43
44#define COMEDI_NUM_MINORS 0x100
45#define COMEDI_NUM_SUBDEVICE_MINORS \
46 (COMEDI_NUM_MINORS - COMEDI_NUM_BOARD_MINORS)
47
48static int comedi_num_legacy_minors;
49module_param(comedi_num_legacy_minors, int, S_IRUGO);
50MODULE_PARM_DESC(comedi_num_legacy_minors,
51 "number of comedi minor devices to reserve for non-auto-configured devices (default 0)"
52 );
53
54unsigned int comedi_default_buf_size_kb = CONFIG_COMEDI_DEFAULT_BUF_SIZE_KB;
55module_param(comedi_default_buf_size_kb, uint, S_IRUGO | S_IWUSR);
56MODULE_PARM_DESC(comedi_default_buf_size_kb,
57 "default asynchronous buffer size in KiB (default "
58 __MODULE_STRING(CONFIG_COMEDI_DEFAULT_BUF_SIZE_KB) ")");
59
60unsigned int comedi_default_buf_maxsize_kb
61 = CONFIG_COMEDI_DEFAULT_BUF_MAXSIZE_KB;
62module_param(comedi_default_buf_maxsize_kb, uint, S_IRUGO | S_IWUSR);
63MODULE_PARM_DESC(comedi_default_buf_maxsize_kb,
64 "default maximum size of asynchronous buffer in KiB (default "
65 __MODULE_STRING(CONFIG_COMEDI_DEFAULT_BUF_MAXSIZE_KB) ")");
66
67static DEFINE_MUTEX(comedi_board_minor_table_lock);
68static struct comedi_device
69*comedi_board_minor_table[COMEDI_NUM_BOARD_MINORS];
70
71static DEFINE_MUTEX(comedi_subdevice_minor_table_lock);
72
73static struct comedi_subdevice
74*comedi_subdevice_minor_table[COMEDI_NUM_SUBDEVICE_MINORS];
75
76static struct class *comedi_class;
77static struct cdev comedi_cdev;
78
79static void comedi_device_init(struct comedi_device *dev)
80{
81 kref_init(&dev->refcount);
82 spin_lock_init(&dev->spinlock);
83 mutex_init(&dev->mutex);
84 init_rwsem(&dev->attach_lock);
85 dev->minor = -1;
86}
87
88static void comedi_dev_kref_release(struct kref *kref)
89{
90 struct comedi_device *dev =
91 container_of(kref, struct comedi_device, refcount);
92
93 mutex_destroy(&dev->mutex);
94 put_device(dev->class_dev);
95 kfree(dev);
96}
97
98int comedi_dev_put(struct comedi_device *dev)
99{
100 if (dev)
101 return kref_put(&dev->refcount, comedi_dev_kref_release);
102 return 1;
103}
104EXPORT_SYMBOL_GPL(comedi_dev_put);
105
106static struct comedi_device *comedi_dev_get(struct comedi_device *dev)
107{
108 if (dev)
109 kref_get(&dev->refcount);
110 return dev;
111}
112
113static void comedi_device_cleanup(struct comedi_device *dev)
114{
115 struct module *driver_module = NULL;
116
117 if (dev == NULL)
118 return;
119 mutex_lock(&dev->mutex);
120 if (dev->attached)
121 driver_module = dev->driver->module;
122 comedi_device_detach(dev);
123 if (driver_module && dev->use_count)
124 module_put(driver_module);
125 mutex_unlock(&dev->mutex);
126}
127
128static bool comedi_clear_board_dev(struct comedi_device *dev)
129{
130 unsigned int i = dev->minor;
131 bool cleared = false;
132
133 mutex_lock(&comedi_board_minor_table_lock);
134 if (dev == comedi_board_minor_table[i]) {
135 comedi_board_minor_table[i] = NULL;
136 cleared = true;
137 }
138 mutex_unlock(&comedi_board_minor_table_lock);
139 return cleared;
140}
141
142static struct comedi_device *comedi_clear_board_minor(unsigned minor)
143{
144 struct comedi_device *dev;
145
146 mutex_lock(&comedi_board_minor_table_lock);
147 dev = comedi_board_minor_table[minor];
148 comedi_board_minor_table[minor] = NULL;
149 mutex_unlock(&comedi_board_minor_table_lock);
150 return dev;
151}
152
153static void comedi_free_board_dev(struct comedi_device *dev)
154{
155 if (dev) {
156 comedi_device_cleanup(dev);
157 if (dev->class_dev) {
158 device_destroy(comedi_class,
159 MKDEV(COMEDI_MAJOR, dev->minor));
160 }
161 comedi_dev_put(dev);
162 }
163}
164
165static struct comedi_subdevice
166*comedi_subdevice_from_minor(const struct comedi_device *dev, unsigned minor)
167{
168 struct comedi_subdevice *s;
169 unsigned int i = minor - COMEDI_NUM_BOARD_MINORS;
170
171 BUG_ON(i >= COMEDI_NUM_SUBDEVICE_MINORS);
172 mutex_lock(&comedi_subdevice_minor_table_lock);
173 s = comedi_subdevice_minor_table[i];
174 if (s && s->device != dev)
175 s = NULL;
176 mutex_unlock(&comedi_subdevice_minor_table_lock);
177 return s;
178}
179
180static struct comedi_device *comedi_dev_get_from_board_minor(unsigned minor)
181{
182 struct comedi_device *dev;
183
184 BUG_ON(minor >= COMEDI_NUM_BOARD_MINORS);
185 mutex_lock(&comedi_board_minor_table_lock);
186 dev = comedi_dev_get(comedi_board_minor_table[minor]);
187 mutex_unlock(&comedi_board_minor_table_lock);
188 return dev;
189}
190
191static struct comedi_device *comedi_dev_get_from_subdevice_minor(unsigned minor)
192{
193 struct comedi_device *dev;
194 struct comedi_subdevice *s;
195 unsigned int i = minor - COMEDI_NUM_BOARD_MINORS;
196
197 BUG_ON(i >= COMEDI_NUM_SUBDEVICE_MINORS);
198 mutex_lock(&comedi_subdevice_minor_table_lock);
199 s = comedi_subdevice_minor_table[i];
200 dev = comedi_dev_get(s ? s->device : NULL);
201 mutex_unlock(&comedi_subdevice_minor_table_lock);
202 return dev;
203}
204
205struct comedi_device *comedi_dev_get_from_minor(unsigned minor)
206{
207 if (minor < COMEDI_NUM_BOARD_MINORS)
208 return comedi_dev_get_from_board_minor(minor);
209 else
210 return comedi_dev_get_from_subdevice_minor(minor);
211}
212EXPORT_SYMBOL_GPL(comedi_dev_get_from_minor);
213
214static struct comedi_subdevice *
215comedi_read_subdevice(const struct comedi_device *dev, unsigned int minor)
216{
217 struct comedi_subdevice *s;
218
219 if (minor >= COMEDI_NUM_BOARD_MINORS) {
220 s = comedi_subdevice_from_minor(dev, minor);
221 if (s == NULL || (s->subdev_flags & SDF_CMD_READ))
222 return s;
223 }
224 return dev->read_subdev;
225}
226
227static struct comedi_subdevice *
228comedi_write_subdevice(const struct comedi_device *dev, unsigned int minor)
229{
230 struct comedi_subdevice *s;
231
232 if (minor >= COMEDI_NUM_BOARD_MINORS) {
233 s = comedi_subdevice_from_minor(dev, minor);
234 if (s == NULL || (s->subdev_flags & SDF_CMD_WRITE))
235 return s;
236 }
237 return dev->write_subdev;
238}
239
240static int resize_async_buffer(struct comedi_device *dev,
241 struct comedi_subdevice *s,
242 struct comedi_async *async, unsigned new_size)
243{
244 int retval;
245
246 if (new_size > async->max_bufsize)
247 return -EPERM;
248
249 if (s->busy) {
250 dev_dbg(dev->class_dev,
251 "subdevice is busy, cannot resize buffer\n");
252 return -EBUSY;
253 }
254 if (comedi_buf_is_mmapped(async)) {
255 dev_dbg(dev->class_dev,
256 "subdevice is mmapped, cannot resize buffer\n");
257 return -EBUSY;
258 }
259
260
261
262 new_size = (new_size + PAGE_SIZE - 1) & PAGE_MASK;
263
264 retval = comedi_buf_alloc(dev, s, new_size);
265 if (retval < 0)
266 return retval;
267
268 if (s->buf_change) {
269 retval = s->buf_change(dev, s, new_size);
270 if (retval < 0)
271 return retval;
272 }
273
274 dev_dbg(dev->class_dev, "subd %d buffer resized to %i bytes\n",
275 s->index, async->prealloc_bufsz);
276 return 0;
277}
278
279
280
281static ssize_t max_read_buffer_kb_show(struct device *csdev,
282 struct device_attribute *attr, char *buf)
283{
284 unsigned int minor = MINOR(csdev->devt);
285 struct comedi_device *dev;
286 struct comedi_subdevice *s;
287 unsigned int size = 0;
288
289 dev = comedi_dev_get_from_minor(minor);
290 if (!dev)
291 return -ENODEV;
292
293 mutex_lock(&dev->mutex);
294 s = comedi_read_subdevice(dev, minor);
295 if (s && (s->subdev_flags & SDF_CMD_READ) && s->async)
296 size = s->async->max_bufsize / 1024;
297 mutex_unlock(&dev->mutex);
298
299 comedi_dev_put(dev);
300 return snprintf(buf, PAGE_SIZE, "%i\n", size);
301}
302
303static ssize_t max_read_buffer_kb_store(struct device *csdev,
304 struct device_attribute *attr,
305 const char *buf, size_t count)
306{
307 unsigned int minor = MINOR(csdev->devt);
308 struct comedi_device *dev;
309 struct comedi_subdevice *s;
310 unsigned int size;
311 int err;
312
313 err = kstrtouint(buf, 10, &size);
314 if (err)
315 return err;
316 if (size > (UINT_MAX / 1024))
317 return -EINVAL;
318 size *= 1024;
319
320 dev = comedi_dev_get_from_minor(minor);
321 if (!dev)
322 return -ENODEV;
323
324 mutex_lock(&dev->mutex);
325 s = comedi_read_subdevice(dev, minor);
326 if (s && (s->subdev_flags & SDF_CMD_READ) && s->async)
327 s->async->max_bufsize = size;
328 else
329 err = -EINVAL;
330 mutex_unlock(&dev->mutex);
331
332 comedi_dev_put(dev);
333 return err ? err : count;
334}
335static DEVICE_ATTR_RW(max_read_buffer_kb);
336
337static ssize_t read_buffer_kb_show(struct device *csdev,
338 struct device_attribute *attr, char *buf)
339{
340 unsigned int minor = MINOR(csdev->devt);
341 struct comedi_device *dev;
342 struct comedi_subdevice *s;
343 unsigned int size = 0;
344
345 dev = comedi_dev_get_from_minor(minor);
346 if (!dev)
347 return -ENODEV;
348
349 mutex_lock(&dev->mutex);
350 s = comedi_read_subdevice(dev, minor);
351 if (s && (s->subdev_flags & SDF_CMD_READ) && s->async)
352 size = s->async->prealloc_bufsz / 1024;
353 mutex_unlock(&dev->mutex);
354
355 comedi_dev_put(dev);
356 return snprintf(buf, PAGE_SIZE, "%i\n", size);
357}
358
359static ssize_t read_buffer_kb_store(struct device *csdev,
360 struct device_attribute *attr,
361 const char *buf, size_t count)
362{
363 unsigned int minor = MINOR(csdev->devt);
364 struct comedi_device *dev;
365 struct comedi_subdevice *s;
366 unsigned int size;
367 int err;
368
369 err = kstrtouint(buf, 10, &size);
370 if (err)
371 return err;
372 if (size > (UINT_MAX / 1024))
373 return -EINVAL;
374 size *= 1024;
375
376 dev = comedi_dev_get_from_minor(minor);
377 if (!dev)
378 return -ENODEV;
379
380 mutex_lock(&dev->mutex);
381 s = comedi_read_subdevice(dev, minor);
382 if (s && (s->subdev_flags & SDF_CMD_READ) && s->async)
383 err = resize_async_buffer(dev, s, s->async, size);
384 else
385 err = -EINVAL;
386 mutex_unlock(&dev->mutex);
387
388 comedi_dev_put(dev);
389 return err ? err : count;
390}
391static DEVICE_ATTR_RW(read_buffer_kb);
392
393static ssize_t max_write_buffer_kb_show(struct device *csdev,
394 struct device_attribute *attr,
395 char *buf)
396{
397 unsigned int minor = MINOR(csdev->devt);
398 struct comedi_device *dev;
399 struct comedi_subdevice *s;
400 unsigned int size = 0;
401
402 dev = comedi_dev_get_from_minor(minor);
403 if (!dev)
404 return -ENODEV;
405
406 mutex_lock(&dev->mutex);
407 s = comedi_write_subdevice(dev, minor);
408 if (s && (s->subdev_flags & SDF_CMD_WRITE) && s->async)
409 size = s->async->max_bufsize / 1024;
410 mutex_unlock(&dev->mutex);
411
412 comedi_dev_put(dev);
413 return snprintf(buf, PAGE_SIZE, "%i\n", size);
414}
415
416static ssize_t max_write_buffer_kb_store(struct device *csdev,
417 struct device_attribute *attr,
418 const char *buf, size_t count)
419{
420 unsigned int minor = MINOR(csdev->devt);
421 struct comedi_device *dev;
422 struct comedi_subdevice *s;
423 unsigned int size;
424 int err;
425
426 err = kstrtouint(buf, 10, &size);
427 if (err)
428 return err;
429 if (size > (UINT_MAX / 1024))
430 return -EINVAL;
431 size *= 1024;
432
433 dev = comedi_dev_get_from_minor(minor);
434 if (!dev)
435 return -ENODEV;
436
437 mutex_lock(&dev->mutex);
438 s = comedi_write_subdevice(dev, minor);
439 if (s && (s->subdev_flags & SDF_CMD_WRITE) && s->async)
440 s->async->max_bufsize = size;
441 else
442 err = -EINVAL;
443 mutex_unlock(&dev->mutex);
444
445 comedi_dev_put(dev);
446 return err ? err : count;
447}
448static DEVICE_ATTR_RW(max_write_buffer_kb);
449
450static ssize_t write_buffer_kb_show(struct device *csdev,
451 struct device_attribute *attr, char *buf)
452{
453 unsigned int minor = MINOR(csdev->devt);
454 struct comedi_device *dev;
455 struct comedi_subdevice *s;
456 unsigned int size = 0;
457
458 dev = comedi_dev_get_from_minor(minor);
459 if (!dev)
460 return -ENODEV;
461
462 mutex_lock(&dev->mutex);
463 s = comedi_write_subdevice(dev, minor);
464 if (s && (s->subdev_flags & SDF_CMD_WRITE) && s->async)
465 size = s->async->prealloc_bufsz / 1024;
466 mutex_unlock(&dev->mutex);
467
468 comedi_dev_put(dev);
469 return snprintf(buf, PAGE_SIZE, "%i\n", size);
470}
471
472static ssize_t write_buffer_kb_store(struct device *csdev,
473 struct device_attribute *attr,
474 const char *buf, size_t count)
475{
476 unsigned int minor = MINOR(csdev->devt);
477 struct comedi_device *dev;
478 struct comedi_subdevice *s;
479 unsigned int size;
480 int err;
481
482 err = kstrtouint(buf, 10, &size);
483 if (err)
484 return err;
485 if (size > (UINT_MAX / 1024))
486 return -EINVAL;
487 size *= 1024;
488
489 dev = comedi_dev_get_from_minor(minor);
490 if (!dev)
491 return -ENODEV;
492
493 mutex_lock(&dev->mutex);
494 s = comedi_write_subdevice(dev, minor);
495 if (s && (s->subdev_flags & SDF_CMD_WRITE) && s->async)
496 err = resize_async_buffer(dev, s, s->async, size);
497 else
498 err = -EINVAL;
499 mutex_unlock(&dev->mutex);
500
501 comedi_dev_put(dev);
502 return err ? err : count;
503}
504static DEVICE_ATTR_RW(write_buffer_kb);
505
506static struct attribute *comedi_dev_attrs[] = {
507 &dev_attr_max_read_buffer_kb.attr,
508 &dev_attr_read_buffer_kb.attr,
509 &dev_attr_max_write_buffer_kb.attr,
510 &dev_attr_write_buffer_kb.attr,
511 NULL,
512};
513ATTRIBUTE_GROUPS(comedi_dev);
514
515static void comedi_set_subdevice_runflags(struct comedi_subdevice *s,
516 unsigned mask, unsigned bits)
517{
518 unsigned long flags;
519
520 spin_lock_irqsave(&s->spin_lock, flags);
521 s->runflags &= ~mask;
522 s->runflags |= (bits & mask);
523 spin_unlock_irqrestore(&s->spin_lock, flags);
524}
525
526static unsigned comedi_get_subdevice_runflags(struct comedi_subdevice *s)
527{
528 unsigned long flags;
529 unsigned runflags;
530
531 spin_lock_irqsave(&s->spin_lock, flags);
532 runflags = s->runflags;
533 spin_unlock_irqrestore(&s->spin_lock, flags);
534 return runflags;
535}
536
537bool comedi_is_subdevice_running(struct comedi_subdevice *s)
538{
539 unsigned runflags = comedi_get_subdevice_runflags(s);
540
541 return (runflags & SRF_RUNNING) ? true : false;
542}
543EXPORT_SYMBOL_GPL(comedi_is_subdevice_running);
544
545static bool comedi_is_subdevice_in_error(struct comedi_subdevice *s)
546{
547 unsigned runflags = comedi_get_subdevice_runflags(s);
548
549 return (runflags & SRF_ERROR) ? true : false;
550}
551
552static bool comedi_is_subdevice_idle(struct comedi_subdevice *s)
553{
554 unsigned runflags = comedi_get_subdevice_runflags(s);
555
556 return (runflags & (SRF_ERROR | SRF_RUNNING)) ? false : true;
557}
558
559
560
561
562
563
564
565
566
567void *comedi_alloc_spriv(struct comedi_subdevice *s, size_t size)
568{
569 s->private = kzalloc(size, GFP_KERNEL);
570 if (s->private)
571 s->runflags |= SRF_FREE_SPRIV;
572 return s->private;
573}
574EXPORT_SYMBOL_GPL(comedi_alloc_spriv);
575
576
577
578
579static void do_become_nonbusy(struct comedi_device *dev,
580 struct comedi_subdevice *s)
581{
582 struct comedi_async *async = s->async;
583
584 comedi_set_subdevice_runflags(s, SRF_RUNNING, 0);
585 if (async) {
586 comedi_buf_reset(async);
587 async->inttrig = NULL;
588 kfree(async->cmd.chanlist);
589 async->cmd.chanlist = NULL;
590 s->busy = NULL;
591 wake_up_interruptible_all(&s->async->wait_head);
592 } else {
593 dev_err(dev->class_dev,
594 "BUG: (?) do_become_nonbusy called with async=NULL\n");
595 s->busy = NULL;
596 }
597}
598
599static int do_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
600{
601 int ret = 0;
602
603 if (comedi_is_subdevice_running(s) && s->cancel)
604 ret = s->cancel(dev, s);
605
606 do_become_nonbusy(dev, s);
607
608 return ret;
609}
610
611void comedi_device_cancel_all(struct comedi_device *dev)
612{
613 struct comedi_subdevice *s;
614 int i;
615
616 if (!dev->attached)
617 return;
618
619 for (i = 0; i < dev->n_subdevices; i++) {
620 s = &dev->subdevices[i];
621 if (s->async)
622 do_cancel(dev, s);
623 }
624}
625
626static int is_device_busy(struct comedi_device *dev)
627{
628 struct comedi_subdevice *s;
629 int i;
630
631 if (!dev->attached)
632 return 0;
633
634 for (i = 0; i < dev->n_subdevices; i++) {
635 s = &dev->subdevices[i];
636 if (s->busy)
637 return 1;
638 if (s->async && comedi_buf_is_mmapped(s->async))
639 return 1;
640 }
641
642 return 0;
643}
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658static int do_devconfig_ioctl(struct comedi_device *dev,
659 struct comedi_devconfig __user *arg)
660{
661 struct comedi_devconfig it;
662
663 if (!capable(CAP_SYS_ADMIN))
664 return -EPERM;
665
666 if (arg == NULL) {
667 if (is_device_busy(dev))
668 return -EBUSY;
669 if (dev->attached) {
670 struct module *driver_module = dev->driver->module;
671 comedi_device_detach(dev);
672 module_put(driver_module);
673 }
674 return 0;
675 }
676
677 if (copy_from_user(&it, arg, sizeof(it)))
678 return -EFAULT;
679
680 it.board_name[COMEDI_NAMELEN - 1] = 0;
681
682 if (it.options[COMEDI_DEVCONF_AUX_DATA_LENGTH]) {
683 dev_warn(dev->class_dev,
684 "comedi_config --init_data is deprecated\n");
685 return -EINVAL;
686 }
687
688 if (dev->minor >= comedi_num_legacy_minors)
689
690 return -EBUSY;
691
692
693 return comedi_device_attach(dev, &it);
694}
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710static int do_bufconfig_ioctl(struct comedi_device *dev,
711 struct comedi_bufconfig __user *arg)
712{
713 struct comedi_bufconfig bc;
714 struct comedi_async *async;
715 struct comedi_subdevice *s;
716 int retval = 0;
717
718 if (copy_from_user(&bc, arg, sizeof(bc)))
719 return -EFAULT;
720
721 if (bc.subdevice >= dev->n_subdevices)
722 return -EINVAL;
723
724 s = &dev->subdevices[bc.subdevice];
725 async = s->async;
726
727 if (!async) {
728 dev_dbg(dev->class_dev,
729 "subdevice does not have async capability\n");
730 bc.size = 0;
731 bc.maximum_size = 0;
732 goto copyback;
733 }
734
735 if (bc.maximum_size) {
736 if (!capable(CAP_SYS_ADMIN))
737 return -EPERM;
738
739 async->max_bufsize = bc.maximum_size;
740 }
741
742 if (bc.size) {
743 retval = resize_async_buffer(dev, s, async, bc.size);
744 if (retval < 0)
745 return retval;
746 }
747
748 bc.size = async->prealloc_bufsz;
749 bc.maximum_size = async->max_bufsize;
750
751copyback:
752 if (copy_to_user(arg, &bc, sizeof(bc)))
753 return -EFAULT;
754
755 return 0;
756}
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772static int do_devinfo_ioctl(struct comedi_device *dev,
773 struct comedi_devinfo __user *arg,
774 struct file *file)
775{
776 const unsigned minor = iminor(file_inode(file));
777 struct comedi_subdevice *s;
778 struct comedi_devinfo devinfo;
779
780 memset(&devinfo, 0, sizeof(devinfo));
781
782
783 devinfo.version_code = COMEDI_VERSION_CODE;
784 devinfo.n_subdevs = dev->n_subdevices;
785 strlcpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN);
786 strlcpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN);
787
788 s = comedi_read_subdevice(dev, minor);
789 if (s)
790 devinfo.read_subdevice = s->index;
791 else
792 devinfo.read_subdevice = -1;
793
794 s = comedi_write_subdevice(dev, minor);
795 if (s)
796 devinfo.write_subdevice = s->index;
797 else
798 devinfo.write_subdevice = -1;
799
800 if (copy_to_user(arg, &devinfo, sizeof(devinfo)))
801 return -EFAULT;
802
803 return 0;
804}
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820static int do_subdinfo_ioctl(struct comedi_device *dev,
821 struct comedi_subdinfo __user *arg, void *file)
822{
823 int ret, i;
824 struct comedi_subdinfo *tmp, *us;
825 struct comedi_subdevice *s;
826
827 tmp = kcalloc(dev->n_subdevices, sizeof(*tmp), GFP_KERNEL);
828 if (!tmp)
829 return -ENOMEM;
830
831
832 for (i = 0; i < dev->n_subdevices; i++) {
833 s = &dev->subdevices[i];
834 us = tmp + i;
835
836 us->type = s->type;
837 us->n_chan = s->n_chan;
838 us->subd_flags = s->subdev_flags;
839 if (comedi_is_subdevice_running(s))
840 us->subd_flags |= SDF_RUNNING;
841#define TIMER_nanosec 5
842 us->timer_type = TIMER_nanosec;
843 us->len_chanlist = s->len_chanlist;
844 us->maxdata = s->maxdata;
845 if (s->range_table) {
846 us->range_type =
847 (i << 24) | (0 << 16) | (s->range_table->length);
848 } else {
849 us->range_type = 0;
850 }
851
852 if (s->busy)
853 us->subd_flags |= SDF_BUSY;
854 if (s->busy == file)
855 us->subd_flags |= SDF_BUSY_OWNER;
856 if (s->lock)
857 us->subd_flags |= SDF_LOCKED;
858 if (s->lock == file)
859 us->subd_flags |= SDF_LOCK_OWNER;
860 if (!s->maxdata && s->maxdata_list)
861 us->subd_flags |= SDF_MAXDATA;
862 if (s->range_table_list)
863 us->subd_flags |= SDF_RANGETYPE;
864 if (s->do_cmd)
865 us->subd_flags |= SDF_CMD;
866
867 if (s->insn_bits != &insn_inval)
868 us->insn_bits_support = COMEDI_SUPPORTED;
869 else
870 us->insn_bits_support = COMEDI_UNSUPPORTED;
871 }
872
873 ret = copy_to_user(arg, tmp, dev->n_subdevices * sizeof(*tmp));
874
875 kfree(tmp);
876
877 return ret ? -EFAULT : 0;
878}
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894static int do_chaninfo_ioctl(struct comedi_device *dev,
895 struct comedi_chaninfo __user *arg)
896{
897 struct comedi_subdevice *s;
898 struct comedi_chaninfo it;
899
900 if (copy_from_user(&it, arg, sizeof(it)))
901 return -EFAULT;
902
903 if (it.subdev >= dev->n_subdevices)
904 return -EINVAL;
905 s = &dev->subdevices[it.subdev];
906
907 if (it.maxdata_list) {
908 if (s->maxdata || !s->maxdata_list)
909 return -EINVAL;
910 if (copy_to_user(it.maxdata_list, s->maxdata_list,
911 s->n_chan * sizeof(unsigned int)))
912 return -EFAULT;
913 }
914
915 if (it.flaglist)
916 return -EINVAL;
917
918 if (it.rangelist) {
919 int i;
920
921 if (!s->range_table_list)
922 return -EINVAL;
923 for (i = 0; i < s->n_chan; i++) {
924 int x;
925
926 x = (dev->minor << 28) | (it.subdev << 24) | (i << 16) |
927 (s->range_table_list[i]->length);
928 if (put_user(x, it.rangelist + i))
929 return -EFAULT;
930 }
931#if 0
932 if (copy_to_user(it.rangelist, s->range_type_list,
933 s->n_chan * sizeof(unsigned int)))
934 return -EFAULT;
935#endif
936 }
937
938 return 0;
939}
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955static int do_bufinfo_ioctl(struct comedi_device *dev,
956 struct comedi_bufinfo __user *arg, void *file)
957{
958 struct comedi_bufinfo bi;
959 struct comedi_subdevice *s;
960 struct comedi_async *async;
961
962 if (copy_from_user(&bi, arg, sizeof(bi)))
963 return -EFAULT;
964
965 if (bi.subdevice >= dev->n_subdevices)
966 return -EINVAL;
967
968 s = &dev->subdevices[bi.subdevice];
969
970 if (s->lock && s->lock != file)
971 return -EACCES;
972
973 async = s->async;
974
975 if (!async) {
976 dev_dbg(dev->class_dev,
977 "subdevice does not have async capability\n");
978 bi.buf_write_ptr = 0;
979 bi.buf_read_ptr = 0;
980 bi.buf_write_count = 0;
981 bi.buf_read_count = 0;
982 bi.bytes_read = 0;
983 bi.bytes_written = 0;
984 goto copyback;
985 }
986 if (!s->busy) {
987 bi.bytes_read = 0;
988 bi.bytes_written = 0;
989 goto copyback_position;
990 }
991 if (s->busy != file)
992 return -EACCES;
993
994 if (bi.bytes_read && (s->subdev_flags & SDF_CMD_READ)) {
995 bi.bytes_read = comedi_buf_read_alloc(async, bi.bytes_read);
996 comedi_buf_read_free(async, bi.bytes_read);
997
998 if (comedi_is_subdevice_idle(s) &&
999 async->buf_write_count == async->buf_read_count) {
1000 do_become_nonbusy(dev, s);
1001 }
1002 }
1003
1004 if (bi.bytes_written && (s->subdev_flags & SDF_CMD_WRITE)) {
1005 bi.bytes_written =
1006 comedi_buf_write_alloc(async, bi.bytes_written);
1007 comedi_buf_write_free(async, bi.bytes_written);
1008 }
1009
1010copyback_position:
1011 bi.buf_write_count = async->buf_write_count;
1012 bi.buf_write_ptr = async->buf_write_ptr;
1013 bi.buf_read_count = async->buf_read_count;
1014 bi.buf_read_ptr = async->buf_read_ptr;
1015
1016copyback:
1017 if (copy_to_user(arg, &bi, sizeof(bi)))
1018 return -EFAULT;
1019
1020 return 0;
1021}
1022
1023static int check_insn_config_length(struct comedi_insn *insn,
1024 unsigned int *data)
1025{
1026 if (insn->n < 1)
1027 return -EINVAL;
1028
1029 switch (data[0]) {
1030 case INSN_CONFIG_DIO_OUTPUT:
1031 case INSN_CONFIG_DIO_INPUT:
1032 case INSN_CONFIG_DISARM:
1033 case INSN_CONFIG_RESET:
1034 if (insn->n == 1)
1035 return 0;
1036 break;
1037 case INSN_CONFIG_ARM:
1038 case INSN_CONFIG_DIO_QUERY:
1039 case INSN_CONFIG_BLOCK_SIZE:
1040 case INSN_CONFIG_FILTER:
1041 case INSN_CONFIG_SERIAL_CLOCK:
1042 case INSN_CONFIG_BIDIRECTIONAL_DATA:
1043 case INSN_CONFIG_ALT_SOURCE:
1044 case INSN_CONFIG_SET_COUNTER_MODE:
1045 case INSN_CONFIG_8254_READ_STATUS:
1046 case INSN_CONFIG_SET_ROUTING:
1047 case INSN_CONFIG_GET_ROUTING:
1048 case INSN_CONFIG_GET_PWM_STATUS:
1049 case INSN_CONFIG_PWM_SET_PERIOD:
1050 case INSN_CONFIG_PWM_GET_PERIOD:
1051 if (insn->n == 2)
1052 return 0;
1053 break;
1054 case INSN_CONFIG_SET_GATE_SRC:
1055 case INSN_CONFIG_GET_GATE_SRC:
1056 case INSN_CONFIG_SET_CLOCK_SRC:
1057 case INSN_CONFIG_GET_CLOCK_SRC:
1058 case INSN_CONFIG_SET_OTHER_SRC:
1059 case INSN_CONFIG_GET_COUNTER_STATUS:
1060 case INSN_CONFIG_PWM_SET_H_BRIDGE:
1061 case INSN_CONFIG_PWM_GET_H_BRIDGE:
1062 case INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE:
1063 if (insn->n == 3)
1064 return 0;
1065 break;
1066 case INSN_CONFIG_PWM_OUTPUT:
1067 case INSN_CONFIG_ANALOG_TRIG:
1068 if (insn->n == 5)
1069 return 0;
1070 break;
1071 case INSN_CONFIG_DIGITAL_TRIG:
1072 if (insn->n == 6)
1073 return 0;
1074 break;
1075
1076
1077 default:
1078 pr_warn("comedi: No check for data length of config insn id %i is implemented.\n",
1079 data[0]);
1080 pr_warn("comedi: Add a check to %s in %s.\n",
1081 __func__, __FILE__);
1082 pr_warn("comedi: Assuming n=%i is correct.\n", insn->n);
1083 return 0;
1084 }
1085 return -EINVAL;
1086}
1087
1088static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
1089 unsigned int *data, void *file)
1090{
1091 struct comedi_subdevice *s;
1092 int ret = 0;
1093 int i;
1094
1095 if (insn->insn & INSN_MASK_SPECIAL) {
1096
1097
1098 switch (insn->insn) {
1099 case INSN_GTOD:
1100 {
1101 struct timeval tv;
1102
1103 if (insn->n != 2) {
1104 ret = -EINVAL;
1105 break;
1106 }
1107
1108 do_gettimeofday(&tv);
1109 data[0] = tv.tv_sec;
1110 data[1] = tv.tv_usec;
1111 ret = 2;
1112
1113 break;
1114 }
1115 case INSN_WAIT:
1116 if (insn->n != 1 || data[0] >= 100000) {
1117 ret = -EINVAL;
1118 break;
1119 }
1120 udelay(data[0] / 1000);
1121 ret = 1;
1122 break;
1123 case INSN_INTTRIG:
1124 if (insn->n != 1) {
1125 ret = -EINVAL;
1126 break;
1127 }
1128 if (insn->subdev >= dev->n_subdevices) {
1129 dev_dbg(dev->class_dev,
1130 "%d not usable subdevice\n",
1131 insn->subdev);
1132 ret = -EINVAL;
1133 break;
1134 }
1135 s = &dev->subdevices[insn->subdev];
1136 if (!s->async) {
1137 dev_dbg(dev->class_dev, "no async\n");
1138 ret = -EINVAL;
1139 break;
1140 }
1141 if (!s->async->inttrig) {
1142 dev_dbg(dev->class_dev, "no inttrig\n");
1143 ret = -EAGAIN;
1144 break;
1145 }
1146 ret = s->async->inttrig(dev, s, data[0]);
1147 if (ret >= 0)
1148 ret = 1;
1149 break;
1150 default:
1151 dev_dbg(dev->class_dev, "invalid insn\n");
1152 ret = -EINVAL;
1153 break;
1154 }
1155 } else {
1156
1157 unsigned int maxdata;
1158
1159 if (insn->subdev >= dev->n_subdevices) {
1160 dev_dbg(dev->class_dev, "subdevice %d out of range\n",
1161 insn->subdev);
1162 ret = -EINVAL;
1163 goto out;
1164 }
1165 s = &dev->subdevices[insn->subdev];
1166
1167 if (s->type == COMEDI_SUBD_UNUSED) {
1168 dev_dbg(dev->class_dev, "%d not usable subdevice\n",
1169 insn->subdev);
1170 ret = -EIO;
1171 goto out;
1172 }
1173
1174
1175 if (s->lock && s->lock != file) {
1176 dev_dbg(dev->class_dev, "device locked\n");
1177 ret = -EACCES;
1178 goto out;
1179 }
1180
1181 ret = comedi_check_chanlist(s, 1, &insn->chanspec);
1182 if (ret < 0) {
1183 ret = -EINVAL;
1184 dev_dbg(dev->class_dev, "bad chanspec\n");
1185 goto out;
1186 }
1187
1188 if (s->busy) {
1189 ret = -EBUSY;
1190 goto out;
1191 }
1192
1193 s->busy = &parse_insn;
1194 switch (insn->insn) {
1195 case INSN_READ:
1196 ret = s->insn_read(dev, s, insn, data);
1197 break;
1198 case INSN_WRITE:
1199 maxdata = s->maxdata_list
1200 ? s->maxdata_list[CR_CHAN(insn->chanspec)]
1201 : s->maxdata;
1202 for (i = 0; i < insn->n; ++i) {
1203 if (data[i] > maxdata) {
1204 ret = -EINVAL;
1205 dev_dbg(dev->class_dev,
1206 "bad data value(s)\n");
1207 break;
1208 }
1209 }
1210 if (ret == 0)
1211 ret = s->insn_write(dev, s, insn, data);
1212 break;
1213 case INSN_BITS:
1214 if (insn->n != 2) {
1215 ret = -EINVAL;
1216 } else {
1217
1218
1219
1220 unsigned int shift;
1221 unsigned int orig_mask;
1222
1223 orig_mask = data[0];
1224 if (s->n_chan <= 32) {
1225 shift = CR_CHAN(insn->chanspec);
1226 if (shift > 0) {
1227 insn->chanspec = 0;
1228 data[0] <<= shift;
1229 data[1] <<= shift;
1230 }
1231 } else
1232 shift = 0;
1233 ret = s->insn_bits(dev, s, insn, data);
1234 data[0] = orig_mask;
1235 if (shift > 0)
1236 data[1] >>= shift;
1237 }
1238 break;
1239 case INSN_CONFIG:
1240 ret = check_insn_config_length(insn, data);
1241 if (ret)
1242 break;
1243 ret = s->insn_config(dev, s, insn, data);
1244 break;
1245 default:
1246 ret = -EINVAL;
1247 break;
1248 }
1249
1250 s->busy = NULL;
1251 }
1252
1253out:
1254 return ret;
1255}
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273#define MAX_SAMPLES 256
1274static int do_insnlist_ioctl(struct comedi_device *dev,
1275 struct comedi_insnlist __user *arg, void *file)
1276{
1277 struct comedi_insnlist insnlist;
1278 struct comedi_insn *insns = NULL;
1279 unsigned int *data = NULL;
1280 int i = 0;
1281 int ret = 0;
1282
1283 if (copy_from_user(&insnlist, arg, sizeof(insnlist)))
1284 return -EFAULT;
1285
1286 data = kmalloc(sizeof(unsigned int) * MAX_SAMPLES, GFP_KERNEL);
1287 if (!data) {
1288 ret = -ENOMEM;
1289 goto error;
1290 }
1291
1292 insns = kcalloc(insnlist.n_insns, sizeof(*insns), GFP_KERNEL);
1293 if (!insns) {
1294 ret = -ENOMEM;
1295 goto error;
1296 }
1297
1298 if (copy_from_user(insns, insnlist.insns,
1299 sizeof(*insns) * insnlist.n_insns)) {
1300 dev_dbg(dev->class_dev, "copy_from_user failed\n");
1301 ret = -EFAULT;
1302 goto error;
1303 }
1304
1305 for (i = 0; i < insnlist.n_insns; i++) {
1306 if (insns[i].n > MAX_SAMPLES) {
1307 dev_dbg(dev->class_dev,
1308 "number of samples too large\n");
1309 ret = -EINVAL;
1310 goto error;
1311 }
1312 if (insns[i].insn & INSN_MASK_WRITE) {
1313 if (copy_from_user(data, insns[i].data,
1314 insns[i].n * sizeof(unsigned int))) {
1315 dev_dbg(dev->class_dev,
1316 "copy_from_user failed\n");
1317 ret = -EFAULT;
1318 goto error;
1319 }
1320 }
1321 ret = parse_insn(dev, insns + i, data, file);
1322 if (ret < 0)
1323 goto error;
1324 if (insns[i].insn & INSN_MASK_READ) {
1325 if (copy_to_user(insns[i].data, data,
1326 insns[i].n * sizeof(unsigned int))) {
1327 dev_dbg(dev->class_dev,
1328 "copy_to_user failed\n");
1329 ret = -EFAULT;
1330 goto error;
1331 }
1332 }
1333 if (need_resched())
1334 schedule();
1335 }
1336
1337error:
1338 kfree(insns);
1339 kfree(data);
1340
1341 if (ret < 0)
1342 return ret;
1343 return i;
1344}
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360static int do_insn_ioctl(struct comedi_device *dev,
1361 struct comedi_insn __user *arg, void *file)
1362{
1363 struct comedi_insn insn;
1364 unsigned int *data = NULL;
1365 int ret = 0;
1366
1367 data = kmalloc(sizeof(unsigned int) * MAX_SAMPLES, GFP_KERNEL);
1368 if (!data) {
1369 ret = -ENOMEM;
1370 goto error;
1371 }
1372
1373 if (copy_from_user(&insn, arg, sizeof(insn))) {
1374 ret = -EFAULT;
1375 goto error;
1376 }
1377
1378
1379 if (insn.n > MAX_SAMPLES)
1380 insn.n = MAX_SAMPLES;
1381 if (insn.insn & INSN_MASK_WRITE) {
1382 if (copy_from_user(data,
1383 insn.data,
1384 insn.n * sizeof(unsigned int))) {
1385 ret = -EFAULT;
1386 goto error;
1387 }
1388 }
1389 ret = parse_insn(dev, &insn, data, file);
1390 if (ret < 0)
1391 goto error;
1392 if (insn.insn & INSN_MASK_READ) {
1393 if (copy_to_user(insn.data,
1394 data,
1395 insn.n * sizeof(unsigned int))) {
1396 ret = -EFAULT;
1397 goto error;
1398 }
1399 }
1400 ret = insn.n;
1401
1402error:
1403 kfree(data);
1404
1405 return ret;
1406}
1407
1408static int do_cmd_ioctl(struct comedi_device *dev,
1409 struct comedi_cmd __user *arg, void *file)
1410{
1411 struct comedi_cmd cmd;
1412 struct comedi_subdevice *s;
1413 struct comedi_async *async;
1414 int ret = 0;
1415 unsigned int __user *user_chanlist;
1416
1417 if (copy_from_user(&cmd, arg, sizeof(cmd))) {
1418 dev_dbg(dev->class_dev, "bad cmd address\n");
1419 return -EFAULT;
1420 }
1421
1422 user_chanlist = (unsigned int __user *)cmd.chanlist;
1423
1424 if (cmd.subdev >= dev->n_subdevices) {
1425 dev_dbg(dev->class_dev, "%d no such subdevice\n", cmd.subdev);
1426 return -ENODEV;
1427 }
1428
1429 s = &dev->subdevices[cmd.subdev];
1430 async = s->async;
1431
1432 if (s->type == COMEDI_SUBD_UNUSED) {
1433 dev_dbg(dev->class_dev, "%d not valid subdevice\n", cmd.subdev);
1434 return -EIO;
1435 }
1436
1437 if (!s->do_cmd || !s->do_cmdtest || !s->async) {
1438 dev_dbg(dev->class_dev,
1439 "subdevice %i does not support commands\n", cmd.subdev);
1440 return -EIO;
1441 }
1442
1443
1444 if (s->lock && s->lock != file) {
1445 dev_dbg(dev->class_dev, "subdevice locked\n");
1446 return -EACCES;
1447 }
1448
1449
1450 if (s->busy) {
1451 dev_dbg(dev->class_dev, "subdevice busy\n");
1452 return -EBUSY;
1453 }
1454
1455
1456 if (cmd.chanlist_len > s->len_chanlist) {
1457 dev_dbg(dev->class_dev, "channel/gain list too long %u > %d\n",
1458 cmd.chanlist_len, s->len_chanlist);
1459 return -EINVAL;
1460 }
1461
1462
1463 if (cmd.chanlist_len < 1) {
1464 dev_dbg(dev->class_dev, "channel/gain list too short %u < 1\n",
1465 cmd.chanlist_len);
1466 return -EINVAL;
1467 }
1468
1469 async->cmd = cmd;
1470 async->cmd.data = NULL;
1471
1472 async->cmd.chanlist = memdup_user(user_chanlist,
1473 async->cmd.chanlist_len * sizeof(int));
1474 if (IS_ERR(async->cmd.chanlist)) {
1475 ret = PTR_ERR(async->cmd.chanlist);
1476 async->cmd.chanlist = NULL;
1477 dev_dbg(dev->class_dev, "memdup_user failed with code %d\n",
1478 ret);
1479 goto cleanup;
1480 }
1481
1482
1483 ret = comedi_check_chanlist(s,
1484 async->cmd.chanlist_len,
1485 async->cmd.chanlist);
1486 if (ret < 0) {
1487 dev_dbg(dev->class_dev, "bad chanlist\n");
1488 goto cleanup;
1489 }
1490
1491 ret = s->do_cmdtest(dev, s, &async->cmd);
1492
1493 if (async->cmd.flags & TRIG_BOGUS || ret) {
1494 dev_dbg(dev->class_dev, "test returned %d\n", ret);
1495 cmd = async->cmd;
1496
1497 cmd.chanlist = (unsigned int __force *)user_chanlist;
1498 cmd.data = NULL;
1499 if (copy_to_user(arg, &cmd, sizeof(cmd))) {
1500 dev_dbg(dev->class_dev, "fault writing cmd\n");
1501 ret = -EFAULT;
1502 goto cleanup;
1503 }
1504 ret = -EAGAIN;
1505 goto cleanup;
1506 }
1507
1508 if (!async->prealloc_bufsz) {
1509 ret = -ENOMEM;
1510 dev_dbg(dev->class_dev, "no buffer (?)\n");
1511 goto cleanup;
1512 }
1513
1514 comedi_buf_reset(async);
1515
1516 async->cb_mask =
1517 COMEDI_CB_EOA | COMEDI_CB_BLOCK | COMEDI_CB_ERROR |
1518 COMEDI_CB_OVERFLOW;
1519 if (async->cmd.flags & TRIG_WAKE_EOS)
1520 async->cb_mask |= COMEDI_CB_EOS;
1521
1522 comedi_set_subdevice_runflags(s, SRF_ERROR | SRF_RUNNING, SRF_RUNNING);
1523
1524
1525
1526 s->busy = file;
1527 ret = s->do_cmd(dev, s);
1528 if (ret == 0)
1529 return 0;
1530
1531cleanup:
1532 do_become_nonbusy(dev, s);
1533
1534 return ret;
1535}
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552static int do_cmdtest_ioctl(struct comedi_device *dev,
1553 struct comedi_cmd __user *arg, void *file)
1554{
1555 struct comedi_cmd cmd;
1556 struct comedi_subdevice *s;
1557 int ret = 0;
1558 unsigned int *chanlist = NULL;
1559 unsigned int __user *user_chanlist;
1560
1561 if (copy_from_user(&cmd, arg, sizeof(cmd))) {
1562 dev_dbg(dev->class_dev, "bad cmd address\n");
1563 return -EFAULT;
1564 }
1565
1566 user_chanlist = (unsigned int __user *)cmd.chanlist;
1567
1568 if (cmd.subdev >= dev->n_subdevices) {
1569 dev_dbg(dev->class_dev, "%d no such subdevice\n", cmd.subdev);
1570 return -ENODEV;
1571 }
1572
1573 s = &dev->subdevices[cmd.subdev];
1574 if (s->type == COMEDI_SUBD_UNUSED) {
1575 dev_dbg(dev->class_dev, "%d not valid subdevice\n", cmd.subdev);
1576 return -EIO;
1577 }
1578
1579 if (!s->do_cmd || !s->do_cmdtest) {
1580 dev_dbg(dev->class_dev,
1581 "subdevice %i does not support commands\n", cmd.subdev);
1582 return -EIO;
1583 }
1584
1585
1586 if (cmd.chanlist_len > s->len_chanlist) {
1587 dev_dbg(dev->class_dev, "channel/gain list too long %d > %d\n",
1588 cmd.chanlist_len, s->len_chanlist);
1589 ret = -EINVAL;
1590 goto cleanup;
1591 }
1592
1593
1594 if (cmd.chanlist) {
1595 chanlist = memdup_user(user_chanlist,
1596 cmd.chanlist_len * sizeof(int));
1597 if (IS_ERR(chanlist)) {
1598 ret = PTR_ERR(chanlist);
1599 chanlist = NULL;
1600 dev_dbg(dev->class_dev,
1601 "memdup_user exited with code %d", ret);
1602 goto cleanup;
1603 }
1604
1605
1606 ret = comedi_check_chanlist(s, cmd.chanlist_len, chanlist);
1607 if (ret < 0) {
1608 dev_dbg(dev->class_dev, "bad chanlist\n");
1609 goto cleanup;
1610 }
1611
1612 cmd.chanlist = chanlist;
1613 }
1614
1615 ret = s->do_cmdtest(dev, s, &cmd);
1616
1617
1618 cmd.chanlist = (unsigned int __force *)user_chanlist;
1619
1620 if (copy_to_user(arg, &cmd, sizeof(cmd))) {
1621 dev_dbg(dev->class_dev, "bad cmd address\n");
1622 ret = -EFAULT;
1623 goto cleanup;
1624 }
1625cleanup:
1626 kfree(chanlist);
1627
1628 return ret;
1629}
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646static int do_lock_ioctl(struct comedi_device *dev, unsigned int arg,
1647 void *file)
1648{
1649 int ret = 0;
1650 unsigned long flags;
1651 struct comedi_subdevice *s;
1652
1653 if (arg >= dev->n_subdevices)
1654 return -EINVAL;
1655 s = &dev->subdevices[arg];
1656
1657 spin_lock_irqsave(&s->spin_lock, flags);
1658 if (s->busy || s->lock)
1659 ret = -EBUSY;
1660 else
1661 s->lock = file;
1662 spin_unlock_irqrestore(&s->spin_lock, flags);
1663
1664#if 0
1665 if (ret < 0)
1666 return ret;
1667
1668 if (s->lock_f)
1669 ret = s->lock_f(dev, s);
1670#endif
1671
1672 return ret;
1673}
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691static int do_unlock_ioctl(struct comedi_device *dev, unsigned int arg,
1692 void *file)
1693{
1694 struct comedi_subdevice *s;
1695
1696 if (arg >= dev->n_subdevices)
1697 return -EINVAL;
1698 s = &dev->subdevices[arg];
1699
1700 if (s->busy)
1701 return -EBUSY;
1702
1703 if (s->lock && s->lock != file)
1704 return -EACCES;
1705
1706 if (s->lock == file) {
1707#if 0
1708 if (s->unlock)
1709 s->unlock(dev, s);
1710#endif
1711
1712 s->lock = NULL;
1713 }
1714
1715 return 0;
1716}
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732static int do_cancel_ioctl(struct comedi_device *dev, unsigned int arg,
1733 void *file)
1734{
1735 struct comedi_subdevice *s;
1736 int ret;
1737
1738 if (arg >= dev->n_subdevices)
1739 return -EINVAL;
1740 s = &dev->subdevices[arg];
1741 if (s->async == NULL)
1742 return -EINVAL;
1743
1744 if (s->lock && s->lock != file)
1745 return -EACCES;
1746
1747 if (!s->busy)
1748 return 0;
1749
1750 if (s->busy != file)
1751 return -EBUSY;
1752
1753 ret = do_cancel(dev, s);
1754
1755 return ret;
1756}
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772static int do_poll_ioctl(struct comedi_device *dev, unsigned int arg,
1773 void *file)
1774{
1775 struct comedi_subdevice *s;
1776
1777 if (arg >= dev->n_subdevices)
1778 return -EINVAL;
1779 s = &dev->subdevices[arg];
1780
1781 if (s->lock && s->lock != file)
1782 return -EACCES;
1783
1784 if (!s->busy)
1785 return 0;
1786
1787 if (s->busy != file)
1788 return -EBUSY;
1789
1790 if (s->poll)
1791 return s->poll(dev, s);
1792
1793 return -EINVAL;
1794}
1795
1796static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
1797 unsigned long arg)
1798{
1799 const unsigned minor = iminor(file_inode(file));
1800 struct comedi_device *dev = file->private_data;
1801 int rc;
1802
1803 mutex_lock(&dev->mutex);
1804
1805
1806
1807 if (cmd == COMEDI_DEVCONFIG) {
1808 if (minor >= COMEDI_NUM_BOARD_MINORS) {
1809
1810 rc = -ENOTTY;
1811 goto done;
1812 }
1813 rc = do_devconfig_ioctl(dev,
1814 (struct comedi_devconfig __user *)arg);
1815 if (rc == 0) {
1816 if (arg == 0 &&
1817 dev->minor >= comedi_num_legacy_minors) {
1818
1819
1820 if (comedi_clear_board_dev(dev)) {
1821 mutex_unlock(&dev->mutex);
1822 comedi_free_board_dev(dev);
1823 return rc;
1824 }
1825 }
1826 }
1827 goto done;
1828 }
1829
1830 if (!dev->attached) {
1831 dev_dbg(dev->class_dev, "no driver attached\n");
1832 rc = -ENODEV;
1833 goto done;
1834 }
1835
1836 switch (cmd) {
1837 case COMEDI_BUFCONFIG:
1838 rc = do_bufconfig_ioctl(dev,
1839 (struct comedi_bufconfig __user *)arg);
1840 break;
1841 case COMEDI_DEVINFO:
1842 rc = do_devinfo_ioctl(dev, (struct comedi_devinfo __user *)arg,
1843 file);
1844 break;
1845 case COMEDI_SUBDINFO:
1846 rc = do_subdinfo_ioctl(dev,
1847 (struct comedi_subdinfo __user *)arg,
1848 file);
1849 break;
1850 case COMEDI_CHANINFO:
1851 rc = do_chaninfo_ioctl(dev, (void __user *)arg);
1852 break;
1853 case COMEDI_RANGEINFO:
1854 rc = do_rangeinfo_ioctl(dev, (void __user *)arg);
1855 break;
1856 case COMEDI_BUFINFO:
1857 rc = do_bufinfo_ioctl(dev,
1858 (struct comedi_bufinfo __user *)arg,
1859 file);
1860 break;
1861 case COMEDI_LOCK:
1862 rc = do_lock_ioctl(dev, arg, file);
1863 break;
1864 case COMEDI_UNLOCK:
1865 rc = do_unlock_ioctl(dev, arg, file);
1866 break;
1867 case COMEDI_CANCEL:
1868 rc = do_cancel_ioctl(dev, arg, file);
1869 break;
1870 case COMEDI_CMD:
1871 rc = do_cmd_ioctl(dev, (struct comedi_cmd __user *)arg, file);
1872 break;
1873 case COMEDI_CMDTEST:
1874 rc = do_cmdtest_ioctl(dev, (struct comedi_cmd __user *)arg,
1875 file);
1876 break;
1877 case COMEDI_INSNLIST:
1878 rc = do_insnlist_ioctl(dev,
1879 (struct comedi_insnlist __user *)arg,
1880 file);
1881 break;
1882 case COMEDI_INSN:
1883 rc = do_insn_ioctl(dev, (struct comedi_insn __user *)arg,
1884 file);
1885 break;
1886 case COMEDI_POLL:
1887 rc = do_poll_ioctl(dev, arg, file);
1888 break;
1889 default:
1890 rc = -ENOTTY;
1891 break;
1892 }
1893
1894done:
1895 mutex_unlock(&dev->mutex);
1896 return rc;
1897}
1898
1899static void comedi_vm_open(struct vm_area_struct *area)
1900{
1901 struct comedi_buf_map *bm;
1902
1903 bm = area->vm_private_data;
1904 comedi_buf_map_get(bm);
1905}
1906
1907static void comedi_vm_close(struct vm_area_struct *area)
1908{
1909 struct comedi_buf_map *bm;
1910
1911 bm = area->vm_private_data;
1912 comedi_buf_map_put(bm);
1913}
1914
1915static struct vm_operations_struct comedi_vm_ops = {
1916 .open = comedi_vm_open,
1917 .close = comedi_vm_close,
1918};
1919
1920static int comedi_mmap(struct file *file, struct vm_area_struct *vma)
1921{
1922 const unsigned minor = iminor(file_inode(file));
1923 struct comedi_device *dev = file->private_data;
1924 struct comedi_subdevice *s;
1925 struct comedi_async *async;
1926 struct comedi_buf_map *bm;
1927 unsigned long start = vma->vm_start;
1928 unsigned long size;
1929 int n_pages;
1930 int i;
1931 int retval;
1932
1933 mutex_lock(&dev->mutex);
1934
1935 if (!dev->attached) {
1936 dev_dbg(dev->class_dev, "no driver attached\n");
1937 retval = -ENODEV;
1938 goto done;
1939 }
1940
1941 if (vma->vm_flags & VM_WRITE)
1942 s = comedi_write_subdevice(dev, minor);
1943 else
1944 s = comedi_read_subdevice(dev, minor);
1945 if (!s) {
1946 retval = -EINVAL;
1947 goto done;
1948 }
1949
1950 async = s->async;
1951 if (!async) {
1952 retval = -EINVAL;
1953 goto done;
1954 }
1955
1956 if (vma->vm_pgoff != 0) {
1957 dev_dbg(dev->class_dev, "mmap() offset must be 0.\n");
1958 retval = -EINVAL;
1959 goto done;
1960 }
1961
1962 size = vma->vm_end - vma->vm_start;
1963 if (size > async->prealloc_bufsz) {
1964 retval = -EFAULT;
1965 goto done;
1966 }
1967 if (size & (~PAGE_MASK)) {
1968 retval = -EFAULT;
1969 goto done;
1970 }
1971
1972 n_pages = size >> PAGE_SHIFT;
1973 bm = async->buf_map;
1974 if (!bm || n_pages > bm->n_pages) {
1975 retval = -EINVAL;
1976 goto done;
1977 }
1978 for (i = 0; i < n_pages; ++i) {
1979 struct comedi_buf_page *buf = &bm->page_list[i];
1980
1981 if (remap_pfn_range(vma, start,
1982 page_to_pfn(virt_to_page(buf->virt_addr)),
1983 PAGE_SIZE, PAGE_SHARED)) {
1984 retval = -EAGAIN;
1985 goto done;
1986 }
1987 start += PAGE_SIZE;
1988 }
1989
1990 vma->vm_ops = &comedi_vm_ops;
1991 vma->vm_private_data = bm;
1992
1993 vma->vm_ops->open(vma);
1994
1995 retval = 0;
1996done:
1997 mutex_unlock(&dev->mutex);
1998 return retval;
1999}
2000
2001static unsigned int comedi_poll(struct file *file, poll_table *wait)
2002{
2003 unsigned int mask = 0;
2004 const unsigned minor = iminor(file_inode(file));
2005 struct comedi_device *dev = file->private_data;
2006 struct comedi_subdevice *s;
2007
2008 mutex_lock(&dev->mutex);
2009
2010 if (!dev->attached) {
2011 dev_dbg(dev->class_dev, "no driver attached\n");
2012 goto done;
2013 }
2014
2015 s = comedi_read_subdevice(dev, minor);
2016 if (s && s->async) {
2017 poll_wait(file, &s->async->wait_head, wait);
2018 if (!s->busy || !comedi_is_subdevice_running(s) ||
2019 comedi_buf_read_n_available(s->async) > 0)
2020 mask |= POLLIN | POLLRDNORM;
2021 }
2022
2023 s = comedi_write_subdevice(dev, minor);
2024 if (s && s->async) {
2025 unsigned int bps = bytes_per_sample(s->async->subdevice);
2026
2027 poll_wait(file, &s->async->wait_head, wait);
2028 comedi_buf_write_alloc(s->async, s->async->prealloc_bufsz);
2029 if (!s->busy || !comedi_is_subdevice_running(s) ||
2030 comedi_buf_write_n_allocated(s->async) >= bps)
2031 mask |= POLLOUT | POLLWRNORM;
2032 }
2033
2034done:
2035 mutex_unlock(&dev->mutex);
2036 return mask;
2037}
2038
2039static ssize_t comedi_write(struct file *file, const char __user *buf,
2040 size_t nbytes, loff_t *offset)
2041{
2042 struct comedi_subdevice *s;
2043 struct comedi_async *async;
2044 int n, m, count = 0, retval = 0;
2045 DECLARE_WAITQUEUE(wait, current);
2046 const unsigned minor = iminor(file_inode(file));
2047 struct comedi_device *dev = file->private_data;
2048 bool on_wait_queue = false;
2049 bool attach_locked;
2050 unsigned int old_detach_count;
2051
2052
2053 down_read(&dev->attach_lock);
2054 attach_locked = true;
2055 old_detach_count = dev->detach_count;
2056
2057 if (!dev->attached) {
2058 dev_dbg(dev->class_dev, "no driver attached\n");
2059 retval = -ENODEV;
2060 goto out;
2061 }
2062
2063 s = comedi_write_subdevice(dev, minor);
2064 if (!s || !s->async) {
2065 retval = -EIO;
2066 goto out;
2067 }
2068
2069 async = s->async;
2070
2071 if (!s->busy || !nbytes)
2072 goto out;
2073 if (s->busy != file) {
2074 retval = -EACCES;
2075 goto out;
2076 }
2077
2078 add_wait_queue(&async->wait_head, &wait);
2079 on_wait_queue = true;
2080 while (nbytes > 0 && !retval) {
2081 set_current_state(TASK_INTERRUPTIBLE);
2082
2083 if (!comedi_is_subdevice_running(s)) {
2084 if (count == 0) {
2085 struct comedi_subdevice *new_s;
2086
2087 if (comedi_is_subdevice_in_error(s))
2088 retval = -EPIPE;
2089 else
2090 retval = 0;
2091
2092
2093
2094
2095
2096
2097
2098 remove_wait_queue(&async->wait_head, &wait);
2099 on_wait_queue = false;
2100 up_read(&dev->attach_lock);
2101 attach_locked = false;
2102 mutex_lock(&dev->mutex);
2103
2104
2105
2106
2107
2108
2109
2110
2111 new_s = comedi_write_subdevice(dev, minor);
2112 if (dev->attached &&
2113 old_detach_count == dev->detach_count &&
2114 s == new_s && new_s->async == async)
2115 do_become_nonbusy(dev, s);
2116 mutex_unlock(&dev->mutex);
2117 }
2118 break;
2119 }
2120
2121 n = nbytes;
2122
2123 m = n;
2124 if (async->buf_write_ptr + m > async->prealloc_bufsz)
2125 m = async->prealloc_bufsz - async->buf_write_ptr;
2126 comedi_buf_write_alloc(async, async->prealloc_bufsz);
2127 if (m > comedi_buf_write_n_allocated(async))
2128 m = comedi_buf_write_n_allocated(async);
2129 if (m < n)
2130 n = m;
2131
2132 if (n == 0) {
2133 if (file->f_flags & O_NONBLOCK) {
2134 retval = -EAGAIN;
2135 break;
2136 }
2137 schedule();
2138 if (signal_pending(current)) {
2139 retval = -ERESTARTSYS;
2140 break;
2141 }
2142 if (!s->busy)
2143 break;
2144 if (s->busy != file) {
2145 retval = -EACCES;
2146 break;
2147 }
2148 continue;
2149 }
2150
2151 m = copy_from_user(async->prealloc_buf + async->buf_write_ptr,
2152 buf, n);
2153 if (m) {
2154 n -= m;
2155 retval = -EFAULT;
2156 }
2157 comedi_buf_write_free(async, n);
2158
2159 count += n;
2160 nbytes -= n;
2161
2162 buf += n;
2163 break;
2164 }
2165out:
2166 if (on_wait_queue)
2167 remove_wait_queue(&async->wait_head, &wait);
2168 set_current_state(TASK_RUNNING);
2169 if (attach_locked)
2170 up_read(&dev->attach_lock);
2171
2172 return count ? count : retval;
2173}
2174
2175static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes,
2176 loff_t *offset)
2177{
2178 struct comedi_subdevice *s;
2179 struct comedi_async *async;
2180 int n, m, count = 0, retval = 0;
2181 DECLARE_WAITQUEUE(wait, current);
2182 const unsigned minor = iminor(file_inode(file));
2183 struct comedi_device *dev = file->private_data;
2184 unsigned int old_detach_count;
2185 bool become_nonbusy = false;
2186 bool attach_locked;
2187
2188
2189 down_read(&dev->attach_lock);
2190 attach_locked = true;
2191 old_detach_count = dev->detach_count;
2192
2193 if (!dev->attached) {
2194 dev_dbg(dev->class_dev, "no driver attached\n");
2195 retval = -ENODEV;
2196 goto out;
2197 }
2198
2199 s = comedi_read_subdevice(dev, minor);
2200 if (!s || !s->async) {
2201 retval = -EIO;
2202 goto out;
2203 }
2204
2205 async = s->async;
2206 if (!s->busy || !nbytes)
2207 goto out;
2208 if (s->busy != file) {
2209 retval = -EACCES;
2210 goto out;
2211 }
2212
2213 add_wait_queue(&async->wait_head, &wait);
2214 while (nbytes > 0 && !retval) {
2215 set_current_state(TASK_INTERRUPTIBLE);
2216
2217 n = nbytes;
2218
2219 m = comedi_buf_read_n_available(async);
2220
2221 if (async->buf_read_ptr + m > async->prealloc_bufsz)
2222 m = async->prealloc_bufsz - async->buf_read_ptr;
2223
2224 if (m < n)
2225 n = m;
2226
2227 if (n == 0) {
2228 if (!comedi_is_subdevice_running(s)) {
2229 if (comedi_is_subdevice_in_error(s))
2230 retval = -EPIPE;
2231 else
2232 retval = 0;
2233 become_nonbusy = true;
2234 break;
2235 }
2236 if (file->f_flags & O_NONBLOCK) {
2237 retval = -EAGAIN;
2238 break;
2239 }
2240 schedule();
2241 if (signal_pending(current)) {
2242 retval = -ERESTARTSYS;
2243 break;
2244 }
2245 if (!s->busy) {
2246 retval = 0;
2247 break;
2248 }
2249 if (s->busy != file) {
2250 retval = -EACCES;
2251 break;
2252 }
2253 continue;
2254 }
2255 m = copy_to_user(buf, async->prealloc_buf +
2256 async->buf_read_ptr, n);
2257 if (m) {
2258 n -= m;
2259 retval = -EFAULT;
2260 }
2261
2262 comedi_buf_read_alloc(async, n);
2263 comedi_buf_read_free(async, n);
2264
2265 count += n;
2266 nbytes -= n;
2267
2268 buf += n;
2269 break;
2270 }
2271 remove_wait_queue(&async->wait_head, &wait);
2272 set_current_state(TASK_RUNNING);
2273 if (become_nonbusy || comedi_is_subdevice_idle(s)) {
2274 struct comedi_subdevice *new_s;
2275
2276
2277
2278
2279
2280 up_read(&dev->attach_lock);
2281 attach_locked = false;
2282 mutex_lock(&dev->mutex);
2283
2284
2285
2286
2287
2288
2289
2290 new_s = comedi_read_subdevice(dev, minor);
2291 if (dev->attached && old_detach_count == dev->detach_count &&
2292 s == new_s && new_s->async == async) {
2293 if (become_nonbusy ||
2294 async->buf_read_count - async->buf_write_count == 0)
2295 do_become_nonbusy(dev, s);
2296 }
2297 mutex_unlock(&dev->mutex);
2298 }
2299out:
2300 if (attach_locked)
2301 up_read(&dev->attach_lock);
2302
2303 return count ? count : retval;
2304}
2305
2306static int comedi_open(struct inode *inode, struct file *file)
2307{
2308 const unsigned minor = iminor(inode);
2309 struct comedi_device *dev = comedi_dev_get_from_minor(minor);
2310 int rc;
2311
2312 if (!dev) {
2313 pr_debug("invalid minor number\n");
2314 return -ENODEV;
2315 }
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330 mutex_lock(&dev->mutex);
2331 if (dev->attached)
2332 goto ok;
2333 if (!capable(CAP_NET_ADMIN) && dev->in_request_module) {
2334 dev_dbg(dev->class_dev, "in request module\n");
2335 rc = -ENODEV;
2336 goto out;
2337 }
2338 if (capable(CAP_NET_ADMIN) && dev->in_request_module)
2339 goto ok;
2340
2341 dev->in_request_module = true;
2342
2343#ifdef CONFIG_KMOD
2344 mutex_unlock(&dev->mutex);
2345 request_module("char-major-%i-%i", COMEDI_MAJOR, dev->minor);
2346 mutex_lock(&dev->mutex);
2347#endif
2348
2349 dev->in_request_module = false;
2350
2351 if (!dev->attached && !capable(CAP_NET_ADMIN)) {
2352 dev_dbg(dev->class_dev, "not attached and not CAP_NET_ADMIN\n");
2353 rc = -ENODEV;
2354 goto out;
2355 }
2356ok:
2357 if (dev->attached && dev->use_count == 0) {
2358 if (!try_module_get(dev->driver->module)) {
2359 rc = -ENOSYS;
2360 goto out;
2361 }
2362 if (dev->open) {
2363 rc = dev->open(dev);
2364 if (rc < 0) {
2365 module_put(dev->driver->module);
2366 goto out;
2367 }
2368 }
2369 }
2370
2371 dev->use_count++;
2372 file->private_data = dev;
2373 rc = 0;
2374
2375out:
2376 mutex_unlock(&dev->mutex);
2377 if (rc)
2378 comedi_dev_put(dev);
2379 return rc;
2380}
2381
2382static int comedi_fasync(int fd, struct file *file, int on)
2383{
2384 struct comedi_device *dev = file->private_data;
2385
2386 return fasync_helper(fd, file, on, &dev->async_queue);
2387}
2388
2389static int comedi_close(struct inode *inode, struct file *file)
2390{
2391 struct comedi_device *dev = file->private_data;
2392 struct comedi_subdevice *s = NULL;
2393 int i;
2394
2395 mutex_lock(&dev->mutex);
2396
2397 if (dev->subdevices) {
2398 for (i = 0; i < dev->n_subdevices; i++) {
2399 s = &dev->subdevices[i];
2400
2401 if (s->busy == file)
2402 do_cancel(dev, s);
2403 if (s->lock == file)
2404 s->lock = NULL;
2405 }
2406 }
2407 if (dev->attached && dev->use_count == 1) {
2408 if (dev->close)
2409 dev->close(dev);
2410 module_put(dev->driver->module);
2411 }
2412
2413 dev->use_count--;
2414
2415 mutex_unlock(&dev->mutex);
2416 comedi_dev_put(dev);
2417
2418 return 0;
2419}
2420
2421static const struct file_operations comedi_fops = {
2422 .owner = THIS_MODULE,
2423 .unlocked_ioctl = comedi_unlocked_ioctl,
2424 .compat_ioctl = comedi_compat_ioctl,
2425 .open = comedi_open,
2426 .release = comedi_close,
2427 .read = comedi_read,
2428 .write = comedi_write,
2429 .mmap = comedi_mmap,
2430 .poll = comedi_poll,
2431 .fasync = comedi_fasync,
2432 .llseek = noop_llseek,
2433};
2434
2435void comedi_error(const struct comedi_device *dev, const char *s)
2436{
2437 dev_err(dev->class_dev, "%s: %s\n", dev->driver->driver_name, s);
2438}
2439EXPORT_SYMBOL_GPL(comedi_error);
2440
2441void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s)
2442{
2443 struct comedi_async *async = s->async;
2444 unsigned runflags = 0;
2445 unsigned runflags_mask = 0;
2446
2447 if (!comedi_is_subdevice_running(s))
2448 return;
2449
2450 if (s->
2451 async->events & (COMEDI_CB_EOA | COMEDI_CB_ERROR |
2452 COMEDI_CB_OVERFLOW)) {
2453 runflags_mask |= SRF_RUNNING;
2454 }
2455
2456
2457 if (s->async->events & (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)) {
2458 runflags_mask |= SRF_ERROR;
2459 runflags |= SRF_ERROR;
2460 }
2461 if (runflags_mask) {
2462
2463 comedi_set_subdevice_runflags(s, runflags_mask, runflags);
2464 }
2465
2466 if (async->cb_mask & s->async->events) {
2467 wake_up_interruptible(&async->wait_head);
2468 if (s->subdev_flags & SDF_CMD_READ)
2469 kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
2470 if (s->subdev_flags & SDF_CMD_WRITE)
2471 kill_fasync(&dev->async_queue, SIGIO, POLL_OUT);
2472 }
2473 s->async->events = 0;
2474}
2475EXPORT_SYMBOL_GPL(comedi_event);
2476
2477
2478struct comedi_device *comedi_alloc_board_minor(struct device *hardware_device)
2479{
2480 struct comedi_device *dev;
2481 struct device *csdev;
2482 unsigned i;
2483
2484 dev = kzalloc(sizeof(struct comedi_device), GFP_KERNEL);
2485 if (dev == NULL)
2486 return ERR_PTR(-ENOMEM);
2487 comedi_device_init(dev);
2488 comedi_set_hw_dev(dev, hardware_device);
2489 mutex_lock(&dev->mutex);
2490 mutex_lock(&comedi_board_minor_table_lock);
2491 for (i = hardware_device ? comedi_num_legacy_minors : 0;
2492 i < COMEDI_NUM_BOARD_MINORS; ++i) {
2493 if (comedi_board_minor_table[i] == NULL) {
2494 comedi_board_minor_table[i] = dev;
2495 break;
2496 }
2497 }
2498 mutex_unlock(&comedi_board_minor_table_lock);
2499 if (i == COMEDI_NUM_BOARD_MINORS) {
2500 mutex_unlock(&dev->mutex);
2501 comedi_device_cleanup(dev);
2502 comedi_dev_put(dev);
2503 pr_err("comedi: error: ran out of minor numbers for board device files.\n");
2504 return ERR_PTR(-EBUSY);
2505 }
2506 dev->minor = i;
2507 csdev = device_create(comedi_class, hardware_device,
2508 MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i", i);
2509 if (!IS_ERR(csdev))
2510 dev->class_dev = get_device(csdev);
2511
2512
2513 return dev;
2514}
2515
2516static void comedi_free_board_minor(unsigned minor)
2517{
2518 BUG_ON(minor >= COMEDI_NUM_BOARD_MINORS);
2519 comedi_free_board_dev(comedi_clear_board_minor(minor));
2520}
2521
2522void comedi_release_hardware_device(struct device *hardware_device)
2523{
2524 int minor;
2525 struct comedi_device *dev;
2526
2527 for (minor = comedi_num_legacy_minors; minor < COMEDI_NUM_BOARD_MINORS;
2528 minor++) {
2529 mutex_lock(&comedi_board_minor_table_lock);
2530 dev = comedi_board_minor_table[minor];
2531 if (dev && dev->hw_dev == hardware_device) {
2532 comedi_board_minor_table[minor] = NULL;
2533 mutex_unlock(&comedi_board_minor_table_lock);
2534 comedi_free_board_dev(dev);
2535 break;
2536 }
2537 mutex_unlock(&comedi_board_minor_table_lock);
2538 }
2539}
2540
2541int comedi_alloc_subdevice_minor(struct comedi_subdevice *s)
2542{
2543 struct comedi_device *dev = s->device;
2544 struct device *csdev;
2545 unsigned i;
2546
2547 mutex_lock(&comedi_subdevice_minor_table_lock);
2548 for (i = 0; i < COMEDI_NUM_SUBDEVICE_MINORS; ++i) {
2549 if (comedi_subdevice_minor_table[i] == NULL) {
2550 comedi_subdevice_minor_table[i] = s;
2551 break;
2552 }
2553 }
2554 mutex_unlock(&comedi_subdevice_minor_table_lock);
2555 if (i == COMEDI_NUM_SUBDEVICE_MINORS) {
2556 pr_err("comedi: error: ran out of minor numbers for subdevice files.\n");
2557 return -EBUSY;
2558 }
2559 i += COMEDI_NUM_BOARD_MINORS;
2560 s->minor = i;
2561 csdev = device_create(comedi_class, dev->class_dev,
2562 MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i_subd%i",
2563 dev->minor, s->index);
2564 if (!IS_ERR(csdev))
2565 s->class_dev = csdev;
2566
2567 return 0;
2568}
2569
2570void comedi_free_subdevice_minor(struct comedi_subdevice *s)
2571{
2572 unsigned int i;
2573
2574 if (s == NULL)
2575 return;
2576 if (s->minor < 0)
2577 return;
2578
2579 BUG_ON(s->minor >= COMEDI_NUM_MINORS);
2580 BUG_ON(s->minor < COMEDI_NUM_BOARD_MINORS);
2581
2582 i = s->minor - COMEDI_NUM_BOARD_MINORS;
2583 mutex_lock(&comedi_subdevice_minor_table_lock);
2584 if (s == comedi_subdevice_minor_table[i])
2585 comedi_subdevice_minor_table[i] = NULL;
2586 mutex_unlock(&comedi_subdevice_minor_table_lock);
2587 if (s->class_dev) {
2588 device_destroy(comedi_class, MKDEV(COMEDI_MAJOR, s->minor));
2589 s->class_dev = NULL;
2590 }
2591}
2592
2593static void comedi_cleanup_board_minors(void)
2594{
2595 unsigned i;
2596
2597 for (i = 0; i < COMEDI_NUM_BOARD_MINORS; i++)
2598 comedi_free_board_minor(i);
2599}
2600
2601static int __init comedi_init(void)
2602{
2603 int i;
2604 int retval;
2605
2606 pr_info("comedi: version " COMEDI_RELEASE " - http://www.comedi.org\n");
2607
2608 if (comedi_num_legacy_minors < 0 ||
2609 comedi_num_legacy_minors > COMEDI_NUM_BOARD_MINORS) {
2610 pr_err("comedi: error: invalid value for module parameter \"comedi_num_legacy_minors\". Valid values are 0 through %i.\n",
2611 COMEDI_NUM_BOARD_MINORS);
2612 return -EINVAL;
2613 }
2614
2615 retval = register_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
2616 COMEDI_NUM_MINORS, "comedi");
2617 if (retval)
2618 return -EIO;
2619 cdev_init(&comedi_cdev, &comedi_fops);
2620 comedi_cdev.owner = THIS_MODULE;
2621 kobject_set_name(&comedi_cdev.kobj, "comedi");
2622 if (cdev_add(&comedi_cdev, MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS)) {
2623 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
2624 COMEDI_NUM_MINORS);
2625 return -EIO;
2626 }
2627 comedi_class = class_create(THIS_MODULE, "comedi");
2628 if (IS_ERR(comedi_class)) {
2629 pr_err("comedi: failed to create class\n");
2630 cdev_del(&comedi_cdev);
2631 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
2632 COMEDI_NUM_MINORS);
2633 return PTR_ERR(comedi_class);
2634 }
2635
2636 comedi_class->dev_groups = comedi_dev_groups;
2637
2638
2639 comedi_proc_init();
2640
2641
2642 for (i = 0; i < comedi_num_legacy_minors; i++) {
2643 struct comedi_device *dev;
2644 dev = comedi_alloc_board_minor(NULL);
2645 if (IS_ERR(dev)) {
2646 comedi_cleanup_board_minors();
2647 cdev_del(&comedi_cdev);
2648 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
2649 COMEDI_NUM_MINORS);
2650 return PTR_ERR(dev);
2651 } else {
2652
2653 mutex_unlock(&dev->mutex);
2654 }
2655 }
2656
2657 return 0;
2658}
2659module_init(comedi_init);
2660
2661static void __exit comedi_cleanup(void)
2662{
2663 int i;
2664
2665 comedi_cleanup_board_minors();
2666 for (i = 0; i < COMEDI_NUM_BOARD_MINORS; ++i)
2667 BUG_ON(comedi_board_minor_table[i]);
2668 for (i = 0; i < COMEDI_NUM_SUBDEVICE_MINORS; ++i)
2669 BUG_ON(comedi_subdevice_minor_table[i]);
2670
2671 class_destroy(comedi_class);
2672 cdev_del(&comedi_cdev);
2673 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS);
2674
2675 comedi_proc_cleanup();
2676}
2677module_exit(comedi_cleanup);
2678
2679MODULE_AUTHOR("http://www.comedi.org");
2680MODULE_DESCRIPTION("Comedi core module");
2681MODULE_LICENSE("GPL");
2682