blob: 3fd9772d7a7dacb344ba6ce5e179b92955d5e5ec [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>
Frank Mori Hess883db3d2009-04-14 11:21:41 -040047#include <linux/stat.h>
David Schleefed9eccb2008-11-04 20:29:31 -080048
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -080049#include <linux/io.h>
50#include <linux/uaccess.h>
David Schleefed9eccb2008-11-04 20:29:31 -080051
Greg Kroah-Hartman242e7ad2010-05-03 15:20:29 -070052#include "internal.h"
David Schleefed9eccb2008-11-04 20:29:31 -080053
54MODULE_AUTHOR("http://www.comedi.org");
55MODULE_DESCRIPTION("Comedi core module");
56MODULE_LICENSE("GPL");
57
58#ifdef CONFIG_COMEDI_DEBUG
59int comedi_debug;
Greg Kroah-Hartman18736432010-05-01 12:02:23 -070060EXPORT_SYMBOL(comedi_debug);
David Schleefed9eccb2008-11-04 20:29:31 -080061module_param(comedi_debug, int, 0644);
62#endif
63
Rusty Russell90ab5ee2012-01-13 09:32:20 +103064bool comedi_autoconfig = 1;
Ian Abbott6a9d7a22008-12-08 17:05:50 +000065module_param(comedi_autoconfig, bool, 0444);
66
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -070067static int comedi_num_legacy_minors;
Bernd Porr1dd33ab2008-12-08 23:30:13 +000068module_param(comedi_num_legacy_minors, int, 0444);
69
David Schleefed9eccb2008-11-04 20:29:31 -080070static DEFINE_SPINLOCK(comedi_file_info_table_lock);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -080071static struct comedi_device_file_info
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +053072*comedi_file_info_table[COMEDI_NUM_MINORS];
David Schleefed9eccb2008-11-04 20:29:31 -080073
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +053074static int do_devconfig_ioctl(struct comedi_device *dev,
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -070075 struct comedi_devconfig __user *arg);
76static int do_bufconfig_ioctl(struct comedi_device *dev,
77 struct comedi_bufconfig __user *arg);
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +053078static int do_devinfo_ioctl(struct comedi_device *dev,
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -070079 struct comedi_devinfo __user *arg,
80 struct file *file);
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +053081static int do_subdinfo_ioctl(struct comedi_device *dev,
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -070082 struct comedi_subdinfo __user *arg, void *file);
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +053083static int do_chaninfo_ioctl(struct comedi_device *dev,
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -070084 struct comedi_chaninfo __user *arg);
85static int do_bufinfo_ioctl(struct comedi_device *dev,
Ian Abbott53fa8272010-05-19 18:09:50 +010086 struct comedi_bufinfo __user *arg, void *file);
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -070087static int do_cmd_ioctl(struct comedi_device *dev,
88 struct comedi_cmd __user *arg, void *file);
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +053089static int do_lock_ioctl(struct comedi_device *dev, unsigned int arg,
90 void *file);
91static int do_unlock_ioctl(struct comedi_device *dev, unsigned int arg,
92 void *file);
93static int do_cancel_ioctl(struct comedi_device *dev, unsigned int arg,
94 void *file);
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -070095static int do_cmdtest_ioctl(struct comedi_device *dev,
96 struct comedi_cmd __user *arg, void *file);
97static int do_insnlist_ioctl(struct comedi_device *dev,
98 struct comedi_insnlist __user *arg, void *file);
99static int do_insn_ioctl(struct comedi_device *dev,
100 struct comedi_insn __user *arg, void *file);
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530101static int do_poll_ioctl(struct comedi_device *dev, unsigned int subd,
102 void *file);
David Schleefed9eccb2008-11-04 20:29:31 -0800103
Mark Pearsonf5283a42011-07-10 21:16:38 +0200104static void do_become_nonbusy(struct comedi_device *dev,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530105 struct comedi_subdevice *s);
Bill Pemberton34c43922009-03-16 22:05:14 -0400106static int do_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
David Schleefed9eccb2008-11-04 20:29:31 -0800107
108static int comedi_fasync(int fd, struct file *file, int on);
109
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400110static int is_device_busy(struct comedi_device *dev);
Frank Mori Hess883db3d2009-04-14 11:21:41 -0400111static int resize_async_buffer(struct comedi_device *dev,
112 struct comedi_subdevice *s,
113 struct comedi_async *async, unsigned new_size);
114
115/* declarations for sysfs attribute files */
116static struct device_attribute dev_attr_max_read_buffer_kb;
117static struct device_attribute dev_attr_read_buffer_kb;
118static struct device_attribute dev_attr_max_write_buffer_kb;
119static struct device_attribute dev_attr_write_buffer_kb;
David Schleefed9eccb2008-11-04 20:29:31 -0800120
David Schleefed9eccb2008-11-04 20:29:31 -0800121static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800122 unsigned long arg)
David Schleefed9eccb2008-11-04 20:29:31 -0800123{
124 const unsigned minor = iminor(file->f_dentry->d_inode);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800125 struct comedi_device_file_info *dev_file_info =
126 comedi_get_device_file_info(minor);
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400127 struct comedi_device *dev;
David Schleefed9eccb2008-11-04 20:29:31 -0800128 int rc;
129
Frank Mori Hess53b670a2008-12-15 13:48:47 +0000130 if (dev_file_info == NULL || dev_file_info->device == NULL)
131 return -ENODEV;
132 dev = dev_file_info->device;
133
David Schleefed9eccb2008-11-04 20:29:31 -0800134 mutex_lock(&dev->mutex);
135
136 /* Device config is special, because it must work on
137 * an unconfigured device. */
138 if (cmd == COMEDI_DEVCONFIG) {
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -0700139 rc = do_devconfig_ioctl(dev,
140 (struct comedi_devconfig __user *)arg);
Ian Abbottf5b13042012-12-04 15:59:55 +0000141 if (rc == 0)
142 /* Evade comedi_auto_unconfig(). */
143 dev_file_info->hardware_device = NULL;
David Schleefed9eccb2008-11-04 20:29:31 -0800144 goto done;
145 }
146
147 if (!dev->attached) {
148 DPRINTK("no driver configured on /dev/comedi%i\n", dev->minor);
149 rc = -ENODEV;
150 goto done;
151 }
152
153 switch (cmd) {
154 case COMEDI_BUFCONFIG:
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -0700155 rc = do_bufconfig_ioctl(dev,
156 (struct comedi_bufconfig __user *)arg);
David Schleefed9eccb2008-11-04 20:29:31 -0800157 break;
158 case COMEDI_DEVINFO:
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -0700159 rc = do_devinfo_ioctl(dev, (struct comedi_devinfo __user *)arg,
160 file);
David Schleefed9eccb2008-11-04 20:29:31 -0800161 break;
162 case COMEDI_SUBDINFO:
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -0700163 rc = do_subdinfo_ioctl(dev,
164 (struct comedi_subdinfo __user *)arg,
165 file);
David Schleefed9eccb2008-11-04 20:29:31 -0800166 break;
167 case COMEDI_CHANINFO:
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -0700168 rc = do_chaninfo_ioctl(dev, (void __user *)arg);
David Schleefed9eccb2008-11-04 20:29:31 -0800169 break;
170 case COMEDI_RANGEINFO:
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -0700171 rc = do_rangeinfo_ioctl(dev, (void __user *)arg);
David Schleefed9eccb2008-11-04 20:29:31 -0800172 break;
173 case COMEDI_BUFINFO:
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -0700174 rc = do_bufinfo_ioctl(dev,
Ian Abbott53fa8272010-05-19 18:09:50 +0100175 (struct comedi_bufinfo __user *)arg,
176 file);
David Schleefed9eccb2008-11-04 20:29:31 -0800177 break;
178 case COMEDI_LOCK:
179 rc = do_lock_ioctl(dev, arg, file);
180 break;
181 case COMEDI_UNLOCK:
182 rc = do_unlock_ioctl(dev, arg, file);
183 break;
184 case COMEDI_CANCEL:
185 rc = do_cancel_ioctl(dev, arg, file);
186 break;
187 case COMEDI_CMD:
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -0700188 rc = do_cmd_ioctl(dev, (struct comedi_cmd __user *)arg, file);
David Schleefed9eccb2008-11-04 20:29:31 -0800189 break;
190 case COMEDI_CMDTEST:
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -0700191 rc = do_cmdtest_ioctl(dev, (struct comedi_cmd __user *)arg,
192 file);
David Schleefed9eccb2008-11-04 20:29:31 -0800193 break;
194 case COMEDI_INSNLIST:
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -0700195 rc = do_insnlist_ioctl(dev,
196 (struct comedi_insnlist __user *)arg,
197 file);
David Schleefed9eccb2008-11-04 20:29:31 -0800198 break;
199 case COMEDI_INSN:
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -0700200 rc = do_insn_ioctl(dev, (struct comedi_insn __user *)arg,
201 file);
David Schleefed9eccb2008-11-04 20:29:31 -0800202 break;
203 case COMEDI_POLL:
204 rc = do_poll_ioctl(dev, arg, file);
205 break;
206 default:
207 rc = -ENOTTY;
208 break;
209 }
210
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800211done:
David Schleefed9eccb2008-11-04 20:29:31 -0800212 mutex_unlock(&dev->mutex);
213 return rc;
214}
215
216/*
217 COMEDI_DEVCONFIG
218 device config ioctl
219
220 arg:
221 pointer to devconfig structure
222
223 reads:
224 devconfig structure at arg
225
226 writes:
227 none
228*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530229static int do_devconfig_ioctl(struct comedi_device *dev,
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -0700230 struct comedi_devconfig __user *arg)
David Schleefed9eccb2008-11-04 20:29:31 -0800231{
Bill Pemberton0707bb02009-03-16 22:06:20 -0400232 struct comedi_devconfig it;
David Schleefed9eccb2008-11-04 20:29:31 -0800233 int ret;
234 unsigned char *aux_data = NULL;
235 int aux_len;
236
237 if (!capable(CAP_SYS_ADMIN))
238 return -EPERM;
239
240 if (arg == NULL) {
241 if (is_device_busy(dev))
242 return -EBUSY;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800243 if (dev->attached) {
David Schleefed9eccb2008-11-04 20:29:31 -0800244 struct module *driver_module = dev->driver->module;
245 comedi_device_detach(dev);
246 module_put(driver_module);
247 }
248 return 0;
249 }
250
Bill Pemberton0707bb02009-03-16 22:06:20 -0400251 if (copy_from_user(&it, arg, sizeof(struct comedi_devconfig)))
David Schleefed9eccb2008-11-04 20:29:31 -0800252 return -EFAULT;
253
254 it.board_name[COMEDI_NAMELEN - 1] = 0;
255
256 if (comedi_aux_data(it.options, 0) &&
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800257 it.options[COMEDI_DEVCONF_AUX_DATA_LENGTH]) {
David Schleefed9eccb2008-11-04 20:29:31 -0800258 int bit_shift;
259 aux_len = it.options[COMEDI_DEVCONF_AUX_DATA_LENGTH];
260 if (aux_len < 0)
261 return -EFAULT;
262
263 aux_data = vmalloc(aux_len);
264 if (!aux_data)
265 return -ENOMEM;
266
267 if (copy_from_user(aux_data,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800268 comedi_aux_data(it.options, 0), aux_len)) {
David Schleefed9eccb2008-11-04 20:29:31 -0800269 vfree(aux_data);
270 return -EFAULT;
271 }
272 it.options[COMEDI_DEVCONF_AUX_DATA_LO] =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800273 (unsigned long)aux_data;
David Schleefed9eccb2008-11-04 20:29:31 -0800274 if (sizeof(void *) > sizeof(int)) {
275 bit_shift = sizeof(int) * 8;
276 it.options[COMEDI_DEVCONF_AUX_DATA_HI] =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800277 ((unsigned long)aux_data) >> bit_shift;
David Schleefed9eccb2008-11-04 20:29:31 -0800278 } else
279 it.options[COMEDI_DEVCONF_AUX_DATA_HI] = 0;
280 }
281
282 ret = comedi_device_attach(dev, &it);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800283 if (ret == 0) {
284 if (!try_module_get(dev->driver->module)) {
David Schleefed9eccb2008-11-04 20:29:31 -0800285 comedi_device_detach(dev);
Julia Lawall84d0e612012-04-22 13:37:09 +0200286 ret = -ENOSYS;
David Schleefed9eccb2008-11-04 20:29:31 -0800287 }
288 }
289
290 if (aux_data)
291 vfree(aux_data);
292
293 return ret;
294}
295
296/*
297 COMEDI_BUFCONFIG
298 buffer configuration ioctl
299
300 arg:
301 pointer to bufconfig structure
302
303 reads:
304 bufconfig at arg
305
306 writes:
307 modified bufconfig at arg
308
309*/
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -0700310static int do_bufconfig_ioctl(struct comedi_device *dev,
311 struct comedi_bufconfig __user *arg)
David Schleefed9eccb2008-11-04 20:29:31 -0800312{
Bill Pembertonbe6aba42009-03-16 22:06:37 -0400313 struct comedi_bufconfig bc;
Bill Pembertond1636792009-03-16 22:05:20 -0400314 struct comedi_async *async;
Bill Pemberton34c43922009-03-16 22:05:14 -0400315 struct comedi_subdevice *s;
Frank Mori Hess883db3d2009-04-14 11:21:41 -0400316 int retval = 0;
David Schleefed9eccb2008-11-04 20:29:31 -0800317
Bill Pembertonbe6aba42009-03-16 22:06:37 -0400318 if (copy_from_user(&bc, arg, sizeof(struct comedi_bufconfig)))
David Schleefed9eccb2008-11-04 20:29:31 -0800319 return -EFAULT;
320
321 if (bc.subdevice >= dev->n_subdevices || bc.subdevice < 0)
322 return -EINVAL;
323
324 s = dev->subdevices + bc.subdevice;
325 async = s->async;
326
327 if (!async) {
328 DPRINTK("subdevice does not have async capability\n");
329 bc.size = 0;
330 bc.maximum_size = 0;
331 goto copyback;
332 }
333
334 if (bc.maximum_size) {
335 if (!capable(CAP_SYS_ADMIN))
336 return -EPERM;
337
338 async->max_bufsize = bc.maximum_size;
339 }
340
341 if (bc.size) {
Frank Mori Hess883db3d2009-04-14 11:21:41 -0400342 retval = resize_async_buffer(dev, s, async, bc.size);
343 if (retval < 0)
344 return retval;
David Schleefed9eccb2008-11-04 20:29:31 -0800345 }
346
347 bc.size = async->prealloc_bufsz;
348 bc.maximum_size = async->max_bufsize;
349
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800350copyback:
Bill Pembertonbe6aba42009-03-16 22:06:37 -0400351 if (copy_to_user(arg, &bc, sizeof(struct comedi_bufconfig)))
David Schleefed9eccb2008-11-04 20:29:31 -0800352 return -EFAULT;
353
354 return 0;
355}
356
357/*
358 COMEDI_DEVINFO
359 device info ioctl
360
361 arg:
362 pointer to devinfo structure
363
364 reads:
365 none
366
367 writes:
368 devinfo structure
369
370*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530371static int do_devinfo_ioctl(struct comedi_device *dev,
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -0700372 struct comedi_devinfo __user *arg,
373 struct file *file)
David Schleefed9eccb2008-11-04 20:29:31 -0800374{
Bill Pemberton063db042009-03-16 22:06:15 -0400375 struct comedi_devinfo devinfo;
David Schleefed9eccb2008-11-04 20:29:31 -0800376 const unsigned minor = iminor(file->f_dentry->d_inode);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800377 struct comedi_device_file_info *dev_file_info =
378 comedi_get_device_file_info(minor);
Bill Pemberton34c43922009-03-16 22:05:14 -0400379 struct comedi_subdevice *read_subdev =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800380 comedi_get_read_subdevice(dev_file_info);
Bill Pemberton34c43922009-03-16 22:05:14 -0400381 struct comedi_subdevice *write_subdev =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800382 comedi_get_write_subdevice(dev_file_info);
David Schleefed9eccb2008-11-04 20:29:31 -0800383
384 memset(&devinfo, 0, sizeof(devinfo));
385
386 /* fill devinfo structure */
387 devinfo.version_code = COMEDI_VERSION_CODE;
388 devinfo.n_subdevs = dev->n_subdevices;
Vasiliy Kulikov819cbb12011-06-26 12:56:22 +0400389 strlcpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN);
390 strlcpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN);
David Schleefed9eccb2008-11-04 20:29:31 -0800391
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800392 if (read_subdev)
David Schleefed9eccb2008-11-04 20:29:31 -0800393 devinfo.read_subdevice = read_subdev - dev->subdevices;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800394 else
David Schleefed9eccb2008-11-04 20:29:31 -0800395 devinfo.read_subdevice = -1;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800396
397 if (write_subdev)
David Schleefed9eccb2008-11-04 20:29:31 -0800398 devinfo.write_subdevice = write_subdev - dev->subdevices;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800399 else
David Schleefed9eccb2008-11-04 20:29:31 -0800400 devinfo.write_subdevice = -1;
David Schleefed9eccb2008-11-04 20:29:31 -0800401
Bill Pemberton063db042009-03-16 22:06:15 -0400402 if (copy_to_user(arg, &devinfo, sizeof(struct comedi_devinfo)))
David Schleefed9eccb2008-11-04 20:29:31 -0800403 return -EFAULT;
404
405 return 0;
406}
407
408/*
409 COMEDI_SUBDINFO
410 subdevice info ioctl
411
412 arg:
413 pointer to array of subdevice info structures
414
415 reads:
416 none
417
418 writes:
419 array of subdevice info structures at arg
420
421*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530422static int do_subdinfo_ioctl(struct comedi_device *dev,
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -0700423 struct comedi_subdinfo __user *arg, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -0800424{
425 int ret, i;
Bill Pembertonbd52efb2009-03-16 22:06:09 -0400426 struct comedi_subdinfo *tmp, *us;
Bill Pemberton34c43922009-03-16 22:05:14 -0400427 struct comedi_subdevice *s;
David Schleefed9eccb2008-11-04 20:29:31 -0800428
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530429 tmp =
430 kcalloc(dev->n_subdevices, sizeof(struct comedi_subdinfo),
431 GFP_KERNEL);
David Schleefed9eccb2008-11-04 20:29:31 -0800432 if (!tmp)
433 return -ENOMEM;
434
435 /* fill subdinfo structs */
436 for (i = 0; i < dev->n_subdevices; i++) {
437 s = dev->subdevices + i;
438 us = tmp + i;
439
440 us->type = s->type;
441 us->n_chan = s->n_chan;
442 us->subd_flags = s->subdev_flags;
443 if (comedi_get_subdevice_runflags(s) & SRF_RUNNING)
444 us->subd_flags |= SDF_RUNNING;
445#define TIMER_nanosec 5 /* backwards compatibility */
446 us->timer_type = TIMER_nanosec;
447 us->len_chanlist = s->len_chanlist;
448 us->maxdata = s->maxdata;
449 if (s->range_table) {
450 us->range_type =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800451 (i << 24) | (0 << 16) | (s->range_table->length);
David Schleefed9eccb2008-11-04 20:29:31 -0800452 } else {
453 us->range_type = 0; /* XXX */
454 }
455 us->flags = s->flags;
456
457 if (s->busy)
458 us->subd_flags |= SDF_BUSY;
459 if (s->busy == file)
460 us->subd_flags |= SDF_BUSY_OWNER;
461 if (s->lock)
462 us->subd_flags |= SDF_LOCKED;
463 if (s->lock == file)
464 us->subd_flags |= SDF_LOCK_OWNER;
465 if (!s->maxdata && s->maxdata_list)
466 us->subd_flags |= SDF_MAXDATA;
467 if (s->flaglist)
468 us->subd_flags |= SDF_FLAGS;
469 if (s->range_table_list)
470 us->subd_flags |= SDF_RANGETYPE;
471 if (s->do_cmd)
472 us->subd_flags |= SDF_CMD;
473
474 if (s->insn_bits != &insn_inval)
475 us->insn_bits_support = COMEDI_SUPPORTED;
476 else
477 us->insn_bits_support = COMEDI_UNSUPPORTED;
478
479 us->settling_time_0 = s->settling_time_0;
480 }
481
482 ret = copy_to_user(arg, tmp,
Bill Pembertonbd52efb2009-03-16 22:06:09 -0400483 dev->n_subdevices * sizeof(struct comedi_subdinfo));
David Schleefed9eccb2008-11-04 20:29:31 -0800484
485 kfree(tmp);
486
487 return ret ? -EFAULT : 0;
488}
489
490/*
491 COMEDI_CHANINFO
492 subdevice info ioctl
493
494 arg:
495 pointer to chaninfo structure
496
497 reads:
498 chaninfo structure at arg
499
500 writes:
501 arrays at elements of chaninfo structure
502
503*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530504static int do_chaninfo_ioctl(struct comedi_device *dev,
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -0700505 struct comedi_chaninfo __user *arg)
David Schleefed9eccb2008-11-04 20:29:31 -0800506{
Bill Pemberton34c43922009-03-16 22:05:14 -0400507 struct comedi_subdevice *s;
Bill Pembertona18b4162009-03-16 22:06:04 -0400508 struct comedi_chaninfo it;
David Schleefed9eccb2008-11-04 20:29:31 -0800509
Bill Pembertona18b4162009-03-16 22:06:04 -0400510 if (copy_from_user(&it, arg, sizeof(struct comedi_chaninfo)))
David Schleefed9eccb2008-11-04 20:29:31 -0800511 return -EFAULT;
512
513 if (it.subdev >= dev->n_subdevices)
514 return -EINVAL;
515 s = dev->subdevices + it.subdev;
516
517 if (it.maxdata_list) {
518 if (s->maxdata || !s->maxdata_list)
519 return -EINVAL;
520 if (copy_to_user(it.maxdata_list, s->maxdata_list,
Bill Pemberton790c5542009-03-16 22:05:02 -0400521 s->n_chan * sizeof(unsigned int)))
David Schleefed9eccb2008-11-04 20:29:31 -0800522 return -EFAULT;
523 }
524
525 if (it.flaglist) {
526 if (!s->flaglist)
527 return -EINVAL;
528 if (copy_to_user(it.flaglist, s->flaglist,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800529 s->n_chan * sizeof(unsigned int)))
David Schleefed9eccb2008-11-04 20:29:31 -0800530 return -EFAULT;
531 }
532
533 if (it.rangelist) {
534 int i;
535
536 if (!s->range_table_list)
537 return -EINVAL;
538 for (i = 0; i < s->n_chan; i++) {
539 int x;
540
541 x = (dev->minor << 28) | (it.subdev << 24) | (i << 16) |
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800542 (s->range_table_list[i]->length);
Vasiliy Kulikov81604d42010-09-05 22:32:33 +0400543 if (put_user(x, it.rangelist + i))
544 return -EFAULT;
David Schleefed9eccb2008-11-04 20:29:31 -0800545 }
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800546#if 0
547 if (copy_to_user(it.rangelist, s->range_type_list,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530548 s->n_chan * sizeof(unsigned int)))
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800549 return -EFAULT;
550#endif
David Schleefed9eccb2008-11-04 20:29:31 -0800551 }
552
553 return 0;
554}
555
556 /*
557 COMEDI_BUFINFO
558 buffer information ioctl
559
560 arg:
561 pointer to bufinfo structure
562
563 reads:
564 bufinfo at arg
565
566 writes:
567 modified bufinfo at arg
568
569 */
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -0700570static int do_bufinfo_ioctl(struct comedi_device *dev,
Ian Abbott53fa8272010-05-19 18:09:50 +0100571 struct comedi_bufinfo __user *arg, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -0800572{
Bill Pemberton9aa53392009-03-16 22:06:42 -0400573 struct comedi_bufinfo bi;
Bill Pemberton34c43922009-03-16 22:05:14 -0400574 struct comedi_subdevice *s;
Bill Pembertond1636792009-03-16 22:05:20 -0400575 struct comedi_async *async;
David Schleefed9eccb2008-11-04 20:29:31 -0800576
Bill Pemberton9aa53392009-03-16 22:06:42 -0400577 if (copy_from_user(&bi, arg, sizeof(struct comedi_bufinfo)))
David Schleefed9eccb2008-11-04 20:29:31 -0800578 return -EFAULT;
579
580 if (bi.subdevice >= dev->n_subdevices || bi.subdevice < 0)
581 return -EINVAL;
582
583 s = dev->subdevices + bi.subdevice;
Ian Abbott53fa8272010-05-19 18:09:50 +0100584
585 if (s->lock && s->lock != file)
586 return -EACCES;
587
David Schleefed9eccb2008-11-04 20:29:31 -0800588 async = s->async;
589
590 if (!async) {
591 DPRINTK("subdevice does not have async capability\n");
592 bi.buf_write_ptr = 0;
593 bi.buf_read_ptr = 0;
594 bi.buf_write_count = 0;
595 bi.buf_read_count = 0;
Ian Abbott4772c012010-05-19 18:09:49 +0100596 bi.bytes_read = 0;
597 bi.bytes_written = 0;
David Schleefed9eccb2008-11-04 20:29:31 -0800598 goto copyback;
599 }
Ian Abbott53fa8272010-05-19 18:09:50 +0100600 if (!s->busy) {
601 bi.bytes_read = 0;
602 bi.bytes_written = 0;
603 goto copyback_position;
604 }
605 if (s->busy != file)
606 return -EACCES;
David Schleefed9eccb2008-11-04 20:29:31 -0800607
608 if (bi.bytes_read && (s->subdev_flags & SDF_CMD_READ)) {
609 bi.bytes_read = comedi_buf_read_alloc(async, bi.bytes_read);
610 comedi_buf_read_free(async, bi.bytes_read);
611
612 if (!(comedi_get_subdevice_runflags(s) & (SRF_ERROR |
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800613 SRF_RUNNING))
614 && async->buf_write_count == async->buf_read_count) {
David Schleefed9eccb2008-11-04 20:29:31 -0800615 do_become_nonbusy(dev, s);
616 }
617 }
618
619 if (bi.bytes_written && (s->subdev_flags & SDF_CMD_WRITE)) {
620 bi.bytes_written =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800621 comedi_buf_write_alloc(async, bi.bytes_written);
David Schleefed9eccb2008-11-04 20:29:31 -0800622 comedi_buf_write_free(async, bi.bytes_written);
623 }
624
Ian Abbott53fa8272010-05-19 18:09:50 +0100625copyback_position:
David Schleefed9eccb2008-11-04 20:29:31 -0800626 bi.buf_write_count = async->buf_write_count;
627 bi.buf_write_ptr = async->buf_write_ptr;
628 bi.buf_read_count = async->buf_read_count;
629 bi.buf_read_ptr = async->buf_read_ptr;
630
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800631copyback:
Bill Pemberton9aa53392009-03-16 22:06:42 -0400632 if (copy_to_user(arg, &bi, sizeof(struct comedi_bufinfo)))
David Schleefed9eccb2008-11-04 20:29:31 -0800633 return -EFAULT;
634
635 return 0;
636}
637
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530638static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
639 unsigned int *data, void *file);
David Schleefed9eccb2008-11-04 20:29:31 -0800640/*
Pieter De Praetere20617f22010-03-10 09:47:44 +0100641 * COMEDI_INSNLIST
642 * synchronous instructions
David Schleefed9eccb2008-11-04 20:29:31 -0800643 *
Pieter De Praetere20617f22010-03-10 09:47:44 +0100644 * arg:
645 * pointer to sync cmd structure
David Schleefed9eccb2008-11-04 20:29:31 -0800646 *
Pieter De Praetere20617f22010-03-10 09:47:44 +0100647 * reads:
648 * sync cmd struct at arg
649 * instruction list
650 * data (for writes)
David Schleefed9eccb2008-11-04 20:29:31 -0800651 *
Pieter De Praetere20617f22010-03-10 09:47:44 +0100652 * writes:
653 * data (for reads)
David Schleefed9eccb2008-11-04 20:29:31 -0800654 */
655/* arbitrary limits */
656#define MAX_SAMPLES 256
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -0700657static int do_insnlist_ioctl(struct comedi_device *dev,
658 struct comedi_insnlist __user *arg, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -0800659{
Bill Pembertonda613f42009-03-16 22:05:59 -0400660 struct comedi_insnlist insnlist;
Bill Pemberton90035c02009-03-16 22:05:53 -0400661 struct comedi_insn *insns = NULL;
Bill Pemberton790c5542009-03-16 22:05:02 -0400662 unsigned int *data = NULL;
David Schleefed9eccb2008-11-04 20:29:31 -0800663 int i = 0;
664 int ret = 0;
665
Bill Pembertonda613f42009-03-16 22:05:59 -0400666 if (copy_from_user(&insnlist, arg, sizeof(struct comedi_insnlist)))
David Schleefed9eccb2008-11-04 20:29:31 -0800667 return -EFAULT;
668
Bill Pemberton790c5542009-03-16 22:05:02 -0400669 data = kmalloc(sizeof(unsigned int) * MAX_SAMPLES, GFP_KERNEL);
David Schleefed9eccb2008-11-04 20:29:31 -0800670 if (!data) {
671 DPRINTK("kmalloc failed\n");
672 ret = -ENOMEM;
673 goto error;
674 }
675
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530676 insns =
Xi Wangdfd8ee92011-11-25 16:46:51 -0500677 kcalloc(insnlist.n_insns, sizeof(struct comedi_insn), GFP_KERNEL);
David Schleefed9eccb2008-11-04 20:29:31 -0800678 if (!insns) {
679 DPRINTK("kmalloc failed\n");
680 ret = -ENOMEM;
681 goto error;
682 }
683
684 if (copy_from_user(insns, insnlist.insns,
Bill Pemberton90035c02009-03-16 22:05:53 -0400685 sizeof(struct comedi_insn) * insnlist.n_insns)) {
David Schleefed9eccb2008-11-04 20:29:31 -0800686 DPRINTK("copy_from_user failed\n");
687 ret = -EFAULT;
688 goto error;
689 }
690
691 for (i = 0; i < insnlist.n_insns; i++) {
692 if (insns[i].n > MAX_SAMPLES) {
693 DPRINTK("number of samples too large\n");
694 ret = -EINVAL;
695 goto error;
696 }
697 if (insns[i].insn & INSN_MASK_WRITE) {
698 if (copy_from_user(data, insns[i].data,
Bill Pemberton790c5542009-03-16 22:05:02 -0400699 insns[i].n * sizeof(unsigned int))) {
David Schleefed9eccb2008-11-04 20:29:31 -0800700 DPRINTK("copy_from_user failed\n");
701 ret = -EFAULT;
702 goto error;
703 }
704 }
705 ret = parse_insn(dev, insns + i, data, file);
706 if (ret < 0)
707 goto error;
708 if (insns[i].insn & INSN_MASK_READ) {
709 if (copy_to_user(insns[i].data, data,
Bill Pemberton790c5542009-03-16 22:05:02 -0400710 insns[i].n * sizeof(unsigned int))) {
David Schleefed9eccb2008-11-04 20:29:31 -0800711 DPRINTK("copy_to_user failed\n");
712 ret = -EFAULT;
713 goto error;
714 }
715 }
716 if (need_resched())
717 schedule();
718 }
719
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800720error:
721 kfree(insns);
722 kfree(data);
David Schleefed9eccb2008-11-04 20:29:31 -0800723
724 if (ret < 0)
725 return ret;
726 return i;
727}
728
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530729static int check_insn_config_length(struct comedi_insn *insn,
730 unsigned int *data)
David Schleefed9eccb2008-11-04 20:29:31 -0800731{
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800732 if (insn->n < 1)
733 return -EINVAL;
David Schleefed9eccb2008-11-04 20:29:31 -0800734
735 switch (data[0]) {
736 case INSN_CONFIG_DIO_OUTPUT:
737 case INSN_CONFIG_DIO_INPUT:
738 case INSN_CONFIG_DISARM:
739 case INSN_CONFIG_RESET:
740 if (insn->n == 1)
741 return 0;
742 break;
743 case INSN_CONFIG_ARM:
744 case INSN_CONFIG_DIO_QUERY:
745 case INSN_CONFIG_BLOCK_SIZE:
746 case INSN_CONFIG_FILTER:
747 case INSN_CONFIG_SERIAL_CLOCK:
748 case INSN_CONFIG_BIDIRECTIONAL_DATA:
749 case INSN_CONFIG_ALT_SOURCE:
750 case INSN_CONFIG_SET_COUNTER_MODE:
751 case INSN_CONFIG_8254_READ_STATUS:
752 case INSN_CONFIG_SET_ROUTING:
753 case INSN_CONFIG_GET_ROUTING:
754 case INSN_CONFIG_GET_PWM_STATUS:
755 case INSN_CONFIG_PWM_SET_PERIOD:
756 case INSN_CONFIG_PWM_GET_PERIOD:
757 if (insn->n == 2)
758 return 0;
759 break;
760 case INSN_CONFIG_SET_GATE_SRC:
761 case INSN_CONFIG_GET_GATE_SRC:
762 case INSN_CONFIG_SET_CLOCK_SRC:
763 case INSN_CONFIG_GET_CLOCK_SRC:
764 case INSN_CONFIG_SET_OTHER_SRC:
765 case INSN_CONFIG_GET_COUNTER_STATUS:
766 case INSN_CONFIG_PWM_SET_H_BRIDGE:
767 case INSN_CONFIG_PWM_GET_H_BRIDGE:
768 case INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE:
769 if (insn->n == 3)
770 return 0;
771 break;
772 case INSN_CONFIG_PWM_OUTPUT:
773 case INSN_CONFIG_ANALOG_TRIG:
774 if (insn->n == 5)
775 return 0;
776 break;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530777 /* by default we allow the insn since we don't have checks for
778 * all possible cases yet */
David Schleefed9eccb2008-11-04 20:29:31 -0800779 default:
Mark Rankilor3fffdf22010-04-29 18:17:16 +0800780 printk(KERN_WARNING
781 "comedi: no check for data length of config insn id "
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530782 "%i is implemented.\n"
783 " Add a check to %s in %s.\n"
784 " Assuming n=%i is correct.\n", data[0], __func__,
785 __FILE__, insn->n);
David Schleefed9eccb2008-11-04 20:29:31 -0800786 return 0;
787 break;
788 }
789 return -EINVAL;
790}
791
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530792static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
793 unsigned int *data, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -0800794{
Bill Pemberton34c43922009-03-16 22:05:14 -0400795 struct comedi_subdevice *s;
David Schleefed9eccb2008-11-04 20:29:31 -0800796 int ret = 0;
797 int i;
798
799 if (insn->insn & INSN_MASK_SPECIAL) {
800 /* a non-subdevice instruction */
801
802 switch (insn->insn) {
803 case INSN_GTOD:
804 {
805 struct timeval tv;
806
807 if (insn->n != 2) {
808 ret = -EINVAL;
809 break;
810 }
811
812 do_gettimeofday(&tv);
813 data[0] = tv.tv_sec;
814 data[1] = tv.tv_usec;
815 ret = 2;
816
817 break;
818 }
819 case INSN_WAIT:
820 if (insn->n != 1 || data[0] >= 100000) {
821 ret = -EINVAL;
822 break;
823 }
824 udelay(data[0] / 1000);
825 ret = 1;
826 break;
827 case INSN_INTTRIG:
828 if (insn->n != 1) {
829 ret = -EINVAL;
830 break;
831 }
832 if (insn->subdev >= dev->n_subdevices) {
833 DPRINTK("%d not usable subdevice\n",
834 insn->subdev);
835 ret = -EINVAL;
836 break;
837 }
838 s = dev->subdevices + insn->subdev;
839 if (!s->async) {
840 DPRINTK("no async\n");
841 ret = -EINVAL;
842 break;
843 }
844 if (!s->async->inttrig) {
845 DPRINTK("no inttrig\n");
846 ret = -EAGAIN;
847 break;
848 }
Ian Abbott2b7f2092012-09-18 19:46:58 +0100849 ret = s->async->inttrig(dev, s, data[0]);
David Schleefed9eccb2008-11-04 20:29:31 -0800850 if (ret >= 0)
851 ret = 1;
852 break;
853 default:
854 DPRINTK("invalid insn\n");
855 ret = -EINVAL;
856 break;
857 }
858 } else {
859 /* a subdevice instruction */
Bill Pemberton790c5542009-03-16 22:05:02 -0400860 unsigned int maxdata;
David Schleefed9eccb2008-11-04 20:29:31 -0800861
862 if (insn->subdev >= dev->n_subdevices) {
863 DPRINTK("subdevice %d out of range\n", insn->subdev);
864 ret = -EINVAL;
865 goto out;
866 }
867 s = dev->subdevices + insn->subdev;
868
869 if (s->type == COMEDI_SUBD_UNUSED) {
870 DPRINTK("%d not usable subdevice\n", insn->subdev);
871 ret = -EIO;
872 goto out;
873 }
874
875 /* are we locked? (ioctl lock) */
876 if (s->lock && s->lock != file) {
877 DPRINTK("device locked\n");
878 ret = -EACCES;
879 goto out;
880 }
881
Greg Kroah-Hartman0fd0ca72010-05-01 12:33:17 -0700882 ret = comedi_check_chanlist(s, 1, &insn->chanspec);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800883 if (ret < 0) {
David Schleefed9eccb2008-11-04 20:29:31 -0800884 ret = -EINVAL;
885 DPRINTK("bad chanspec\n");
886 goto out;
887 }
888
889 if (s->busy) {
890 ret = -EBUSY;
891 goto out;
892 }
893 /* This looks arbitrary. It is. */
894 s->busy = &parse_insn;
895 switch (insn->insn) {
896 case INSN_READ:
897 ret = s->insn_read(dev, s, insn, data);
898 break;
899 case INSN_WRITE:
900 maxdata = s->maxdata_list
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800901 ? s->maxdata_list[CR_CHAN(insn->chanspec)]
902 : s->maxdata;
David Schleefed9eccb2008-11-04 20:29:31 -0800903 for (i = 0; i < insn->n; ++i) {
904 if (data[i] > maxdata) {
905 ret = -EINVAL;
906 DPRINTK("bad data value(s)\n");
907 break;
908 }
909 }
910 if (ret == 0)
911 ret = s->insn_write(dev, s, insn, data);
912 break;
913 case INSN_BITS:
914 if (insn->n != 2) {
915 ret = -EINVAL;
Ian Abbott2f644cc2011-01-18 17:44:33 +0000916 } else {
917 /* Most drivers ignore the base channel in
918 * insn->chanspec. Fix this here if
919 * the subdevice has <= 32 channels. */
920 unsigned int shift;
921 unsigned int orig_mask;
922
923 orig_mask = data[0];
924 if (s->n_chan <= 32) {
925 shift = CR_CHAN(insn->chanspec);
926 if (shift > 0) {
927 insn->chanspec = 0;
928 data[0] <<= shift;
929 data[1] <<= shift;
930 }
931 } else
932 shift = 0;
933 ret = s->insn_bits(dev, s, insn, data);
934 data[0] = orig_mask;
935 if (shift > 0)
936 data[1] >>= shift;
David Schleefed9eccb2008-11-04 20:29:31 -0800937 }
David Schleefed9eccb2008-11-04 20:29:31 -0800938 break;
939 case INSN_CONFIG:
940 ret = check_insn_config_length(insn, data);
941 if (ret)
942 break;
943 ret = s->insn_config(dev, s, insn, data);
944 break;
945 default:
946 ret = -EINVAL;
947 break;
948 }
949
950 s->busy = NULL;
951 }
952
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800953out:
David Schleefed9eccb2008-11-04 20:29:31 -0800954 return ret;
955}
956
957/*
Pieter De Praetere20617f22010-03-10 09:47:44 +0100958 * COMEDI_INSN
959 * synchronous instructions
David Schleefed9eccb2008-11-04 20:29:31 -0800960 *
Pieter De Praetere20617f22010-03-10 09:47:44 +0100961 * arg:
962 * pointer to insn
David Schleefed9eccb2008-11-04 20:29:31 -0800963 *
Pieter De Praetere20617f22010-03-10 09:47:44 +0100964 * reads:
965 * struct comedi_insn struct at arg
966 * data (for writes)
David Schleefed9eccb2008-11-04 20:29:31 -0800967 *
Pieter De Praetere20617f22010-03-10 09:47:44 +0100968 * writes:
969 * data (for reads)
David Schleefed9eccb2008-11-04 20:29:31 -0800970 */
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -0700971static int do_insn_ioctl(struct comedi_device *dev,
972 struct comedi_insn __user *arg, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -0800973{
Bill Pemberton90035c02009-03-16 22:05:53 -0400974 struct comedi_insn insn;
Bill Pemberton790c5542009-03-16 22:05:02 -0400975 unsigned int *data = NULL;
David Schleefed9eccb2008-11-04 20:29:31 -0800976 int ret = 0;
977
Bill Pemberton790c5542009-03-16 22:05:02 -0400978 data = kmalloc(sizeof(unsigned int) * MAX_SAMPLES, GFP_KERNEL);
David Schleefed9eccb2008-11-04 20:29:31 -0800979 if (!data) {
980 ret = -ENOMEM;
981 goto error;
982 }
983
Bill Pemberton90035c02009-03-16 22:05:53 -0400984 if (copy_from_user(&insn, arg, sizeof(struct comedi_insn))) {
David Schleefed9eccb2008-11-04 20:29:31 -0800985 ret = -EFAULT;
986 goto error;
987 }
988
989 /* This is where the behavior of insn and insnlist deviate. */
990 if (insn.n > MAX_SAMPLES)
991 insn.n = MAX_SAMPLES;
992 if (insn.insn & INSN_MASK_WRITE) {
Mark21fe2ee2010-05-13 17:44:39 +0800993 if (copy_from_user(data,
994 insn.data,
995 insn.n * sizeof(unsigned int))) {
David Schleefed9eccb2008-11-04 20:29:31 -0800996 ret = -EFAULT;
997 goto error;
998 }
999 }
1000 ret = parse_insn(dev, &insn, data, file);
1001 if (ret < 0)
1002 goto error;
1003 if (insn.insn & INSN_MASK_READ) {
Mark21fe2ee2010-05-13 17:44:39 +08001004 if (copy_to_user(insn.data,
1005 data,
1006 insn.n * sizeof(unsigned int))) {
David Schleefed9eccb2008-11-04 20:29:31 -08001007 ret = -EFAULT;
1008 goto error;
1009 }
1010 }
1011 ret = insn.n;
1012
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001013error:
1014 kfree(data);
David Schleefed9eccb2008-11-04 20:29:31 -08001015
1016 return ret;
1017}
1018
Greg Kroah-Hartman181bd672010-05-03 15:15:06 -07001019static void comedi_set_subdevice_runflags(struct comedi_subdevice *s,
1020 unsigned mask, unsigned bits)
1021{
1022 unsigned long flags;
1023
1024 spin_lock_irqsave(&s->spin_lock, flags);
1025 s->runflags &= ~mask;
1026 s->runflags |= (bits & mask);
1027 spin_unlock_irqrestore(&s->spin_lock, flags);
1028}
1029
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -07001030static int do_cmd_ioctl(struct comedi_device *dev,
1031 struct comedi_cmd __user *cmd, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -08001032{
Bill Pembertonea6d0d42009-03-16 22:05:47 -04001033 struct comedi_cmd user_cmd;
Bill Pemberton34c43922009-03-16 22:05:14 -04001034 struct comedi_subdevice *s;
Bill Pembertond1636792009-03-16 22:05:20 -04001035 struct comedi_async *async;
David Schleefed9eccb2008-11-04 20:29:31 -08001036 int ret = 0;
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -07001037 unsigned int __user *chanlist_saver = NULL;
David Schleefed9eccb2008-11-04 20:29:31 -08001038
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -07001039 if (copy_from_user(&user_cmd, cmd, sizeof(struct comedi_cmd))) {
David Schleefed9eccb2008-11-04 20:29:31 -08001040 DPRINTK("bad cmd address\n");
1041 return -EFAULT;
1042 }
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001043 /* save user's chanlist pointer so it can be restored later */
David Schleefed9eccb2008-11-04 20:29:31 -08001044 chanlist_saver = user_cmd.chanlist;
1045
1046 if (user_cmd.subdev >= dev->n_subdevices) {
1047 DPRINTK("%d no such subdevice\n", user_cmd.subdev);
1048 return -ENODEV;
1049 }
1050
1051 s = dev->subdevices + user_cmd.subdev;
1052 async = s->async;
1053
1054 if (s->type == COMEDI_SUBD_UNUSED) {
1055 DPRINTK("%d not valid subdevice\n", user_cmd.subdev);
1056 return -EIO;
1057 }
1058
1059 if (!s->do_cmd || !s->do_cmdtest || !s->async) {
1060 DPRINTK("subdevice %i does not support commands\n",
1061 user_cmd.subdev);
1062 return -EIO;
1063 }
1064
1065 /* are we locked? (ioctl lock) */
1066 if (s->lock && s->lock != file) {
1067 DPRINTK("subdevice locked\n");
1068 return -EACCES;
1069 }
1070
1071 /* are we busy? */
1072 if (s->busy) {
1073 DPRINTK("subdevice busy\n");
1074 return -EBUSY;
1075 }
1076 s->busy = file;
1077
1078 /* make sure channel/gain list isn't too long */
1079 if (user_cmd.chanlist_len > s->len_chanlist) {
1080 DPRINTK("channel/gain list too long %u > %d\n",
1081 user_cmd.chanlist_len, s->len_chanlist);
1082 ret = -EINVAL;
1083 goto cleanup;
1084 }
1085
1086 /* make sure channel/gain list isn't too short */
1087 if (user_cmd.chanlist_len < 1) {
1088 DPRINTK("channel/gain list too short %u < 1\n",
1089 user_cmd.chanlist_len);
1090 ret = -EINVAL;
1091 goto cleanup;
1092 }
1093
David Schleefed9eccb2008-11-04 20:29:31 -08001094 async->cmd = user_cmd;
1095 async->cmd.data = NULL;
1096 /* load channel/gain list */
1097 async->cmd.chanlist =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001098 kmalloc(async->cmd.chanlist_len * sizeof(int), GFP_KERNEL);
David Schleefed9eccb2008-11-04 20:29:31 -08001099 if (!async->cmd.chanlist) {
1100 DPRINTK("allocation failed\n");
1101 ret = -ENOMEM;
1102 goto cleanup;
1103 }
1104
1105 if (copy_from_user(async->cmd.chanlist, user_cmd.chanlist,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001106 async->cmd.chanlist_len * sizeof(int))) {
David Schleefed9eccb2008-11-04 20:29:31 -08001107 DPRINTK("fault reading chanlist\n");
1108 ret = -EFAULT;
1109 goto cleanup;
1110 }
1111
1112 /* make sure each element in channel/gain list is valid */
Mark21fe2ee2010-05-13 17:44:39 +08001113 ret = comedi_check_chanlist(s,
1114 async->cmd.chanlist_len,
1115 async->cmd.chanlist);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001116 if (ret < 0) {
David Schleefed9eccb2008-11-04 20:29:31 -08001117 DPRINTK("bad chanlist\n");
1118 goto cleanup;
1119 }
1120
1121 ret = s->do_cmdtest(dev, s, &async->cmd);
1122
1123 if (async->cmd.flags & TRIG_BOGUS || ret) {
1124 DPRINTK("test returned %d\n", ret);
1125 user_cmd = async->cmd;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001126 /* restore chanlist pointer before copying back */
David Schleefed9eccb2008-11-04 20:29:31 -08001127 user_cmd.chanlist = chanlist_saver;
1128 user_cmd.data = NULL;
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -07001129 if (copy_to_user(cmd, &user_cmd, sizeof(struct comedi_cmd))) {
David Schleefed9eccb2008-11-04 20:29:31 -08001130 DPRINTK("fault writing cmd\n");
1131 ret = -EFAULT;
1132 goto cleanup;
1133 }
1134 ret = -EAGAIN;
1135 goto cleanup;
1136 }
1137
1138 if (!async->prealloc_bufsz) {
1139 ret = -ENOMEM;
1140 DPRINTK("no buffer (?)\n");
1141 goto cleanup;
1142 }
1143
1144 comedi_reset_async_buf(async);
1145
1146 async->cb_mask =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001147 COMEDI_CB_EOA | COMEDI_CB_BLOCK | COMEDI_CB_ERROR |
1148 COMEDI_CB_OVERFLOW;
1149 if (async->cmd.flags & TRIG_WAKE_EOS)
David Schleefed9eccb2008-11-04 20:29:31 -08001150 async->cb_mask |= COMEDI_CB_EOS;
David Schleefed9eccb2008-11-04 20:29:31 -08001151
1152 comedi_set_subdevice_runflags(s, ~0, SRF_USER | SRF_RUNNING);
1153
David Schleefed9eccb2008-11-04 20:29:31 -08001154 ret = s->do_cmd(dev, s);
1155 if (ret == 0)
1156 return 0;
1157
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001158cleanup:
David Schleefed9eccb2008-11-04 20:29:31 -08001159 do_become_nonbusy(dev, s);
1160
1161 return ret;
1162}
1163
1164/*
1165 COMEDI_CMDTEST
1166 command testing ioctl
1167
1168 arg:
1169 pointer to cmd structure
1170
1171 reads:
1172 cmd structure at arg
1173 channel/range list
1174
1175 writes:
1176 modified cmd structure at arg
1177
1178*/
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -07001179static int do_cmdtest_ioctl(struct comedi_device *dev,
1180 struct comedi_cmd __user *arg, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -08001181{
Bill Pembertonea6d0d42009-03-16 22:05:47 -04001182 struct comedi_cmd user_cmd;
Bill Pemberton34c43922009-03-16 22:05:14 -04001183 struct comedi_subdevice *s;
David Schleefed9eccb2008-11-04 20:29:31 -08001184 int ret = 0;
1185 unsigned int *chanlist = NULL;
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -07001186 unsigned int __user *chanlist_saver = NULL;
David Schleefed9eccb2008-11-04 20:29:31 -08001187
Bill Pembertonea6d0d42009-03-16 22:05:47 -04001188 if (copy_from_user(&user_cmd, arg, sizeof(struct comedi_cmd))) {
David Schleefed9eccb2008-11-04 20:29:31 -08001189 DPRINTK("bad cmd address\n");
1190 return -EFAULT;
1191 }
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001192 /* save user's chanlist pointer so it can be restored later */
David Schleefed9eccb2008-11-04 20:29:31 -08001193 chanlist_saver = user_cmd.chanlist;
1194
1195 if (user_cmd.subdev >= dev->n_subdevices) {
1196 DPRINTK("%d no such subdevice\n", user_cmd.subdev);
1197 return -ENODEV;
1198 }
1199
1200 s = dev->subdevices + user_cmd.subdev;
1201 if (s->type == COMEDI_SUBD_UNUSED) {
1202 DPRINTK("%d not valid subdevice\n", user_cmd.subdev);
1203 return -EIO;
1204 }
1205
1206 if (!s->do_cmd || !s->do_cmdtest) {
1207 DPRINTK("subdevice %i does not support commands\n",
1208 user_cmd.subdev);
1209 return -EIO;
1210 }
1211
1212 /* make sure channel/gain list isn't too long */
1213 if (user_cmd.chanlist_len > s->len_chanlist) {
1214 DPRINTK("channel/gain list too long %d > %d\n",
1215 user_cmd.chanlist_len, s->len_chanlist);
1216 ret = -EINVAL;
1217 goto cleanup;
1218 }
1219
1220 /* load channel/gain list */
1221 if (user_cmd.chanlist) {
1222 chanlist =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001223 kmalloc(user_cmd.chanlist_len * sizeof(int), GFP_KERNEL);
David Schleefed9eccb2008-11-04 20:29:31 -08001224 if (!chanlist) {
1225 DPRINTK("allocation failed\n");
1226 ret = -ENOMEM;
1227 goto cleanup;
1228 }
1229
1230 if (copy_from_user(chanlist, user_cmd.chanlist,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001231 user_cmd.chanlist_len * sizeof(int))) {
David Schleefed9eccb2008-11-04 20:29:31 -08001232 DPRINTK("fault reading chanlist\n");
1233 ret = -EFAULT;
1234 goto cleanup;
1235 }
1236
1237 /* make sure each element in channel/gain list is valid */
Greg Kroah-Hartman0fd0ca72010-05-01 12:33:17 -07001238 ret = comedi_check_chanlist(s, user_cmd.chanlist_len, chanlist);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001239 if (ret < 0) {
David Schleefed9eccb2008-11-04 20:29:31 -08001240 DPRINTK("bad chanlist\n");
1241 goto cleanup;
1242 }
1243
1244 user_cmd.chanlist = chanlist;
1245 }
1246
1247 ret = s->do_cmdtest(dev, s, &user_cmd);
1248
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001249 /* restore chanlist pointer before copying back */
David Schleefed9eccb2008-11-04 20:29:31 -08001250 user_cmd.chanlist = chanlist_saver;
1251
Bill Pembertonea6d0d42009-03-16 22:05:47 -04001252 if (copy_to_user(arg, &user_cmd, sizeof(struct comedi_cmd))) {
David Schleefed9eccb2008-11-04 20:29:31 -08001253 DPRINTK("bad cmd address\n");
1254 ret = -EFAULT;
1255 goto cleanup;
1256 }
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001257cleanup:
1258 kfree(chanlist);
David Schleefed9eccb2008-11-04 20:29:31 -08001259
1260 return ret;
1261}
1262
1263/*
1264 COMEDI_LOCK
1265 lock subdevice
1266
1267 arg:
1268 subdevice number
1269
1270 reads:
1271 none
1272
1273 writes:
1274 none
1275
1276*/
1277
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301278static int do_lock_ioctl(struct comedi_device *dev, unsigned int arg,
1279 void *file)
David Schleefed9eccb2008-11-04 20:29:31 -08001280{
1281 int ret = 0;
1282 unsigned long flags;
Bill Pemberton34c43922009-03-16 22:05:14 -04001283 struct comedi_subdevice *s;
David Schleefed9eccb2008-11-04 20:29:31 -08001284
1285 if (arg >= dev->n_subdevices)
1286 return -EINVAL;
1287 s = dev->subdevices + arg;
1288
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001289 spin_lock_irqsave(&s->spin_lock, flags);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001290 if (s->busy || s->lock)
David Schleefed9eccb2008-11-04 20:29:31 -08001291 ret = -EBUSY;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001292 else
David Schleefed9eccb2008-11-04 20:29:31 -08001293 s->lock = file;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001294 spin_unlock_irqrestore(&s->spin_lock, flags);
David Schleefed9eccb2008-11-04 20:29:31 -08001295
Greg Dietschec5274ab2011-06-13 13:11:47 -05001296#if 0
David Schleefed9eccb2008-11-04 20:29:31 -08001297 if (ret < 0)
1298 return ret;
1299
David Schleefed9eccb2008-11-04 20:29:31 -08001300 if (s->lock_f)
1301 ret = s->lock_f(dev, s);
1302#endif
1303
1304 return ret;
1305}
1306
1307/*
1308 COMEDI_UNLOCK
1309 unlock subdevice
1310
1311 arg:
1312 subdevice number
1313
1314 reads:
1315 none
1316
1317 writes:
1318 none
1319
1320 This function isn't protected by the semaphore, since
1321 we already own the lock.
1322*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301323static int do_unlock_ioctl(struct comedi_device *dev, unsigned int arg,
1324 void *file)
David Schleefed9eccb2008-11-04 20:29:31 -08001325{
Bill Pemberton34c43922009-03-16 22:05:14 -04001326 struct comedi_subdevice *s;
David Schleefed9eccb2008-11-04 20:29:31 -08001327
1328 if (arg >= dev->n_subdevices)
1329 return -EINVAL;
1330 s = dev->subdevices + arg;
1331
1332 if (s->busy)
1333 return -EBUSY;
1334
1335 if (s->lock && s->lock != file)
1336 return -EACCES;
1337
1338 if (s->lock == file) {
1339#if 0
1340 if (s->unlock)
1341 s->unlock(dev, s);
1342#endif
1343
1344 s->lock = NULL;
1345 }
1346
1347 return 0;
1348}
1349
1350/*
1351 COMEDI_CANCEL
1352 cancel acquisition ioctl
1353
1354 arg:
1355 subdevice number
1356
1357 reads:
1358 nothing
1359
1360 writes:
1361 nothing
1362
1363*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301364static int do_cancel_ioctl(struct comedi_device *dev, unsigned int arg,
1365 void *file)
David Schleefed9eccb2008-11-04 20:29:31 -08001366{
Bill Pemberton34c43922009-03-16 22:05:14 -04001367 struct comedi_subdevice *s;
David Schleefed9eccb2008-11-04 20:29:31 -08001368
1369 if (arg >= dev->n_subdevices)
1370 return -EINVAL;
1371 s = dev->subdevices + arg;
1372 if (s->async == NULL)
1373 return -EINVAL;
1374
1375 if (s->lock && s->lock != file)
1376 return -EACCES;
1377
1378 if (!s->busy)
1379 return 0;
1380
1381 if (s->busy != file)
1382 return -EBUSY;
1383
1384 return do_cancel(dev, s);
1385}
1386
1387/*
1388 COMEDI_POLL ioctl
1389 instructs driver to synchronize buffers
1390
1391 arg:
1392 subdevice number
1393
1394 reads:
1395 nothing
1396
1397 writes:
1398 nothing
1399
1400*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301401static int do_poll_ioctl(struct comedi_device *dev, unsigned int arg,
1402 void *file)
David Schleefed9eccb2008-11-04 20:29:31 -08001403{
Bill Pemberton34c43922009-03-16 22:05:14 -04001404 struct comedi_subdevice *s;
David Schleefed9eccb2008-11-04 20:29:31 -08001405
1406 if (arg >= dev->n_subdevices)
1407 return -EINVAL;
1408 s = dev->subdevices + arg;
1409
1410 if (s->lock && s->lock != file)
1411 return -EACCES;
1412
1413 if (!s->busy)
1414 return 0;
1415
1416 if (s->busy != file)
1417 return -EBUSY;
1418
1419 if (s->poll)
1420 return s->poll(dev, s);
1421
1422 return -EINVAL;
1423}
1424
Bill Pemberton34c43922009-03-16 22:05:14 -04001425static int do_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
David Schleefed9eccb2008-11-04 20:29:31 -08001426{
1427 int ret = 0;
1428
1429 if ((comedi_get_subdevice_runflags(s) & SRF_RUNNING) && s->cancel)
1430 ret = s->cancel(dev, s);
1431
1432 do_become_nonbusy(dev, s);
1433
1434 return ret;
1435}
1436
Federico Vagadf30b212011-10-29 09:45:39 +02001437
1438static void comedi_vm_open(struct vm_area_struct *area)
1439{
1440 struct comedi_async *async;
1441 struct comedi_device *dev;
1442
1443 async = area->vm_private_data;
1444 dev = async->subdevice->device;
1445
1446 mutex_lock(&dev->mutex);
1447 async->mmap_count++;
1448 mutex_unlock(&dev->mutex);
1449}
1450
1451static void comedi_vm_close(struct vm_area_struct *area)
David Schleefed9eccb2008-11-04 20:29:31 -08001452{
Bill Pembertond1636792009-03-16 22:05:20 -04001453 struct comedi_async *async;
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04001454 struct comedi_device *dev;
David Schleefed9eccb2008-11-04 20:29:31 -08001455
1456 async = area->vm_private_data;
1457 dev = async->subdevice->device;
1458
1459 mutex_lock(&dev->mutex);
1460 async->mmap_count--;
1461 mutex_unlock(&dev->mutex);
1462}
1463
1464static struct vm_operations_struct comedi_vm_ops = {
Federico Vagadf30b212011-10-29 09:45:39 +02001465 .open = comedi_vm_open,
1466 .close = comedi_vm_close,
David Schleefed9eccb2008-11-04 20:29:31 -08001467};
1468
1469static int comedi_mmap(struct file *file, struct vm_area_struct *vma)
1470{
1471 const unsigned minor = iminor(file->f_dentry->d_inode);
Bill Pembertond1636792009-03-16 22:05:20 -04001472 struct comedi_async *async = NULL;
David Schleefed9eccb2008-11-04 20:29:31 -08001473 unsigned long start = vma->vm_start;
1474 unsigned long size;
1475 int n_pages;
1476 int i;
1477 int retval;
Bill Pemberton34c43922009-03-16 22:05:14 -04001478 struct comedi_subdevice *s;
Bernd Porr3ffab422011-11-08 21:23:03 +00001479 struct comedi_device_file_info *dev_file_info;
1480 struct comedi_device *dev;
1481
1482 dev_file_info = comedi_get_device_file_info(minor);
1483 if (dev_file_info == NULL)
Florian Schmaus70fe7422011-12-14 14:23:24 +01001484 return -ENODEV;
Bernd Porr3ffab422011-11-08 21:23:03 +00001485 dev = dev_file_info->device;
1486 if (dev == NULL)
Florian Schmaus70fe7422011-12-14 14:23:24 +01001487 return -ENODEV;
David Schleefed9eccb2008-11-04 20:29:31 -08001488
1489 mutex_lock(&dev->mutex);
1490 if (!dev->attached) {
1491 DPRINTK("no driver configured on comedi%i\n", dev->minor);
1492 retval = -ENODEV;
1493 goto done;
1494 }
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001495 if (vma->vm_flags & VM_WRITE)
David Schleefed9eccb2008-11-04 20:29:31 -08001496 s = comedi_get_write_subdevice(dev_file_info);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001497 else
David Schleefed9eccb2008-11-04 20:29:31 -08001498 s = comedi_get_read_subdevice(dev_file_info);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001499
David Schleefed9eccb2008-11-04 20:29:31 -08001500 if (s == NULL) {
1501 retval = -EINVAL;
1502 goto done;
1503 }
1504 async = s->async;
1505 if (async == NULL) {
1506 retval = -EINVAL;
1507 goto done;
1508 }
1509
1510 if (vma->vm_pgoff != 0) {
1511 DPRINTK("comedi: mmap() offset must be 0.\n");
1512 retval = -EINVAL;
1513 goto done;
1514 }
1515
1516 size = vma->vm_end - vma->vm_start;
1517 if (size > async->prealloc_bufsz) {
1518 retval = -EFAULT;
1519 goto done;
1520 }
1521 if (size & (~PAGE_MASK)) {
1522 retval = -EFAULT;
1523 goto done;
1524 }
1525
1526 n_pages = size >> PAGE_SHIFT;
1527 for (i = 0; i < n_pages; ++i) {
1528 if (remap_pfn_range(vma, start,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301529 page_to_pfn(virt_to_page
1530 (async->buf_page_list
1531 [i].virt_addr)), PAGE_SIZE,
1532 PAGE_SHARED)) {
David Schleefed9eccb2008-11-04 20:29:31 -08001533 retval = -EAGAIN;
1534 goto done;
1535 }
1536 start += PAGE_SIZE;
1537 }
1538
1539 vma->vm_ops = &comedi_vm_ops;
1540 vma->vm_private_data = async;
1541
1542 async->mmap_count++;
1543
1544 retval = 0;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001545done:
David Schleefed9eccb2008-11-04 20:29:31 -08001546 mutex_unlock(&dev->mutex);
1547 return retval;
1548}
1549
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301550static unsigned int comedi_poll(struct file *file, poll_table * wait)
David Schleefed9eccb2008-11-04 20:29:31 -08001551{
1552 unsigned int mask = 0;
1553 const unsigned minor = iminor(file->f_dentry->d_inode);
Bill Pemberton34c43922009-03-16 22:05:14 -04001554 struct comedi_subdevice *read_subdev;
1555 struct comedi_subdevice *write_subdev;
Bernd Porr3ffab422011-11-08 21:23:03 +00001556 struct comedi_device_file_info *dev_file_info;
1557 struct comedi_device *dev;
1558 dev_file_info = comedi_get_device_file_info(minor);
1559
1560 if (dev_file_info == NULL)
Florian Schmaus70fe7422011-12-14 14:23:24 +01001561 return -ENODEV;
Bernd Porr3ffab422011-11-08 21:23:03 +00001562 dev = dev_file_info->device;
1563 if (dev == NULL)
Florian Schmaus70fe7422011-12-14 14:23:24 +01001564 return -ENODEV;
David Schleefed9eccb2008-11-04 20:29:31 -08001565
1566 mutex_lock(&dev->mutex);
1567 if (!dev->attached) {
1568 DPRINTK("no driver configured on comedi%i\n", dev->minor);
1569 mutex_unlock(&dev->mutex);
1570 return 0;
1571 }
1572
1573 mask = 0;
1574 read_subdev = comedi_get_read_subdevice(dev_file_info);
1575 if (read_subdev) {
1576 poll_wait(file, &read_subdev->async->wait_head, wait);
1577 if (!read_subdev->busy
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001578 || comedi_buf_read_n_available(read_subdev->async) > 0
1579 || !(comedi_get_subdevice_runflags(read_subdev) &
1580 SRF_RUNNING)) {
David Schleefed9eccb2008-11-04 20:29:31 -08001581 mask |= POLLIN | POLLRDNORM;
1582 }
1583 }
1584 write_subdev = comedi_get_write_subdevice(dev_file_info);
1585 if (write_subdev) {
1586 poll_wait(file, &write_subdev->async->wait_head, wait);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001587 comedi_buf_write_alloc(write_subdev->async,
1588 write_subdev->async->prealloc_bufsz);
David Schleefed9eccb2008-11-04 20:29:31 -08001589 if (!write_subdev->busy
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001590 || !(comedi_get_subdevice_runflags(write_subdev) &
1591 SRF_RUNNING)
1592 || comedi_buf_write_n_allocated(write_subdev->async) >=
1593 bytes_per_sample(write_subdev->async->subdevice)) {
David Schleefed9eccb2008-11-04 20:29:31 -08001594 mask |= POLLOUT | POLLWRNORM;
1595 }
1596 }
1597
1598 mutex_unlock(&dev->mutex);
1599 return mask;
1600}
1601
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -07001602static ssize_t comedi_write(struct file *file, const char __user *buf,
1603 size_t nbytes, loff_t *offset)
David Schleefed9eccb2008-11-04 20:29:31 -08001604{
Bill Pemberton34c43922009-03-16 22:05:14 -04001605 struct comedi_subdevice *s;
Bill Pembertond1636792009-03-16 22:05:20 -04001606 struct comedi_async *async;
David Schleefed9eccb2008-11-04 20:29:31 -08001607 int n, m, count = 0, retval = 0;
1608 DECLARE_WAITQUEUE(wait, current);
1609 const unsigned minor = iminor(file->f_dentry->d_inode);
Bernd Porr3ffab422011-11-08 21:23:03 +00001610 struct comedi_device_file_info *dev_file_info;
1611 struct comedi_device *dev;
1612 dev_file_info = comedi_get_device_file_info(minor);
1613
1614 if (dev_file_info == NULL)
Florian Schmaus70fe7422011-12-14 14:23:24 +01001615 return -ENODEV;
Bernd Porr3ffab422011-11-08 21:23:03 +00001616 dev = dev_file_info->device;
1617 if (dev == NULL)
Florian Schmaus70fe7422011-12-14 14:23:24 +01001618 return -ENODEV;
David Schleefed9eccb2008-11-04 20:29:31 -08001619
1620 if (!dev->attached) {
1621 DPRINTK("no driver configured on comedi%i\n", dev->minor);
1622 retval = -ENODEV;
1623 goto done;
1624 }
1625
1626 s = comedi_get_write_subdevice(dev_file_info);
1627 if (s == NULL) {
1628 retval = -EIO;
1629 goto done;
1630 }
1631 async = s->async;
1632
1633 if (!nbytes) {
1634 retval = 0;
1635 goto done;
1636 }
1637 if (!s->busy) {
1638 retval = 0;
1639 goto done;
1640 }
1641 if (s->busy != file) {
1642 retval = -EACCES;
1643 goto done;
1644 }
1645 add_wait_queue(&async->wait_head, &wait);
1646 while (nbytes > 0 && !retval) {
1647 set_current_state(TASK_INTERRUPTIBLE);
1648
Ian Abbottd2611542010-05-19 17:22:41 +01001649 if (!(comedi_get_subdevice_runflags(s) & SRF_RUNNING)) {
1650 if (count == 0) {
1651 if (comedi_get_subdevice_runflags(s) &
1652 SRF_ERROR) {
1653 retval = -EPIPE;
1654 } else {
1655 retval = 0;
1656 }
1657 do_become_nonbusy(dev, s);
1658 }
1659 break;
1660 }
1661
David Schleefed9eccb2008-11-04 20:29:31 -08001662 n = nbytes;
1663
1664 m = n;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001665 if (async->buf_write_ptr + m > async->prealloc_bufsz)
David Schleefed9eccb2008-11-04 20:29:31 -08001666 m = async->prealloc_bufsz - async->buf_write_ptr;
David Schleefed9eccb2008-11-04 20:29:31 -08001667 comedi_buf_write_alloc(async, async->prealloc_bufsz);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001668 if (m > comedi_buf_write_n_allocated(async))
David Schleefed9eccb2008-11-04 20:29:31 -08001669 m = comedi_buf_write_n_allocated(async);
David Schleefed9eccb2008-11-04 20:29:31 -08001670 if (m < n)
1671 n = m;
1672
1673 if (n == 0) {
David Schleefed9eccb2008-11-04 20:29:31 -08001674 if (file->f_flags & O_NONBLOCK) {
1675 retval = -EAGAIN;
1676 break;
1677 }
Federico Vaga6a9ce6b2011-10-29 09:47:39 +02001678 schedule();
David Schleefed9eccb2008-11-04 20:29:31 -08001679 if (signal_pending(current)) {
1680 retval = -ERESTARTSYS;
1681 break;
1682 }
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001683 if (!s->busy)
David Schleefed9eccb2008-11-04 20:29:31 -08001684 break;
David Schleefed9eccb2008-11-04 20:29:31 -08001685 if (s->busy != file) {
1686 retval = -EACCES;
1687 break;
1688 }
1689 continue;
1690 }
1691
1692 m = copy_from_user(async->prealloc_buf + async->buf_write_ptr,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001693 buf, n);
David Schleefed9eccb2008-11-04 20:29:31 -08001694 if (m) {
1695 n -= m;
1696 retval = -EFAULT;
1697 }
1698 comedi_buf_write_free(async, n);
1699
1700 count += n;
1701 nbytes -= n;
1702
1703 buf += n;
1704 break; /* makes device work like a pipe */
1705 }
1706 set_current_state(TASK_RUNNING);
1707 remove_wait_queue(&async->wait_head, &wait);
1708
1709done:
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001710 return count ? count : retval;
David Schleefed9eccb2008-11-04 20:29:31 -08001711}
1712
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -07001713static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes,
Andrea Gelmini6705b682010-02-26 10:14:55 +01001714 loff_t *offset)
David Schleefed9eccb2008-11-04 20:29:31 -08001715{
Bill Pemberton34c43922009-03-16 22:05:14 -04001716 struct comedi_subdevice *s;
Bill Pembertond1636792009-03-16 22:05:20 -04001717 struct comedi_async *async;
David Schleefed9eccb2008-11-04 20:29:31 -08001718 int n, m, count = 0, retval = 0;
1719 DECLARE_WAITQUEUE(wait, current);
1720 const unsigned minor = iminor(file->f_dentry->d_inode);
Bernd Porr3ffab422011-11-08 21:23:03 +00001721 struct comedi_device_file_info *dev_file_info;
1722 struct comedi_device *dev;
1723 dev_file_info = comedi_get_device_file_info(minor);
1724
1725 if (dev_file_info == NULL)
Florian Schmaus70fe7422011-12-14 14:23:24 +01001726 return -ENODEV;
Bernd Porr3ffab422011-11-08 21:23:03 +00001727 dev = dev_file_info->device;
1728 if (dev == NULL)
Florian Schmaus70fe7422011-12-14 14:23:24 +01001729 return -ENODEV;
David Schleefed9eccb2008-11-04 20:29:31 -08001730
1731 if (!dev->attached) {
1732 DPRINTK("no driver configured on comedi%i\n", dev->minor);
1733 retval = -ENODEV;
1734 goto done;
1735 }
1736
1737 s = comedi_get_read_subdevice(dev_file_info);
1738 if (s == NULL) {
1739 retval = -EIO;
1740 goto done;
1741 }
1742 async = s->async;
1743 if (!nbytes) {
1744 retval = 0;
1745 goto done;
1746 }
1747 if (!s->busy) {
1748 retval = 0;
1749 goto done;
1750 }
1751 if (s->busy != file) {
1752 retval = -EACCES;
1753 goto done;
1754 }
1755
1756 add_wait_queue(&async->wait_head, &wait);
1757 while (nbytes > 0 && !retval) {
1758 set_current_state(TASK_INTERRUPTIBLE);
1759
1760 n = nbytes;
1761
1762 m = comedi_buf_read_n_available(async);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001763 /* printk("%d available\n",m); */
1764 if (async->buf_read_ptr + m > async->prealloc_bufsz)
David Schleefed9eccb2008-11-04 20:29:31 -08001765 m = async->prealloc_bufsz - async->buf_read_ptr;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001766 /* printk("%d contiguous\n",m); */
David Schleefed9eccb2008-11-04 20:29:31 -08001767 if (m < n)
1768 n = m;
1769
1770 if (n == 0) {
1771 if (!(comedi_get_subdevice_runflags(s) & SRF_RUNNING)) {
1772 do_become_nonbusy(dev, s);
1773 if (comedi_get_subdevice_runflags(s) &
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001774 SRF_ERROR) {
David Schleefed9eccb2008-11-04 20:29:31 -08001775 retval = -EPIPE;
1776 } else {
1777 retval = 0;
1778 }
1779 break;
1780 }
1781 if (file->f_flags & O_NONBLOCK) {
1782 retval = -EAGAIN;
1783 break;
1784 }
Federico Vaga6a9ce6b2011-10-29 09:47:39 +02001785 schedule();
David Schleefed9eccb2008-11-04 20:29:31 -08001786 if (signal_pending(current)) {
1787 retval = -ERESTARTSYS;
1788 break;
1789 }
David Schleefed9eccb2008-11-04 20:29:31 -08001790 if (!s->busy) {
1791 retval = 0;
1792 break;
1793 }
1794 if (s->busy != file) {
1795 retval = -EACCES;
1796 break;
1797 }
1798 continue;
1799 }
1800 m = copy_to_user(buf, async->prealloc_buf +
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001801 async->buf_read_ptr, n);
David Schleefed9eccb2008-11-04 20:29:31 -08001802 if (m) {
1803 n -= m;
1804 retval = -EFAULT;
1805 }
1806
1807 comedi_buf_read_alloc(async, n);
1808 comedi_buf_read_free(async, n);
1809
1810 count += n;
1811 nbytes -= n;
1812
1813 buf += n;
1814 break; /* makes device work like a pipe */
1815 }
1816 if (!(comedi_get_subdevice_runflags(s) & (SRF_ERROR | SRF_RUNNING)) &&
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001817 async->buf_read_count - async->buf_write_count == 0) {
David Schleefed9eccb2008-11-04 20:29:31 -08001818 do_become_nonbusy(dev, s);
1819 }
1820 set_current_state(TASK_RUNNING);
1821 remove_wait_queue(&async->wait_head, &wait);
1822
1823done:
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001824 return count ? count : retval;
David Schleefed9eccb2008-11-04 20:29:31 -08001825}
1826
1827/*
1828 This function restores a subdevice to an idle state.
1829 */
Bill Pemberton34c43922009-03-16 22:05:14 -04001830void do_become_nonbusy(struct comedi_device *dev, struct comedi_subdevice *s)
David Schleefed9eccb2008-11-04 20:29:31 -08001831{
Bill Pembertond1636792009-03-16 22:05:20 -04001832 struct comedi_async *async = s->async;
David Schleefed9eccb2008-11-04 20:29:31 -08001833
1834 comedi_set_subdevice_runflags(s, SRF_RUNNING, 0);
David Schleefed9eccb2008-11-04 20:29:31 -08001835 if (async) {
1836 comedi_reset_async_buf(async);
1837 async->inttrig = NULL;
Ian Abbottcc088792012-09-19 19:37:39 +01001838 kfree(async->cmd.chanlist);
1839 async->cmd.chanlist = NULL;
David Schleefed9eccb2008-11-04 20:29:31 -08001840 } else {
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001841 printk(KERN_ERR
1842 "BUG: (?) do_become_nonbusy called with async=0\n");
David Schleefed9eccb2008-11-04 20:29:31 -08001843 }
1844
1845 s->busy = NULL;
1846}
1847
1848static int comedi_open(struct inode *inode, struct file *file)
1849{
David Schleefed9eccb2008-11-04 20:29:31 -08001850 const unsigned minor = iminor(inode);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001851 struct comedi_device_file_info *dev_file_info =
1852 comedi_get_device_file_info(minor);
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301853 struct comedi_device *dev =
1854 dev_file_info ? dev_file_info->device : NULL;
Ian Abbott97920072009-02-09 16:51:38 +00001855
David Schleefed9eccb2008-11-04 20:29:31 -08001856 if (dev == NULL) {
1857 DPRINTK("invalid minor number\n");
1858 return -ENODEV;
1859 }
1860
1861 /* This is slightly hacky, but we want module autoloading
1862 * to work for root.
1863 * case: user opens device, attached -> ok
1864 * case: user opens device, unattached, in_request_module=0 -> autoload
1865 * case: user opens device, unattached, in_request_module=1 -> fail
1866 * case: root opens device, attached -> ok
1867 * case: root opens device, unattached, in_request_module=1 -> ok
1868 * (typically called from modprobe)
1869 * case: root opens device, unattached, in_request_module=0 -> autoload
1870 *
1871 * The last could be changed to "-> ok", which would deny root
1872 * autoloading.
1873 */
1874 mutex_lock(&dev->mutex);
1875 if (dev->attached)
1876 goto ok;
Eric Parisa8f80e82009-08-13 09:44:51 -04001877 if (!capable(CAP_NET_ADMIN) && dev->in_request_module) {
David Schleefed9eccb2008-11-04 20:29:31 -08001878 DPRINTK("in request module\n");
1879 mutex_unlock(&dev->mutex);
1880 return -ENODEV;
1881 }
Eric Parisa8f80e82009-08-13 09:44:51 -04001882 if (capable(CAP_NET_ADMIN) && dev->in_request_module)
David Schleefed9eccb2008-11-04 20:29:31 -08001883 goto ok;
1884
1885 dev->in_request_module = 1;
1886
David Schleefed9eccb2008-11-04 20:29:31 -08001887#ifdef CONFIG_KMOD
1888 mutex_unlock(&dev->mutex);
Ian Abbott56d92c62009-02-09 16:32:12 +00001889 request_module("char-major-%i-%i", COMEDI_MAJOR, dev->minor);
David Schleefed9eccb2008-11-04 20:29:31 -08001890 mutex_lock(&dev->mutex);
1891#endif
1892
1893 dev->in_request_module = 0;
1894
Eric Parisa8f80e82009-08-13 09:44:51 -04001895 if (!dev->attached && !capable(CAP_NET_ADMIN)) {
1896 DPRINTK("not attached and not CAP_NET_ADMIN\n");
David Schleefed9eccb2008-11-04 20:29:31 -08001897 mutex_unlock(&dev->mutex);
1898 return -ENODEV;
1899 }
1900ok:
1901 __module_get(THIS_MODULE);
1902
1903 if (dev->attached) {
1904 if (!try_module_get(dev->driver->module)) {
1905 module_put(THIS_MODULE);
1906 mutex_unlock(&dev->mutex);
1907 return -ENOSYS;
1908 }
1909 }
1910
Ian Abbott3c17ba072010-05-19 14:10:00 +01001911 if (dev->attached && dev->use_count == 0 && dev->open) {
1912 int rc = dev->open(dev);
1913 if (rc < 0) {
1914 module_put(dev->driver->module);
1915 module_put(THIS_MODULE);
1916 mutex_unlock(&dev->mutex);
1917 return rc;
1918 }
1919 }
David Schleefed9eccb2008-11-04 20:29:31 -08001920
1921 dev->use_count++;
1922
1923 mutex_unlock(&dev->mutex);
1924
1925 return 0;
1926}
1927
1928static int comedi_close(struct inode *inode, struct file *file)
1929{
1930 const unsigned minor = iminor(inode);
Bill Pemberton34c43922009-03-16 22:05:14 -04001931 struct comedi_subdevice *s = NULL;
David Schleefed9eccb2008-11-04 20:29:31 -08001932 int i;
Bernd Porr3ffab422011-11-08 21:23:03 +00001933 struct comedi_device_file_info *dev_file_info;
1934 struct comedi_device *dev;
1935 dev_file_info = comedi_get_device_file_info(minor);
1936
1937 if (dev_file_info == NULL)
Florian Schmaus70fe7422011-12-14 14:23:24 +01001938 return -ENODEV;
Bernd Porr3ffab422011-11-08 21:23:03 +00001939 dev = dev_file_info->device;
1940 if (dev == NULL)
Florian Schmaus70fe7422011-12-14 14:23:24 +01001941 return -ENODEV;
David Schleefed9eccb2008-11-04 20:29:31 -08001942
1943 mutex_lock(&dev->mutex);
1944
1945 if (dev->subdevices) {
1946 for (i = 0; i < dev->n_subdevices; i++) {
1947 s = dev->subdevices + i;
1948
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001949 if (s->busy == file)
David Schleefed9eccb2008-11-04 20:29:31 -08001950 do_cancel(dev, s);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001951 if (s->lock == file)
David Schleefed9eccb2008-11-04 20:29:31 -08001952 s->lock = NULL;
David Schleefed9eccb2008-11-04 20:29:31 -08001953 }
1954 }
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001955 if (dev->attached && dev->use_count == 1 && dev->close)
David Schleefed9eccb2008-11-04 20:29:31 -08001956 dev->close(dev);
David Schleefed9eccb2008-11-04 20:29:31 -08001957
1958 module_put(THIS_MODULE);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001959 if (dev->attached)
David Schleefed9eccb2008-11-04 20:29:31 -08001960 module_put(dev->driver->module);
David Schleefed9eccb2008-11-04 20:29:31 -08001961
1962 dev->use_count--;
1963
1964 mutex_unlock(&dev->mutex);
1965
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001966 if (file->f_flags & FASYNC)
David Schleefed9eccb2008-11-04 20:29:31 -08001967 comedi_fasync(-1, file, 0);
David Schleefed9eccb2008-11-04 20:29:31 -08001968
1969 return 0;
1970}
1971
1972static int comedi_fasync(int fd, struct file *file, int on)
1973{
1974 const unsigned minor = iminor(file->f_dentry->d_inode);
Bernd Porr3ffab422011-11-08 21:23:03 +00001975 struct comedi_device_file_info *dev_file_info;
1976 struct comedi_device *dev;
1977 dev_file_info = comedi_get_device_file_info(minor);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001978
Bernd Porr3ffab422011-11-08 21:23:03 +00001979 if (dev_file_info == NULL)
Florian Schmaus70fe7422011-12-14 14:23:24 +01001980 return -ENODEV;
Bernd Porr3ffab422011-11-08 21:23:03 +00001981 dev = dev_file_info->device;
1982 if (dev == NULL)
Florian Schmaus70fe7422011-12-14 14:23:24 +01001983 return -ENODEV;
David Schleefed9eccb2008-11-04 20:29:31 -08001984
1985 return fasync_helper(fd, file, on, &dev->async_queue);
1986}
1987
1988const struct file_operations comedi_fops = {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301989 .owner = THIS_MODULE,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301990 .unlocked_ioctl = comedi_unlocked_ioctl,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301991 .compat_ioctl = comedi_compat_ioctl,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301992 .open = comedi_open,
1993 .release = comedi_close,
1994 .read = comedi_read,
1995 .write = comedi_write,
1996 .mmap = comedi_mmap,
1997 .poll = comedi_poll,
1998 .fasync = comedi_fasync,
Arnd Bergmann6038f372010-08-15 18:52:59 +02001999 .llseek = noop_llseek,
David Schleefed9eccb2008-11-04 20:29:31 -08002000};
2001
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002002struct class *comedi_class;
David Schleefed9eccb2008-11-04 20:29:31 -08002003static struct cdev comedi_cdev;
2004
2005static void comedi_cleanup_legacy_minors(void)
2006{
2007 unsigned i;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002008
Bernd Porr1dd33ab2008-12-08 23:30:13 +00002009 for (i = 0; i < comedi_num_legacy_minors; i++)
David Schleefed9eccb2008-11-04 20:29:31 -08002010 comedi_free_board_minor(i);
David Schleefed9eccb2008-11-04 20:29:31 -08002011}
2012
2013static int __init comedi_init(void)
2014{
2015 int i;
2016 int retval;
2017
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002018 printk(KERN_INFO "comedi: version " COMEDI_RELEASE
2019 " - http://www.comedi.org\n");
David Schleefed9eccb2008-11-04 20:29:31 -08002020
Frank Mori Hessa3cb7292008-12-15 13:44:45 +00002021 if (comedi_num_legacy_minors < 0 ||
2022 comedi_num_legacy_minors > COMEDI_NUM_BOARD_MINORS) {
2023 printk(KERN_ERR "comedi: error: invalid value for module "
2024 "parameter \"comedi_num_legacy_minors\". Valid values "
2025 "are 0 through %i.\n", COMEDI_NUM_BOARD_MINORS);
2026 return -EINVAL;
2027 }
2028
2029 /*
2030 * comedi is unusable if both comedi_autoconfig and
2031 * comedi_num_legacy_minors are zero, so we might as well adjust the
2032 * defaults in that case
2033 */
2034 if (comedi_autoconfig == 0 && comedi_num_legacy_minors == 0)
2035 comedi_num_legacy_minors = 16;
2036
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002037 memset(comedi_file_info_table, 0,
2038 sizeof(struct comedi_device_file_info *) * COMEDI_NUM_MINORS);
David Schleefed9eccb2008-11-04 20:29:31 -08002039
2040 retval = register_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002041 COMEDI_NUM_MINORS, "comedi");
David Schleefed9eccb2008-11-04 20:29:31 -08002042 if (retval)
2043 return -EIO;
2044 cdev_init(&comedi_cdev, &comedi_fops);
2045 comedi_cdev.owner = THIS_MODULE;
2046 kobject_set_name(&comedi_cdev.kobj, "comedi");
2047 if (cdev_add(&comedi_cdev, MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS)) {
2048 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002049 COMEDI_NUM_MINORS);
David Schleefed9eccb2008-11-04 20:29:31 -08002050 return -EIO;
2051 }
2052 comedi_class = class_create(THIS_MODULE, "comedi");
2053 if (IS_ERR(comedi_class)) {
Mark Rankilor3fffdf22010-04-29 18:17:16 +08002054 printk(KERN_ERR "comedi: failed to create class");
David Schleefed9eccb2008-11-04 20:29:31 -08002055 cdev_del(&comedi_cdev);
2056 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002057 COMEDI_NUM_MINORS);
David Schleefed9eccb2008-11-04 20:29:31 -08002058 return PTR_ERR(comedi_class);
2059 }
2060
2061 /* XXX requires /proc interface */
2062 comedi_proc_init();
2063
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002064 /* create devices files for legacy/manual use */
Bernd Porr1dd33ab2008-12-08 23:30:13 +00002065 for (i = 0; i < comedi_num_legacy_minors; i++) {
David Schleefed9eccb2008-11-04 20:29:31 -08002066 int minor;
2067 minor = comedi_alloc_board_minor(NULL);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002068 if (minor < 0) {
David Schleefed9eccb2008-11-04 20:29:31 -08002069 comedi_cleanup_legacy_minors();
2070 cdev_del(&comedi_cdev);
2071 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002072 COMEDI_NUM_MINORS);
David Schleefed9eccb2008-11-04 20:29:31 -08002073 return minor;
2074 }
2075 }
2076
David Schleefed9eccb2008-11-04 20:29:31 -08002077 return 0;
2078}
2079
2080static void __exit comedi_cleanup(void)
2081{
2082 int i;
2083
2084 comedi_cleanup_legacy_minors();
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002085 for (i = 0; i < COMEDI_NUM_MINORS; ++i)
David Schleefed9eccb2008-11-04 20:29:31 -08002086 BUG_ON(comedi_file_info_table[i]);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002087
David Schleefed9eccb2008-11-04 20:29:31 -08002088 class_destroy(comedi_class);
2089 cdev_del(&comedi_cdev);
2090 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS);
2091
2092 comedi_proc_cleanup();
David Schleefed9eccb2008-11-04 20:29:31 -08002093}
2094
2095module_init(comedi_init);
2096module_exit(comedi_cleanup);
2097
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04002098void comedi_error(const struct comedi_device *dev, const char *s)
David Schleefed9eccb2008-11-04 20:29:31 -08002099{
Mark Rankilor3fffdf22010-04-29 18:17:16 +08002100 printk(KERN_ERR "comedi%d: %s: %s\n", dev->minor,
2101 dev->driver->driver_name, s);
David Schleefed9eccb2008-11-04 20:29:31 -08002102}
Greg Kroah-Hartman18736432010-05-01 12:02:23 -07002103EXPORT_SYMBOL(comedi_error);
David Schleefed9eccb2008-11-04 20:29:31 -08002104
Bill Pemberton34c43922009-03-16 22:05:14 -04002105void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s)
David Schleefed9eccb2008-11-04 20:29:31 -08002106{
Bill Pembertond1636792009-03-16 22:05:20 -04002107 struct comedi_async *async = s->async;
David Schleefed9eccb2008-11-04 20:29:31 -08002108 unsigned runflags = 0;
2109 unsigned runflags_mask = 0;
2110
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002111 /* DPRINTK("comedi_event 0x%x\n",mask); */
David Schleefed9eccb2008-11-04 20:29:31 -08002112
2113 if ((comedi_get_subdevice_runflags(s) & SRF_RUNNING) == 0)
2114 return;
2115
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302116 if (s->
2117 async->events & (COMEDI_CB_EOA | COMEDI_CB_ERROR |
2118 COMEDI_CB_OVERFLOW)) {
David Schleefed9eccb2008-11-04 20:29:31 -08002119 runflags_mask |= SRF_RUNNING;
2120 }
Lucas De Marchi25985ed2011-03-30 22:57:33 -03002121 /* remember if an error event has occurred, so an error
David Schleefed9eccb2008-11-04 20:29:31 -08002122 * can be returned the next time the user does a read() */
2123 if (s->async->events & (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)) {
2124 runflags_mask |= SRF_ERROR;
2125 runflags |= SRF_ERROR;
2126 }
2127 if (runflags_mask) {
2128 /*sets SRF_ERROR and SRF_RUNNING together atomically */
2129 comedi_set_subdevice_runflags(s, runflags_mask, runflags);
2130 }
2131
2132 if (async->cb_mask & s->async->events) {
2133 if (comedi_get_subdevice_runflags(s) & SRF_USER) {
Greg Kroah-Hartmanfcea1152009-04-27 15:15:30 -07002134 wake_up_interruptible(&async->wait_head);
Andrea Gelmini6705b682010-02-26 10:14:55 +01002135 if (s->subdev_flags & SDF_CMD_READ)
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302136 kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
Andrea Gelmini6705b682010-02-26 10:14:55 +01002137 if (s->subdev_flags & SDF_CMD_WRITE)
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302138 kill_fasync(&dev->async_queue, SIGIO, POLL_OUT);
David Schleefed9eccb2008-11-04 20:29:31 -08002139 } else {
2140 if (async->cb_func)
2141 async->cb_func(s->async->events, async->cb_arg);
David Schleefed9eccb2008-11-04 20:29:31 -08002142 }
2143 }
2144 s->async->events = 0;
2145}
Greg Kroah-Hartman18736432010-05-01 12:02:23 -07002146EXPORT_SYMBOL(comedi_event);
David Schleefed9eccb2008-11-04 20:29:31 -08002147
Bill Pemberton34c43922009-03-16 22:05:14 -04002148unsigned comedi_get_subdevice_runflags(struct comedi_subdevice *s)
David Schleefed9eccb2008-11-04 20:29:31 -08002149{
2150 unsigned long flags;
2151 unsigned runflags;
2152
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002153 spin_lock_irqsave(&s->spin_lock, flags);
David Schleefed9eccb2008-11-04 20:29:31 -08002154 runflags = s->runflags;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002155 spin_unlock_irqrestore(&s->spin_lock, flags);
David Schleefed9eccb2008-11-04 20:29:31 -08002156 return runflags;
2157}
Greg Kroah-Hartman18736432010-05-01 12:02:23 -07002158EXPORT_SYMBOL(comedi_get_subdevice_runflags);
David Schleefed9eccb2008-11-04 20:29:31 -08002159
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04002160static int is_device_busy(struct comedi_device *dev)
David Schleefed9eccb2008-11-04 20:29:31 -08002161{
Bill Pemberton34c43922009-03-16 22:05:14 -04002162 struct comedi_subdevice *s;
David Schleefed9eccb2008-11-04 20:29:31 -08002163 int i;
2164
2165 if (!dev->attached)
2166 return 0;
2167
2168 for (i = 0; i < dev->n_subdevices; i++) {
2169 s = dev->subdevices + i;
2170 if (s->busy)
2171 return 1;
2172 if (s->async && s->async->mmap_count)
2173 return 1;
2174 }
2175
2176 return 0;
2177}
2178
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -07002179static void comedi_device_init(struct comedi_device *dev)
David Schleefed9eccb2008-11-04 20:29:31 -08002180{
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04002181 memset(dev, 0, sizeof(struct comedi_device));
David Schleefed9eccb2008-11-04 20:29:31 -08002182 spin_lock_init(&dev->spinlock);
2183 mutex_init(&dev->mutex);
2184 dev->minor = -1;
2185}
2186
Greg Kroah-Hartman92d01272010-05-03 16:32:28 -07002187static void comedi_device_cleanup(struct comedi_device *dev)
David Schleefed9eccb2008-11-04 20:29:31 -08002188{
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002189 if (dev == NULL)
2190 return;
David Schleefed9eccb2008-11-04 20:29:31 -08002191 mutex_lock(&dev->mutex);
2192 comedi_device_detach(dev);
2193 mutex_unlock(&dev->mutex);
2194 mutex_destroy(&dev->mutex);
2195}
2196
2197int comedi_alloc_board_minor(struct device *hardware_device)
2198{
2199 unsigned long flags;
2200 struct comedi_device_file_info *info;
Bill Pemberton0bfbbe82009-03-16 22:05:36 -04002201 struct device *csdev;
David Schleefed9eccb2008-11-04 20:29:31 -08002202 unsigned i;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002203 int retval;
David Schleefed9eccb2008-11-04 20:29:31 -08002204
2205 info = kzalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002206 if (info == NULL)
2207 return -ENOMEM;
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04002208 info->device = kzalloc(sizeof(struct comedi_device), GFP_KERNEL);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002209 if (info->device == NULL) {
David Schleefed9eccb2008-11-04 20:29:31 -08002210 kfree(info);
2211 return -ENOMEM;
2212 }
2213 comedi_device_init(info->device);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002214 spin_lock_irqsave(&comedi_file_info_table_lock, flags);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002215 for (i = 0; i < COMEDI_NUM_BOARD_MINORS; ++i) {
2216 if (comedi_file_info_table[i] == NULL) {
David Schleefed9eccb2008-11-04 20:29:31 -08002217 comedi_file_info_table[i] = info;
2218 break;
2219 }
2220 }
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002221 spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002222 if (i == COMEDI_NUM_BOARD_MINORS) {
David Schleefed9eccb2008-11-04 20:29:31 -08002223 comedi_device_cleanup(info->device);
2224 kfree(info->device);
2225 kfree(info);
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302226 printk(KERN_ERR
Mark21fe2ee2010-05-13 17:44:39 +08002227 "comedi: error: "
2228 "ran out of minor numbers for board device files.\n");
David Schleefed9eccb2008-11-04 20:29:31 -08002229 return -EBUSY;
2230 }
2231 info->device->minor = i;
Pavel Roskin0435f932011-07-06 10:15:44 -04002232 csdev = device_create(comedi_class, hardware_device,
2233 MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i", i);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002234 if (!IS_ERR(csdev))
David Schleefed9eccb2008-11-04 20:29:31 -08002235 info->device->class_dev = csdev;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002236 dev_set_drvdata(csdev, info);
2237 retval = device_create_file(csdev, &dev_attr_max_read_buffer_kb);
2238 if (retval) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302239 printk(KERN_ERR
Mark21fe2ee2010-05-13 17:44:39 +08002240 "comedi: "
2241 "failed to create sysfs attribute file \"%s\".\n",
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302242 dev_attr_max_read_buffer_kb.attr.name);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002243 comedi_free_board_minor(i);
2244 return retval;
2245 }
2246 retval = device_create_file(csdev, &dev_attr_read_buffer_kb);
2247 if (retval) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302248 printk(KERN_ERR
Mark21fe2ee2010-05-13 17:44:39 +08002249 "comedi: "
2250 "failed to create sysfs attribute file \"%s\".\n",
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302251 dev_attr_read_buffer_kb.attr.name);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002252 comedi_free_board_minor(i);
2253 return retval;
2254 }
2255 retval = device_create_file(csdev, &dev_attr_max_write_buffer_kb);
2256 if (retval) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302257 printk(KERN_ERR
Mark21fe2ee2010-05-13 17:44:39 +08002258 "comedi: "
2259 "failed to create sysfs attribute file \"%s\".\n",
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302260 dev_attr_max_write_buffer_kb.attr.name);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002261 comedi_free_board_minor(i);
2262 return retval;
2263 }
2264 retval = device_create_file(csdev, &dev_attr_write_buffer_kb);
2265 if (retval) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302266 printk(KERN_ERR
Mark21fe2ee2010-05-13 17:44:39 +08002267 "comedi: "
2268 "failed to create sysfs attribute file \"%s\".\n",
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302269 dev_attr_write_buffer_kb.attr.name);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002270 comedi_free_board_minor(i);
2271 return retval;
2272 }
David Schleefed9eccb2008-11-04 20:29:31 -08002273 return i;
2274}
2275
2276void comedi_free_board_minor(unsigned minor)
2277{
2278 unsigned long flags;
2279 struct comedi_device_file_info *info;
2280
2281 BUG_ON(minor >= COMEDI_NUM_BOARD_MINORS);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002282 spin_lock_irqsave(&comedi_file_info_table_lock, flags);
David Schleefed9eccb2008-11-04 20:29:31 -08002283 info = comedi_file_info_table[minor];
2284 comedi_file_info_table[minor] = NULL;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002285 spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
David Schleefed9eccb2008-11-04 20:29:31 -08002286
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002287 if (info) {
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04002288 struct comedi_device *dev = info->device;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002289 if (dev) {
2290 if (dev->class_dev) {
2291 device_destroy(comedi_class,
2292 MKDEV(COMEDI_MAJOR, dev->minor));
David Schleefed9eccb2008-11-04 20:29:31 -08002293 }
2294 comedi_device_cleanup(dev);
2295 kfree(dev);
2296 }
2297 kfree(info);
2298 }
2299}
2300
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002301int comedi_alloc_subdevice_minor(struct comedi_device *dev,
2302 struct comedi_subdevice *s)
David Schleefed9eccb2008-11-04 20:29:31 -08002303{
2304 unsigned long flags;
2305 struct comedi_device_file_info *info;
Bill Pemberton0bfbbe82009-03-16 22:05:36 -04002306 struct device *csdev;
David Schleefed9eccb2008-11-04 20:29:31 -08002307 unsigned i;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002308 int retval;
David Schleefed9eccb2008-11-04 20:29:31 -08002309
2310 info = kmalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002311 if (info == NULL)
2312 return -ENOMEM;
David Schleefed9eccb2008-11-04 20:29:31 -08002313 info->device = dev;
2314 info->read_subdevice = s;
2315 info->write_subdevice = s;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002316 spin_lock_irqsave(&comedi_file_info_table_lock, flags);
Frank Mori Hess4c41f3a2008-12-09 14:47:22 +00002317 for (i = COMEDI_FIRST_SUBDEVICE_MINOR; i < COMEDI_NUM_MINORS; ++i) {
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002318 if (comedi_file_info_table[i] == NULL) {
David Schleefed9eccb2008-11-04 20:29:31 -08002319 comedi_file_info_table[i] = info;
2320 break;
2321 }
2322 }
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002323 spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002324 if (i == COMEDI_NUM_MINORS) {
David Schleefed9eccb2008-11-04 20:29:31 -08002325 kfree(info);
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302326 printk(KERN_ERR
Mark21fe2ee2010-05-13 17:44:39 +08002327 "comedi: error: "
2328 "ran out of minor numbers for board device files.\n");
David Schleefed9eccb2008-11-04 20:29:31 -08002329 return -EBUSY;
2330 }
2331 s->minor = i;
Pavel Roskin0435f932011-07-06 10:15:44 -04002332 csdev = device_create(comedi_class, dev->class_dev,
2333 MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i_subd%i",
2334 dev->minor, (int)(s - dev->subdevices));
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002335 if (!IS_ERR(csdev))
David Schleefed9eccb2008-11-04 20:29:31 -08002336 s->class_dev = csdev;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002337 dev_set_drvdata(csdev, info);
2338 retval = device_create_file(csdev, &dev_attr_max_read_buffer_kb);
2339 if (retval) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302340 printk(KERN_ERR
Mark21fe2ee2010-05-13 17:44:39 +08002341 "comedi: "
2342 "failed to create sysfs attribute file \"%s\".\n",
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302343 dev_attr_max_read_buffer_kb.attr.name);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002344 comedi_free_subdevice_minor(s);
2345 return retval;
2346 }
2347 retval = device_create_file(csdev, &dev_attr_read_buffer_kb);
2348 if (retval) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302349 printk(KERN_ERR
Mark21fe2ee2010-05-13 17:44:39 +08002350 "comedi: "
2351 "failed to create sysfs attribute file \"%s\".\n",
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302352 dev_attr_read_buffer_kb.attr.name);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002353 comedi_free_subdevice_minor(s);
2354 return retval;
2355 }
2356 retval = device_create_file(csdev, &dev_attr_max_write_buffer_kb);
2357 if (retval) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302358 printk(KERN_ERR
Mark21fe2ee2010-05-13 17:44:39 +08002359 "comedi: "
2360 "failed to create sysfs attribute file \"%s\".\n",
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302361 dev_attr_max_write_buffer_kb.attr.name);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002362 comedi_free_subdevice_minor(s);
2363 return retval;
2364 }
2365 retval = device_create_file(csdev, &dev_attr_write_buffer_kb);
2366 if (retval) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302367 printk(KERN_ERR
Mark21fe2ee2010-05-13 17:44:39 +08002368 "comedi: "
2369 "failed to create sysfs attribute file \"%s\".\n",
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302370 dev_attr_write_buffer_kb.attr.name);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002371 comedi_free_subdevice_minor(s);
2372 return retval;
2373 }
David Schleefed9eccb2008-11-04 20:29:31 -08002374 return i;
2375}
2376
Bill Pemberton34c43922009-03-16 22:05:14 -04002377void comedi_free_subdevice_minor(struct comedi_subdevice *s)
David Schleefed9eccb2008-11-04 20:29:31 -08002378{
2379 unsigned long flags;
2380 struct comedi_device_file_info *info;
2381
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002382 if (s == NULL)
2383 return;
2384 if (s->minor < 0)
2385 return;
David Schleefed9eccb2008-11-04 20:29:31 -08002386
2387 BUG_ON(s->minor >= COMEDI_NUM_MINORS);
2388 BUG_ON(s->minor < COMEDI_FIRST_SUBDEVICE_MINOR);
2389
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002390 spin_lock_irqsave(&comedi_file_info_table_lock, flags);
David Schleefed9eccb2008-11-04 20:29:31 -08002391 info = comedi_file_info_table[s->minor];
2392 comedi_file_info_table[s->minor] = NULL;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002393 spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
David Schleefed9eccb2008-11-04 20:29:31 -08002394
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002395 if (s->class_dev) {
David Schleefed9eccb2008-11-04 20:29:31 -08002396 device_destroy(comedi_class, MKDEV(COMEDI_MAJOR, s->minor));
2397 s->class_dev = NULL;
2398 }
2399 kfree(info);
2400}
2401
2402struct comedi_device_file_info *comedi_get_device_file_info(unsigned minor)
2403{
2404 unsigned long flags;
2405 struct comedi_device_file_info *info;
2406
2407 BUG_ON(minor >= COMEDI_NUM_MINORS);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002408 spin_lock_irqsave(&comedi_file_info_table_lock, flags);
David Schleefed9eccb2008-11-04 20:29:31 -08002409 info = comedi_file_info_table[minor];
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002410 spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
David Schleefed9eccb2008-11-04 20:29:31 -08002411 return info;
2412}
Greg Kroah-Hartman18736432010-05-01 12:02:23 -07002413EXPORT_SYMBOL_GPL(comedi_get_device_file_info);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002414
2415static int resize_async_buffer(struct comedi_device *dev,
2416 struct comedi_subdevice *s,
2417 struct comedi_async *async, unsigned new_size)
2418{
2419 int retval;
2420
2421 if (new_size > async->max_bufsize)
2422 return -EPERM;
2423
2424 if (s->busy) {
2425 DPRINTK("subdevice is busy, cannot resize buffer\n");
2426 return -EBUSY;
2427 }
2428 if (async->mmap_count) {
2429 DPRINTK("subdevice is mmapped, cannot resize buffer\n");
2430 return -EBUSY;
2431 }
2432
2433 if (!async->prealloc_buf)
2434 return -EINVAL;
2435
2436 /* make sure buffer is an integral number of pages
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302437 * (we round up) */
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002438 new_size = (new_size + PAGE_SIZE - 1) & PAGE_MASK;
2439
2440 retval = comedi_buf_alloc(dev, s, new_size);
2441 if (retval < 0)
2442 return retval;
2443
2444 if (s->buf_change) {
2445 retval = s->buf_change(dev, s, new_size);
2446 if (retval < 0)
2447 return retval;
2448 }
2449
2450 DPRINTK("comedi%i subd %d buffer resized to %i bytes\n",
Ian Abbottb8b5cd92009-09-21 14:55:23 -04002451 dev->minor, (int)(s - dev->subdevices), async->prealloc_bufsz);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002452 return 0;
2453}
2454
2455/* sysfs attribute files */
2456
2457static const unsigned bytes_per_kibi = 1024;
2458
2459static ssize_t show_max_read_buffer_kb(struct device *dev,
2460 struct device_attribute *attr, char *buf)
2461{
2462 ssize_t retval;
2463 struct comedi_device_file_info *info = dev_get_drvdata(dev);
2464 unsigned max_buffer_size_kb = 0;
2465 struct comedi_subdevice *const read_subdevice =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302466 comedi_get_read_subdevice(info);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002467
2468 mutex_lock(&info->device->mutex);
2469 if (read_subdevice &&
2470 (read_subdevice->subdev_flags & SDF_CMD_READ) &&
2471 read_subdevice->async) {
2472 max_buffer_size_kb = read_subdevice->async->max_bufsize /
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302473 bytes_per_kibi;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002474 }
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302475 retval = snprintf(buf, PAGE_SIZE, "%i\n", max_buffer_size_kb);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002476 mutex_unlock(&info->device->mutex);
2477
2478 return retval;
2479}
2480
2481static ssize_t store_max_read_buffer_kb(struct device *dev,
2482 struct device_attribute *attr,
2483 const char *buf, size_t count)
2484{
2485 struct comedi_device_file_info *info = dev_get_drvdata(dev);
Florian Schmaus2e1c3942011-12-08 12:12:44 +01002486 unsigned int new_max_size_kb;
Florian Schmausc5018162011-12-08 12:12:45 +01002487 unsigned int new_max_size;
Florian Schmaus2e1c3942011-12-08 12:12:44 +01002488 int ret;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002489 struct comedi_subdevice *const read_subdevice =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302490 comedi_get_read_subdevice(info);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002491
Florian Schmaus2e1c3942011-12-08 12:12:44 +01002492 ret = kstrtouint(buf, 10, &new_max_size_kb);
2493 if (ret)
2494 return ret;
Florian Schmausc5018162011-12-08 12:12:45 +01002495 if (new_max_size_kb > (UINT_MAX / bytes_per_kibi))
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002496 return -EINVAL;
Florian Schmausc5018162011-12-08 12:12:45 +01002497 new_max_size = new_max_size_kb * bytes_per_kibi;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002498
2499 mutex_lock(&info->device->mutex);
2500 if (read_subdevice == NULL ||
2501 (read_subdevice->subdev_flags & SDF_CMD_READ) == 0 ||
2502 read_subdevice->async == NULL) {
2503 mutex_unlock(&info->device->mutex);
2504 return -EINVAL;
2505 }
2506 read_subdevice->async->max_bufsize = new_max_size;
2507 mutex_unlock(&info->device->mutex);
2508
2509 return count;
2510}
2511
2512static struct device_attribute dev_attr_max_read_buffer_kb = {
2513 .attr = {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302514 .name = "max_read_buffer_kb",
2515 .mode = S_IRUGO | S_IWUSR},
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002516 .show = &show_max_read_buffer_kb,
2517 .store = &store_max_read_buffer_kb
2518};
2519
2520static ssize_t show_read_buffer_kb(struct device *dev,
2521 struct device_attribute *attr, char *buf)
2522{
2523 ssize_t retval;
2524 struct comedi_device_file_info *info = dev_get_drvdata(dev);
2525 unsigned buffer_size_kb = 0;
2526 struct comedi_subdevice *const read_subdevice =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302527 comedi_get_read_subdevice(info);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002528
2529 mutex_lock(&info->device->mutex);
2530 if (read_subdevice &&
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302531 (read_subdevice->subdev_flags & SDF_CMD_READ) &&
2532 read_subdevice->async) {
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002533 buffer_size_kb = read_subdevice->async->prealloc_bufsz /
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302534 bytes_per_kibi;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002535 }
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302536 retval = snprintf(buf, PAGE_SIZE, "%i\n", buffer_size_kb);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002537 mutex_unlock(&info->device->mutex);
2538
2539 return retval;
2540}
2541
2542static ssize_t store_read_buffer_kb(struct device *dev,
2543 struct device_attribute *attr,
2544 const char *buf, size_t count)
2545{
2546 struct comedi_device_file_info *info = dev_get_drvdata(dev);
Florian Schmaus2e1c3942011-12-08 12:12:44 +01002547 unsigned int new_size_kb;
Florian Schmausc5018162011-12-08 12:12:45 +01002548 unsigned int new_size;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002549 int retval;
Florian Schmaus2e1c3942011-12-08 12:12:44 +01002550 int ret;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002551 struct comedi_subdevice *const read_subdevice =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302552 comedi_get_read_subdevice(info);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002553
Florian Schmaus2e1c3942011-12-08 12:12:44 +01002554 ret = kstrtouint(buf, 10, &new_size_kb);
2555 if (ret)
2556 return ret;
Florian Schmausc5018162011-12-08 12:12:45 +01002557 if (new_size_kb > (UINT_MAX / bytes_per_kibi))
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002558 return -EINVAL;
Florian Schmausc5018162011-12-08 12:12:45 +01002559 new_size = new_size_kb * bytes_per_kibi;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002560
2561 mutex_lock(&info->device->mutex);
2562 if (read_subdevice == NULL ||
2563 (read_subdevice->subdev_flags & SDF_CMD_READ) == 0 ||
2564 read_subdevice->async == NULL) {
2565 mutex_unlock(&info->device->mutex);
2566 return -EINVAL;
2567 }
2568 retval = resize_async_buffer(info->device, read_subdevice,
2569 read_subdevice->async, new_size);
2570 mutex_unlock(&info->device->mutex);
2571
2572 if (retval < 0)
2573 return retval;
2574 return count;
2575}
2576
2577static struct device_attribute dev_attr_read_buffer_kb = {
2578 .attr = {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302579 .name = "read_buffer_kb",
2580 .mode = S_IRUGO | S_IWUSR | S_IWGRP},
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002581 .show = &show_read_buffer_kb,
2582 .store = &store_read_buffer_kb
2583};
2584
2585static ssize_t show_max_write_buffer_kb(struct device *dev,
2586 struct device_attribute *attr,
2587 char *buf)
2588{
2589 ssize_t retval;
2590 struct comedi_device_file_info *info = dev_get_drvdata(dev);
2591 unsigned max_buffer_size_kb = 0;
2592 struct comedi_subdevice *const write_subdevice =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302593 comedi_get_write_subdevice(info);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002594
2595 mutex_lock(&info->device->mutex);
2596 if (write_subdevice &&
2597 (write_subdevice->subdev_flags & SDF_CMD_WRITE) &&
2598 write_subdevice->async) {
2599 max_buffer_size_kb = write_subdevice->async->max_bufsize /
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302600 bytes_per_kibi;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002601 }
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302602 retval = snprintf(buf, PAGE_SIZE, "%i\n", max_buffer_size_kb);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002603 mutex_unlock(&info->device->mutex);
2604
2605 return retval;
2606}
2607
2608static ssize_t store_max_write_buffer_kb(struct device *dev,
2609 struct device_attribute *attr,
2610 const char *buf, size_t count)
2611{
2612 struct comedi_device_file_info *info = dev_get_drvdata(dev);
Florian Schmaus2e1c3942011-12-08 12:12:44 +01002613 unsigned int new_max_size_kb;
Florian Schmausc5018162011-12-08 12:12:45 +01002614 unsigned int new_max_size;
Florian Schmaus2e1c3942011-12-08 12:12:44 +01002615 int ret;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002616 struct comedi_subdevice *const write_subdevice =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302617 comedi_get_write_subdevice(info);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002618
Florian Schmaus2e1c3942011-12-08 12:12:44 +01002619 ret = kstrtouint(buf, 10, &new_max_size_kb);
2620 if (ret)
2621 return ret;
Florian Schmausc5018162011-12-08 12:12:45 +01002622 if (new_max_size_kb > (UINT_MAX / bytes_per_kibi))
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002623 return -EINVAL;
Florian Schmausc5018162011-12-08 12:12:45 +01002624 new_max_size = new_max_size_kb * bytes_per_kibi;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002625
2626 mutex_lock(&info->device->mutex);
2627 if (write_subdevice == NULL ||
2628 (write_subdevice->subdev_flags & SDF_CMD_WRITE) == 0 ||
2629 write_subdevice->async == NULL) {
2630 mutex_unlock(&info->device->mutex);
2631 return -EINVAL;
2632 }
2633 write_subdevice->async->max_bufsize = new_max_size;
2634 mutex_unlock(&info->device->mutex);
2635
2636 return count;
2637}
2638
2639static struct device_attribute dev_attr_max_write_buffer_kb = {
2640 .attr = {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302641 .name = "max_write_buffer_kb",
2642 .mode = S_IRUGO | S_IWUSR},
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002643 .show = &show_max_write_buffer_kb,
2644 .store = &store_max_write_buffer_kb
2645};
2646
2647static ssize_t show_write_buffer_kb(struct device *dev,
2648 struct device_attribute *attr, char *buf)
2649{
2650 ssize_t retval;
2651 struct comedi_device_file_info *info = dev_get_drvdata(dev);
2652 unsigned buffer_size_kb = 0;
2653 struct comedi_subdevice *const write_subdevice =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302654 comedi_get_write_subdevice(info);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002655
2656 mutex_lock(&info->device->mutex);
2657 if (write_subdevice &&
2658 (write_subdevice->subdev_flags & SDF_CMD_WRITE) &&
2659 write_subdevice->async) {
2660 buffer_size_kb = write_subdevice->async->prealloc_bufsz /
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302661 bytes_per_kibi;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002662 }
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302663 retval = snprintf(buf, PAGE_SIZE, "%i\n", buffer_size_kb);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002664 mutex_unlock(&info->device->mutex);
2665
2666 return retval;
2667}
2668
2669static ssize_t store_write_buffer_kb(struct device *dev,
2670 struct device_attribute *attr,
2671 const char *buf, size_t count)
2672{
2673 struct comedi_device_file_info *info = dev_get_drvdata(dev);
Florian Schmaus2e1c3942011-12-08 12:12:44 +01002674 unsigned int new_size_kb;
Florian Schmausc5018162011-12-08 12:12:45 +01002675 unsigned int new_size;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002676 int retval;
Florian Schmaus2e1c3942011-12-08 12:12:44 +01002677 int ret;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002678 struct comedi_subdevice *const write_subdevice =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302679 comedi_get_write_subdevice(info);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002680
Florian Schmaus2e1c3942011-12-08 12:12:44 +01002681 ret = kstrtouint(buf, 10, &new_size_kb);
2682 if (ret)
2683 return ret;
Florian Schmausc5018162011-12-08 12:12:45 +01002684 if (new_size_kb > (UINT_MAX / bytes_per_kibi))
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002685 return -EINVAL;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302686 new_size = ((uint64_t) new_size_kb) * bytes_per_kibi;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002687
2688 mutex_lock(&info->device->mutex);
2689 if (write_subdevice == NULL ||
2690 (write_subdevice->subdev_flags & SDF_CMD_WRITE) == 0 ||
2691 write_subdevice->async == NULL) {
2692 mutex_unlock(&info->device->mutex);
2693 return -EINVAL;
2694 }
2695 retval = resize_async_buffer(info->device, write_subdevice,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302696 write_subdevice->async, new_size);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002697 mutex_unlock(&info->device->mutex);
2698
2699 if (retval < 0)
2700 return retval;
2701 return count;
2702}
2703
2704static struct device_attribute dev_attr_write_buffer_kb = {
2705 .attr = {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302706 .name = "write_buffer_kb",
2707 .mode = S_IRUGO | S_IWUSR | S_IWGRP},
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002708 .show = &show_write_buffer_kb,
2709 .store = &store_write_buffer_kb
2710};