blob: bacfa2b4b22914dac79ba8ed097cde71b08049e5 [file] [log] [blame]
David Schleefed9eccb2008-11-04 20:29:31 -08001/*
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
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -080048#include <linux/io.h>
49#include <linux/uaccess.h>
David Schleefed9eccb2008-11-04 20:29:31 -080050
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -080051/* #include "kvmem.h" */
David Schleefed9eccb2008-11-04 20:29:31 -080052
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
Ian Abbott6a9d7a22008-12-08 17:05:50 +000062int comedi_autoconfig = 1;
63module_param(comedi_autoconfig, bool, 0444);
64
Bernd Porr1dd33ab2008-12-08 23:30:13 +000065int comedi_num_legacy_minors = 0;
66module_param(comedi_num_legacy_minors, int, 0444);
67
David Schleefed9eccb2008-11-04 20:29:31 -080068static DEFINE_SPINLOCK(comedi_file_info_table_lock);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -080069static struct comedi_device_file_info
70 *comedi_file_info_table[COMEDI_NUM_MINORS];
David Schleefed9eccb2008-11-04 20:29:31 -080071
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -080072static 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);
David Schleefed9eccb2008-11-04 20:29:31 -080088
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -080089extern void do_become_nonbusy(comedi_device *dev, comedi_subdevice *s);
90static int do_cancel(comedi_device *dev, comedi_subdevice *s);
David Schleefed9eccb2008-11-04 20:29:31 -080091
92static int comedi_fasync(int fd, struct file *file, int on);
93
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -080094static int is_device_busy(comedi_device *dev);
David Schleefed9eccb2008-11-04 20:29:31 -080095
96#ifdef HAVE_UNLOCKED_IOCTL
97static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -080098 unsigned long arg)
David Schleefed9eccb2008-11-04 20:29:31 -080099#else
100static int comedi_ioctl(struct inode *inode, struct file *file,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800101 unsigned int cmd, unsigned long arg)
David Schleefed9eccb2008-11-04 20:29:31 -0800102#endif
103{
104 const unsigned minor = iminor(file->f_dentry->d_inode);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800105 struct comedi_device_file_info *dev_file_info =
106 comedi_get_device_file_info(minor);
David Schleefed9eccb2008-11-04 20:29:31 -0800107 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
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800173done:
David Schleefed9eccb2008-11-04 20:29:31 -0800174 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*/
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800191static int do_devconfig_ioctl(comedi_device *dev, comedi_devconfig *arg)
David Schleefed9eccb2008-11-04 20:29:31 -0800192{
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;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800204 if (dev->attached) {
David Schleefed9eccb2008-11-04 20:29:31 -0800205 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) &&
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800218 it.options[COMEDI_DEVCONF_AUX_DATA_LENGTH]) {
David Schleefed9eccb2008-11-04 20:29:31 -0800219 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,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800229 comedi_aux_data(it.options, 0), aux_len)) {
David Schleefed9eccb2008-11-04 20:29:31 -0800230 vfree(aux_data);
231 return -EFAULT;
232 }
233 it.options[COMEDI_DEVCONF_AUX_DATA_LO] =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800234 (unsigned long)aux_data;
David Schleefed9eccb2008-11-04 20:29:31 -0800235 if (sizeof(void *) > sizeof(int)) {
236 bit_shift = sizeof(int) * 8;
237 it.options[COMEDI_DEVCONF_AUX_DATA_HI] =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800238 ((unsigned long)aux_data) >> bit_shift;
David Schleefed9eccb2008-11-04 20:29:31 -0800239 } else
240 it.options[COMEDI_DEVCONF_AUX_DATA_HI] = 0;
241 }
242
243 ret = comedi_device_attach(dev, &it);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800244 if (ret == 0) {
245 if (!try_module_get(dev->driver->module)) {
David Schleefed9eccb2008-11-04 20:29:31 -0800246 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*/
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800271static int do_bufconfig_ioctl(comedi_device *dev, void *arg)
David Schleefed9eccb2008-11-04 20:29:31 -0800272{
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
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800338copyback:
David Schleefed9eccb2008-11-04 20:29:31 -0800339 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*/
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800359static int do_devinfo_ioctl(comedi_device *dev, comedi_devinfo *arg,
360 struct file *file)
David Schleefed9eccb2008-11-04 20:29:31 -0800361{
362 comedi_devinfo devinfo;
363 const unsigned minor = iminor(file->f_dentry->d_inode);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800364 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);
David Schleefed9eccb2008-11-04 20:29:31 -0800370
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
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800379 if (read_subdev)
David Schleefed9eccb2008-11-04 20:29:31 -0800380 devinfo.read_subdevice = read_subdev - dev->subdevices;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800381 else
David Schleefed9eccb2008-11-04 20:29:31 -0800382 devinfo.read_subdevice = -1;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800383
384 if (write_subdev)
David Schleefed9eccb2008-11-04 20:29:31 -0800385 devinfo.write_subdevice = write_subdev - dev->subdevices;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800386 else
David Schleefed9eccb2008-11-04 20:29:31 -0800387 devinfo.write_subdevice = -1;
David Schleefed9eccb2008-11-04 20:29:31 -0800388
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*/
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800409static int do_subdinfo_ioctl(comedi_device *dev, comedi_subdinfo *arg,
410 void *file)
David Schleefed9eccb2008-11-04 20:29:31 -0800411{
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 =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800436 (i << 24) | (0 << 16) | (s->range_table->length);
David Schleefed9eccb2008-11-04 20:29:31 -0800437 } 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,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800468 dev->n_subdevices * sizeof(comedi_subdinfo));
David Schleefed9eccb2008-11-04 20:29:31 -0800469
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*/
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800489static int do_chaninfo_ioctl(comedi_device *dev, comedi_chaninfo *arg)
David Schleefed9eccb2008-11-04 20:29:31 -0800490{
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,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800505 s->n_chan * sizeof(lsampl_t)))
David Schleefed9eccb2008-11-04 20:29:31 -0800506 return -EFAULT;
507 }
508
509 if (it.flaglist) {
510 if (!s->flaglist)
511 return -EINVAL;
512 if (copy_to_user(it.flaglist, s->flaglist,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800513 s->n_chan * sizeof(unsigned int)))
David Schleefed9eccb2008-11-04 20:29:31 -0800514 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) |
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800526 (s->range_table_list[i]->length);
David Schleefed9eccb2008-11-04 20:29:31 -0800527 put_user(x, it.rangelist + i);
528 }
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800529#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
David Schleefed9eccb2008-11-04 20:29:31 -0800534 }
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 */
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800553static int do_bufinfo_ioctl(comedi_device *dev, void *arg)
David Schleefed9eccb2008-11-04 20:29:31 -0800554{
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 |
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800582 SRF_RUNNING))
583 && async->buf_write_count == async->buf_read_count) {
David Schleefed9eccb2008-11-04 20:29:31 -0800584 do_become_nonbusy(dev, s);
585 }
586 }
587
588 if (bi.bytes_written && (s->subdev_flags & SDF_CMD_WRITE)) {
589 bi.bytes_written =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800590 comedi_buf_write_alloc(async, bi.bytes_written);
David Schleefed9eccb2008-11-04 20:29:31 -0800591 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
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800599copyback:
David Schleefed9eccb2008-11-04 20:29:31 -0800600 if (copy_to_user(arg, &bi, sizeof(comedi_bufinfo)))
601 return -EFAULT;
602
603 return 0;
604}
605
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800606static int parse_insn(comedi_device *dev, comedi_insn *insn, lsampl_t *data,
607 void *file);
David Schleefed9eccb2008-11-04 20:29:31 -0800608/*
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
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800625static int do_insnlist_ioctl(comedi_device *dev, void *arg, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -0800626{
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,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800651 sizeof(comedi_insn) * insnlist.n_insns)) {
David Schleefed9eccb2008-11-04 20:29:31 -0800652 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,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800665 insns[i].n * sizeof(lsampl_t))) {
David Schleefed9eccb2008-11-04 20:29:31 -0800666 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,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800676 insns[i].n * sizeof(lsampl_t))) {
David Schleefed9eccb2008-11-04 20:29:31 -0800677 DPRINTK("copy_to_user failed\n");
678 ret = -EFAULT;
679 goto error;
680 }
681 }
682 if (need_resched())
683 schedule();
684 }
685
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800686error:
687 kfree(insns);
688 kfree(data);
David Schleefed9eccb2008-11-04 20:29:31 -0800689
690 if (ret < 0)
691 return ret;
692 return i;
693}
694
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800695static int check_insn_config_length(comedi_insn *insn, lsampl_t *data)
David Schleefed9eccb2008-11-04 20:29:31 -0800696{
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800697 if (insn->n < 1)
698 return -EINVAL;
David Schleefed9eccb2008-11-04 20:29:31 -0800699
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;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800742 /* by default we allow the insn since we don't have checks for
743 * all possible cases yet */
David Schleefed9eccb2008-11-04 20:29:31 -0800744 default:
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800745 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);
David Schleefed9eccb2008-11-04 20:29:31 -0800750 return 0;
751 break;
752 }
753 return -EINVAL;
754}
755
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800756static int parse_insn(comedi_device *dev, comedi_insn *insn, lsampl_t *data,
757 void *file)
David Schleefed9eccb2008-11-04 20:29:31 -0800758{
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
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800846 ret = check_chanlist(s, 1, &insn->chanspec);
847 if (ret < 0) {
David Schleefed9eccb2008-11-04 20:29:31 -0800848 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
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800865 ? s->maxdata_list[CR_CHAN(insn->chanspec)]
866 : s->maxdata;
David Schleefed9eccb2008-11-04 20:29:31 -0800867 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
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800898out:
David Schleefed9eccb2008-11-04 20:29:31 -0800899 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 */
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800916static int do_insn_ioctl(comedi_device *dev, void *arg, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -0800917{
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
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800953error:
954 kfree(data);
David Schleefed9eccb2008-11-04 20:29:31 -0800955
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*/
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800974static int do_cmd_ioctl(comedi_device *dev, void *arg, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -0800975{
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 }
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800986 /* save user's chanlist pointer so it can be restored later */
David Schleefed9eccb2008-11-04 20:29:31 -0800987 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
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001037 kfree(async->cmd.chanlist);
David Schleefed9eccb2008-11-04 20:29:31 -08001038 async->cmd = user_cmd;
1039 async->cmd.data = NULL;
1040 /* load channel/gain list */
1041 async->cmd.chanlist =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001042 kmalloc(async->cmd.chanlist_len * sizeof(int), GFP_KERNEL);
David Schleefed9eccb2008-11-04 20:29:31 -08001043 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,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001050 async->cmd.chanlist_len * sizeof(int))) {
David Schleefed9eccb2008-11-04 20:29:31 -08001051 DPRINTK("fault reading chanlist\n");
1052 ret = -EFAULT;
1053 goto cleanup;
1054 }
1055
1056 /* make sure each element in channel/gain list is valid */
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001057 ret = check_chanlist(s, async->cmd.chanlist_len, async->cmd.chanlist);
1058 if (ret < 0) {
David Schleefed9eccb2008-11-04 20:29:31 -08001059 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;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001068 /* restore chanlist pointer before copying back */
David Schleefed9eccb2008-11-04 20:29:31 -08001069 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 =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001089 COMEDI_CB_EOA | COMEDI_CB_BLOCK | COMEDI_CB_ERROR |
1090 COMEDI_CB_OVERFLOW;
1091 if (async->cmd.flags & TRIG_WAKE_EOS)
David Schleefed9eccb2008-11-04 20:29:31 -08001092 async->cb_mask |= COMEDI_CB_EOS;
David Schleefed9eccb2008-11-04 20:29:31 -08001093
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
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001107cleanup:
David Schleefed9eccb2008-11-04 20:29:31 -08001108 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*/
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001128static int do_cmdtest_ioctl(comedi_device *dev, void *arg, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -08001129{
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 }
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001140 /* save user's chanlist pointer so it can be restored later */
David Schleefed9eccb2008-11-04 20:29:31 -08001141 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 =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001171 kmalloc(user_cmd.chanlist_len * sizeof(int), GFP_KERNEL);
David Schleefed9eccb2008-11-04 20:29:31 -08001172 if (!chanlist) {
1173 DPRINTK("allocation failed\n");
1174 ret = -ENOMEM;
1175 goto cleanup;
1176 }
1177
1178 if (copy_from_user(chanlist, user_cmd.chanlist,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001179 user_cmd.chanlist_len * sizeof(int))) {
David Schleefed9eccb2008-11-04 20:29:31 -08001180 DPRINTK("fault reading chanlist\n");
1181 ret = -EFAULT;
1182 goto cleanup;
1183 }
1184
1185 /* make sure each element in channel/gain list is valid */
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001186 ret = check_chanlist(s, user_cmd.chanlist_len, chanlist);
1187 if (ret < 0) {
David Schleefed9eccb2008-11-04 20:29:31 -08001188 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
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001197 /* restore chanlist pointer before copying back */
David Schleefed9eccb2008-11-04 20:29:31 -08001198 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 }
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001205cleanup:
1206 kfree(chanlist);
David Schleefed9eccb2008-11-04 20:29:31 -08001207
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
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001226static int do_lock_ioctl(comedi_device *dev, unsigned int arg, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -08001227{
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);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001237 if (s->busy || s->lock)
David Schleefed9eccb2008-11-04 20:29:31 -08001238 ret = -EBUSY;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001239 else
David Schleefed9eccb2008-11-04 20:29:31 -08001240 s->lock = file;
David Schleefed9eccb2008-11-04 20:29:31 -08001241 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*/
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001270static int do_unlock_ioctl(comedi_device *dev, unsigned int arg, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -08001271{
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*/
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001310static int do_cancel_ioctl(comedi_device *dev, unsigned int arg, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -08001311{
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*/
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001346static int do_poll_ioctl(comedi_device *dev, unsigned int arg, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -08001347{
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
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001369static int do_cancel(comedi_device *dev, comedi_subdevice *s)
David Schleefed9eccb2008-11-04 20:29:31 -08001370{
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 = {
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001395 .close = comedi_unmap,
David Schleefed9eccb2008-11-04 20:29:31 -08001396};
1397
1398static int comedi_mmap(struct file *file, struct vm_area_struct *vma)
1399{
1400 const unsigned minor = iminor(file->f_dentry->d_inode);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001401 struct comedi_device_file_info *dev_file_info =
1402 comedi_get_device_file_info(minor);
David Schleefed9eccb2008-11-04 20:29:31 -08001403 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 }
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001418 if (vma->vm_flags & VM_WRITE)
David Schleefed9eccb2008-11-04 20:29:31 -08001419 s = comedi_get_write_subdevice(dev_file_info);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001420 else
David Schleefed9eccb2008-11-04 20:29:31 -08001421 s = comedi_get_read_subdevice(dev_file_info);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001422
David Schleefed9eccb2008-11-04 20:29:31 -08001423 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,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001452 page_to_pfn(virt_to_page(async->
1453 buf_page_list[i].
1454 virt_addr)),
1455 PAGE_SIZE, PAGE_SHARED)) {
David Schleefed9eccb2008-11-04 20:29:31 -08001456 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;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001468done:
David Schleefed9eccb2008-11-04 20:29:31 -08001469 mutex_unlock(&dev->mutex);
1470 return retval;
1471}
1472
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001473static unsigned int comedi_poll(struct file *file, poll_table *wait)
David Schleefed9eccb2008-11-04 20:29:31 -08001474{
1475 unsigned int mask = 0;
1476 const unsigned minor = iminor(file->f_dentry->d_inode);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001477 struct comedi_device_file_info *dev_file_info =
1478 comedi_get_device_file_info(minor);
David Schleefed9eccb2008-11-04 20:29:31 -08001479 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
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001495 || comedi_buf_read_n_available(read_subdev->async) > 0
1496 || !(comedi_get_subdevice_runflags(read_subdev) &
1497 SRF_RUNNING)) {
David Schleefed9eccb2008-11-04 20:29:31 -08001498 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);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001504 comedi_buf_write_alloc(write_subdev->async,
1505 write_subdev->async->prealloc_bufsz);
David Schleefed9eccb2008-11-04 20:29:31 -08001506 if (!write_subdev->busy
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001507 || !(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)) {
David Schleefed9eccb2008-11-04 20:29:31 -08001511 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,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001520 loff_t *offset)
David Schleefed9eccb2008-11-04 20:29:31 -08001521{
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);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001527 struct comedi_device_file_info *dev_file_info =
1528 comedi_get_device_file_info(minor);
David Schleefed9eccb2008-11-04 20:29:31 -08001529 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;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001563 if (async->buf_write_ptr + m > async->prealloc_bufsz)
David Schleefed9eccb2008-11-04 20:29:31 -08001564 m = async->prealloc_bufsz - async->buf_write_ptr;
David Schleefed9eccb2008-11-04 20:29:31 -08001565 comedi_buf_write_alloc(async, async->prealloc_bufsz);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001566 if (m > comedi_buf_write_n_allocated(async))
David Schleefed9eccb2008-11-04 20:29:31 -08001567 m = comedi_buf_write_n_allocated(async);
David Schleefed9eccb2008-11-04 20:29:31 -08001568 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) &
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001574 SRF_ERROR) {
David Schleefed9eccb2008-11-04 20:29:31 -08001575 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();
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001591 if (!s->busy)
David Schleefed9eccb2008-11-04 20:29:31 -08001592 break;
David Schleefed9eccb2008-11-04 20:29:31 -08001593 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,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001601 buf, n);
David Schleefed9eccb2008-11-04 20:29:31 -08001602 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:
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001618 return count ? count : retval;
David Schleefed9eccb2008-11-04 20:29:31 -08001619}
1620
1621static ssize_t comedi_read(struct file *file, char *buf, size_t nbytes,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001622 loff_t *offset)
David Schleefed9eccb2008-11-04 20:29:31 -08001623{
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);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001629 struct comedi_device_file_info *dev_file_info =
1630 comedi_get_device_file_info(minor);
David Schleefed9eccb2008-11-04 20:29:31 -08001631 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);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001665 /* printk("%d available\n",m); */
1666 if (async->buf_read_ptr + m > async->prealloc_bufsz)
David Schleefed9eccb2008-11-04 20:29:31 -08001667 m = async->prealloc_bufsz - async->buf_read_ptr;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001668 /* printk("%d contiguous\n",m); */
David Schleefed9eccb2008-11-04 20:29:31 -08001669 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) &
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001676 SRF_ERROR) {
David Schleefed9eccb2008-11-04 20:29:31 -08001677 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 +
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001703 async->buf_read_ptr, n);
David Schleefed9eccb2008-11-04 20:29:31 -08001704 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)) &&
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001719 async->buf_read_count - async->buf_write_count == 0) {
David Schleefed9eccb2008-11-04 20:29:31 -08001720 do_become_nonbusy(dev, s);
1721 }
1722 set_current_state(TASK_RUNNING);
1723 remove_wait_queue(&async->wait_head, &wait);
1724
1725done:
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001726 return count ? count : retval;
David Schleefed9eccb2008-11-04 20:29:31 -08001727}
1728
1729/*
1730 This function restores a subdevice to an idle state.
1731 */
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001732void do_become_nonbusy(comedi_device *dev, comedi_subdevice *s)
David Schleefed9eccb2008-11-04 20:29:31 -08001733{
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 {
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001747 printk(KERN_ERR
1748 "BUG: (?) do_become_nonbusy called with async=0\n");
David Schleefed9eccb2008-11-04 20:29:31 -08001749 }
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);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001758 struct comedi_device_file_info *dev_file_info =
1759 comedi_get_device_file_info(minor);
David Schleefed9eccb2008-11-04 20:29:31 -08001760 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
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001817 if (dev->attached && dev->use_count == 0 && dev->open)
David Schleefed9eccb2008-11-04 20:29:31 -08001818 dev->open(dev);
David Schleefed9eccb2008-11-04 20:29:31 -08001819
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);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001830 struct comedi_device_file_info *dev_file_info =
1831 comedi_get_device_file_info(minor);
David Schleefed9eccb2008-11-04 20:29:31 -08001832 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
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001842 if (s->busy == file)
David Schleefed9eccb2008-11-04 20:29:31 -08001843 do_cancel(dev, s);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001844 if (s->lock == file)
David Schleefed9eccb2008-11-04 20:29:31 -08001845 s->lock = NULL;
David Schleefed9eccb2008-11-04 20:29:31 -08001846 }
1847 }
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001848 if (dev->attached && dev->use_count == 1 && dev->close)
David Schleefed9eccb2008-11-04 20:29:31 -08001849 dev->close(dev);
David Schleefed9eccb2008-11-04 20:29:31 -08001850
1851 module_put(THIS_MODULE);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001852 if (dev->attached)
David Schleefed9eccb2008-11-04 20:29:31 -08001853 module_put(dev->driver->module);
David Schleefed9eccb2008-11-04 20:29:31 -08001854
1855 dev->use_count--;
1856
1857 mutex_unlock(&dev->mutex);
1858
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001859 if (file->f_flags & FASYNC)
David Schleefed9eccb2008-11-04 20:29:31 -08001860 comedi_fasync(-1, file, 0);
David Schleefed9eccb2008-11-04 20:29:31 -08001861
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);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001868 struct comedi_device_file_info *dev_file_info =
1869 comedi_get_device_file_info(minor);
1870
David Schleefed9eccb2008-11-04 20:29:31 -08001871 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 = {
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001877 .owner = THIS_MODULE,
David Schleefed9eccb2008-11-04 20:29:31 -08001878#ifdef HAVE_UNLOCKED_IOCTL
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001879 .unlocked_ioctl = comedi_unlocked_ioctl,
David Schleefed9eccb2008-11-04 20:29:31 -08001880#else
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001881 .ioctl = comedi_ioctl,
David Schleefed9eccb2008-11-04 20:29:31 -08001882#endif
1883#ifdef HAVE_COMPAT_IOCTL
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001884 .compat_ioctl = comedi_compat_ioctl,
David Schleefed9eccb2008-11-04 20:29:31 -08001885#endif
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001886 .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,
David Schleefed9eccb2008-11-04 20:29:31 -08001893};
1894
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001895struct class *comedi_class;
David Schleefed9eccb2008-11-04 20:29:31 -08001896static struct cdev comedi_cdev;
1897
1898static void comedi_cleanup_legacy_minors(void)
1899{
1900 unsigned i;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001901
Bernd Porr1dd33ab2008-12-08 23:30:13 +00001902 for (i = 0; i < comedi_num_legacy_minors; i++)
David Schleefed9eccb2008-11-04 20:29:31 -08001903 comedi_free_board_minor(i);
David Schleefed9eccb2008-11-04 20:29:31 -08001904}
1905
1906static int __init comedi_init(void)
1907{
1908 int i;
1909 int retval;
1910
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001911 printk(KERN_INFO "comedi: version " COMEDI_RELEASE
1912 " - http://www.comedi.org\n");
David Schleefed9eccb2008-11-04 20:29:31 -08001913
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001914 memset(comedi_file_info_table, 0,
1915 sizeof(struct comedi_device_file_info *) * COMEDI_NUM_MINORS);
David Schleefed9eccb2008-11-04 20:29:31 -08001916
1917 retval = register_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001918 COMEDI_NUM_MINORS, "comedi");
David Schleefed9eccb2008-11-04 20:29:31 -08001919 if (retval)
1920 return -EIO;
1921 cdev_init(&comedi_cdev, &comedi_fops);
1922 comedi_cdev.owner = THIS_MODULE;
1923 kobject_set_name(&comedi_cdev.kobj, "comedi");
1924 if (cdev_add(&comedi_cdev, MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS)) {
1925 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001926 COMEDI_NUM_MINORS);
David Schleefed9eccb2008-11-04 20:29:31 -08001927 return -EIO;
1928 }
1929 comedi_class = class_create(THIS_MODULE, "comedi");
1930 if (IS_ERR(comedi_class)) {
1931 printk("comedi: failed to create class");
1932 cdev_del(&comedi_cdev);
1933 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001934 COMEDI_NUM_MINORS);
David Schleefed9eccb2008-11-04 20:29:31 -08001935 return PTR_ERR(comedi_class);
1936 }
1937
1938 /* XXX requires /proc interface */
1939 comedi_proc_init();
1940
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001941 /* create devices files for legacy/manual use */
Bernd Porr1dd33ab2008-12-08 23:30:13 +00001942 for (i = 0; i < comedi_num_legacy_minors; i++) {
David Schleefed9eccb2008-11-04 20:29:31 -08001943 int minor;
1944 minor = comedi_alloc_board_minor(NULL);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001945 if (minor < 0) {
David Schleefed9eccb2008-11-04 20:29:31 -08001946 comedi_cleanup_legacy_minors();
1947 cdev_del(&comedi_cdev);
1948 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001949 COMEDI_NUM_MINORS);
David Schleefed9eccb2008-11-04 20:29:31 -08001950 return minor;
1951 }
1952 }
1953
1954 comedi_rt_init();
1955
1956 comedi_register_ioctl32();
1957
1958 return 0;
1959}
1960
1961static void __exit comedi_cleanup(void)
1962{
1963 int i;
1964
1965 comedi_cleanup_legacy_minors();
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001966 for (i = 0; i < COMEDI_NUM_MINORS; ++i)
David Schleefed9eccb2008-11-04 20:29:31 -08001967 BUG_ON(comedi_file_info_table[i]);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001968
David Schleefed9eccb2008-11-04 20:29:31 -08001969
1970 class_destroy(comedi_class);
1971 cdev_del(&comedi_cdev);
1972 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS);
1973
1974 comedi_proc_cleanup();
1975
1976 comedi_rt_cleanup();
1977
1978 comedi_unregister_ioctl32();
1979}
1980
1981module_init(comedi_init);
1982module_exit(comedi_cleanup);
1983
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001984void comedi_error(const comedi_device *dev, const char *s)
David Schleefed9eccb2008-11-04 20:29:31 -08001985{
1986 rt_printk("comedi%d: %s: %s\n", dev->minor, dev->driver->driver_name,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001987 s);
David Schleefed9eccb2008-11-04 20:29:31 -08001988}
1989
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001990void comedi_event(comedi_device *dev, comedi_subdevice *s)
David Schleefed9eccb2008-11-04 20:29:31 -08001991{
1992 comedi_async *async = s->async;
1993 unsigned runflags = 0;
1994 unsigned runflags_mask = 0;
1995
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001996 /* DPRINTK("comedi_event 0x%x\n",mask); */
David Schleefed9eccb2008-11-04 20:29:31 -08001997
1998 if ((comedi_get_subdevice_runflags(s) & SRF_RUNNING) == 0)
1999 return;
2000
2001 if (s->async->
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002002 events & (COMEDI_CB_EOA | COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)) {
David Schleefed9eccb2008-11-04 20:29:31 -08002003 runflags_mask |= SRF_RUNNING;
2004 }
2005 /* remember if an error event has occured, so an error
2006 * can be returned the next time the user does a read() */
2007 if (s->async->events & (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)) {
2008 runflags_mask |= SRF_ERROR;
2009 runflags |= SRF_ERROR;
2010 }
2011 if (runflags_mask) {
2012 /*sets SRF_ERROR and SRF_RUNNING together atomically */
2013 comedi_set_subdevice_runflags(s, runflags_mask, runflags);
2014 }
2015
2016 if (async->cb_mask & s->async->events) {
2017 if (comedi_get_subdevice_runflags(s) & SRF_USER) {
2018
2019 if (dev->rt) {
2020#ifdef CONFIG_COMEDI_RT
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002021 /* pend wake up */
David Schleefed9eccb2008-11-04 20:29:31 -08002022 comedi_rt_pend_wakeup(&async->wait_head);
2023#else
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002024 printk
2025 ("BUG: comedi_event() code unreachable\n");
David Schleefed9eccb2008-11-04 20:29:31 -08002026#endif
2027 } else {
2028 wake_up_interruptible(&async->wait_head);
2029 if (s->subdev_flags & SDF_CMD_READ) {
2030 kill_fasync(&dev->async_queue, SIGIO,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002031 POLL_IN);
David Schleefed9eccb2008-11-04 20:29:31 -08002032 }
2033 if (s->subdev_flags & SDF_CMD_WRITE) {
2034 kill_fasync(&dev->async_queue, SIGIO,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002035 POLL_OUT);
David Schleefed9eccb2008-11-04 20:29:31 -08002036 }
2037 }
2038 } else {
2039 if (async->cb_func)
2040 async->cb_func(s->async->events, async->cb_arg);
2041 /* XXX bug here. If subdevice A is rt, and
2042 * subdevice B tries to callback to a normal
2043 * linux kernel function, it will be at the
2044 * wrong priority. Since this isn't very
2045 * common, I'm not going to worry about it. */
2046 }
2047 }
2048 s->async->events = 0;
2049}
2050
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002051void comedi_set_subdevice_runflags(comedi_subdevice *s, unsigned mask,
2052 unsigned bits)
David Schleefed9eccb2008-11-04 20:29:31 -08002053{
2054 unsigned long flags;
2055
2056 comedi_spin_lock_irqsave(&s->spin_lock, flags);
2057 s->runflags &= ~mask;
2058 s->runflags |= (bits & mask);
2059 comedi_spin_unlock_irqrestore(&s->spin_lock, flags);
2060}
2061
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002062unsigned comedi_get_subdevice_runflags(comedi_subdevice *s)
David Schleefed9eccb2008-11-04 20:29:31 -08002063{
2064 unsigned long flags;
2065 unsigned runflags;
2066
2067 comedi_spin_lock_irqsave(&s->spin_lock, flags);
2068 runflags = s->runflags;
2069 comedi_spin_unlock_irqrestore(&s->spin_lock, flags);
2070 return runflags;
2071}
2072
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002073static int is_device_busy(comedi_device *dev)
David Schleefed9eccb2008-11-04 20:29:31 -08002074{
2075 comedi_subdevice *s;
2076 int i;
2077
2078 if (!dev->attached)
2079 return 0;
2080
2081 for (i = 0; i < dev->n_subdevices; i++) {
2082 s = dev->subdevices + i;
2083 if (s->busy)
2084 return 1;
2085 if (s->async && s->async->mmap_count)
2086 return 1;
2087 }
2088
2089 return 0;
2090}
2091
2092void comedi_device_init(comedi_device *dev)
2093{
2094 memset(dev, 0, sizeof(comedi_device));
2095 spin_lock_init(&dev->spinlock);
2096 mutex_init(&dev->mutex);
2097 dev->minor = -1;
2098}
2099
2100void comedi_device_cleanup(comedi_device *dev)
2101{
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002102 if (dev == NULL)
2103 return;
David Schleefed9eccb2008-11-04 20:29:31 -08002104 mutex_lock(&dev->mutex);
2105 comedi_device_detach(dev);
2106 mutex_unlock(&dev->mutex);
2107 mutex_destroy(&dev->mutex);
2108}
2109
2110int comedi_alloc_board_minor(struct device *hardware_device)
2111{
2112 unsigned long flags;
2113 struct comedi_device_file_info *info;
2114 device_create_result_type *csdev;
2115 unsigned i;
2116
2117 info = kzalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002118 if (info == NULL)
2119 return -ENOMEM;
David Schleefed9eccb2008-11-04 20:29:31 -08002120 info->device = kzalloc(sizeof(comedi_device), GFP_KERNEL);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002121 if (info->device == NULL) {
David Schleefed9eccb2008-11-04 20:29:31 -08002122 kfree(info);
2123 return -ENOMEM;
2124 }
2125 comedi_device_init(info->device);
2126 comedi_spin_lock_irqsave(&comedi_file_info_table_lock, flags);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002127 for (i = 0; i < COMEDI_NUM_BOARD_MINORS; ++i) {
2128 if (comedi_file_info_table[i] == NULL) {
David Schleefed9eccb2008-11-04 20:29:31 -08002129 comedi_file_info_table[i] = info;
2130 break;
2131 }
2132 }
2133 comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002134 if (i == COMEDI_NUM_BOARD_MINORS) {
David Schleefed9eccb2008-11-04 20:29:31 -08002135 comedi_device_cleanup(info->device);
2136 kfree(info->device);
2137 kfree(info);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002138 rt_printk
2139 ("comedi: error: ran out of minor numbers for board device files.\n");
David Schleefed9eccb2008-11-04 20:29:31 -08002140 return -EBUSY;
2141 }
2142 info->device->minor = i;
2143 csdev = COMEDI_DEVICE_CREATE(comedi_class, NULL,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002144 MKDEV(COMEDI_MAJOR, i), NULL,
2145 hardware_device, "comedi%i", i);
2146 if (!IS_ERR(csdev))
David Schleefed9eccb2008-11-04 20:29:31 -08002147 info->device->class_dev = csdev;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002148
David Schleefed9eccb2008-11-04 20:29:31 -08002149 return i;
2150}
2151
2152void comedi_free_board_minor(unsigned minor)
2153{
2154 unsigned long flags;
2155 struct comedi_device_file_info *info;
2156
2157 BUG_ON(minor >= COMEDI_NUM_BOARD_MINORS);
2158 comedi_spin_lock_irqsave(&comedi_file_info_table_lock, flags);
2159 info = comedi_file_info_table[minor];
2160 comedi_file_info_table[minor] = NULL;
2161 comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
2162
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002163 if (info) {
David Schleefed9eccb2008-11-04 20:29:31 -08002164 comedi_device *dev = info->device;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002165 if (dev) {
2166 if (dev->class_dev) {
2167 device_destroy(comedi_class,
2168 MKDEV(COMEDI_MAJOR, dev->minor));
David Schleefed9eccb2008-11-04 20:29:31 -08002169 }
2170 comedi_device_cleanup(dev);
2171 kfree(dev);
2172 }
2173 kfree(info);
2174 }
2175}
2176
2177int comedi_alloc_subdevice_minor(comedi_device *dev, comedi_subdevice *s)
2178{
2179 unsigned long flags;
2180 struct comedi_device_file_info *info;
2181 device_create_result_type *csdev;
2182 unsigned i;
2183
2184 info = kmalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002185 if (info == NULL)
2186 return -ENOMEM;
David Schleefed9eccb2008-11-04 20:29:31 -08002187 info->device = dev;
2188 info->read_subdevice = s;
2189 info->write_subdevice = s;
2190 comedi_spin_lock_irqsave(&comedi_file_info_table_lock, flags);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002191 for (i = COMEDI_FIRST_SUBDEVICE_MINOR; i < COMEDI_NUM_BOARD_MINORS; ++i) {
2192 if (comedi_file_info_table[i] == NULL) {
David Schleefed9eccb2008-11-04 20:29:31 -08002193 comedi_file_info_table[i] = info;
2194 break;
2195 }
2196 }
2197 comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002198 if (i == COMEDI_NUM_MINORS) {
David Schleefed9eccb2008-11-04 20:29:31 -08002199 kfree(info);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002200 rt_printk
2201 ("comedi: error: ran out of minor numbers for board device files.\n");
David Schleefed9eccb2008-11-04 20:29:31 -08002202 return -EBUSY;
2203 }
2204 s->minor = i;
2205 csdev = COMEDI_DEVICE_CREATE(comedi_class, dev->class_dev,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002206 MKDEV(COMEDI_MAJOR, i), NULL, NULL,
2207 "comedi%i_subd%i", dev->minor,
2208 (int)(s - dev->subdevices));
2209 if (!IS_ERR(csdev))
David Schleefed9eccb2008-11-04 20:29:31 -08002210 s->class_dev = csdev;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002211
David Schleefed9eccb2008-11-04 20:29:31 -08002212 return i;
2213}
2214
2215void comedi_free_subdevice_minor(comedi_subdevice *s)
2216{
2217 unsigned long flags;
2218 struct comedi_device_file_info *info;
2219
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002220 if (s == NULL)
2221 return;
2222 if (s->minor < 0)
2223 return;
David Schleefed9eccb2008-11-04 20:29:31 -08002224
2225 BUG_ON(s->minor >= COMEDI_NUM_MINORS);
2226 BUG_ON(s->minor < COMEDI_FIRST_SUBDEVICE_MINOR);
2227
2228 comedi_spin_lock_irqsave(&comedi_file_info_table_lock, flags);
2229 info = comedi_file_info_table[s->minor];
2230 comedi_file_info_table[s->minor] = NULL;
2231 comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
2232
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002233 if (s->class_dev) {
David Schleefed9eccb2008-11-04 20:29:31 -08002234 device_destroy(comedi_class, MKDEV(COMEDI_MAJOR, s->minor));
2235 s->class_dev = NULL;
2236 }
2237 kfree(info);
2238}
2239
2240struct comedi_device_file_info *comedi_get_device_file_info(unsigned minor)
2241{
2242 unsigned long flags;
2243 struct comedi_device_file_info *info;
2244
2245 BUG_ON(minor >= COMEDI_NUM_MINORS);
2246 comedi_spin_lock_irqsave(&comedi_file_info_table_lock, flags);
2247 info = comedi_file_info_table[minor];
2248 comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
2249 return info;
2250}