blob: aaad76e0a76a351f6b26a508982dbbfa9787c7a9 [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-Hartman476b8472008-11-13 17:05:58 -080052/* #include "kvmem.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;
60module_param(comedi_debug, int, 0644);
61#endif
62
Ian Abbott6a9d7a22008-12-08 17:05:50 +000063int comedi_autoconfig = 1;
64module_param(comedi_autoconfig, bool, 0444);
65
Bernd Porr1dd33ab2008-12-08 23:30:13 +000066int comedi_num_legacy_minors = 0;
67module_param(comedi_num_legacy_minors, int, 0444);
68
David Schleefed9eccb2008-11-04 20:29:31 -080069static DEFINE_SPINLOCK(comedi_file_info_table_lock);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -080070static struct comedi_device_file_info
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +053071*comedi_file_info_table[COMEDI_NUM_MINORS];
David Schleefed9eccb2008-11-04 20:29:31 -080072
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +053073static int do_devconfig_ioctl(struct comedi_device *dev,
74 struct comedi_devconfig *arg);
Bill Pemberton71b5f4f2009-03-16 22:05:08 -040075static int do_bufconfig_ioctl(struct comedi_device *dev, void *arg);
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +053076static int do_devinfo_ioctl(struct comedi_device *dev,
77 struct comedi_devinfo *arg, struct file *file);
78static int do_subdinfo_ioctl(struct comedi_device *dev,
79 struct comedi_subdinfo *arg, void *file);
80static int do_chaninfo_ioctl(struct comedi_device *dev,
81 struct comedi_chaninfo *arg);
Bill Pemberton71b5f4f2009-03-16 22:05:08 -040082static int do_bufinfo_ioctl(struct comedi_device *dev, void *arg);
83static int do_cmd_ioctl(struct comedi_device *dev, void *arg, void *file);
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +053084static int do_lock_ioctl(struct comedi_device *dev, unsigned int arg,
85 void *file);
86static int do_unlock_ioctl(struct comedi_device *dev, unsigned int arg,
87 void *file);
88static int do_cancel_ioctl(struct comedi_device *dev, unsigned int arg,
89 void *file);
Bill Pemberton71b5f4f2009-03-16 22:05:08 -040090static int do_cmdtest_ioctl(struct comedi_device *dev, void *arg, void *file);
91static int do_insnlist_ioctl(struct comedi_device *dev, void *arg, void *file);
92static int do_insn_ioctl(struct comedi_device *dev, void *arg, void *file);
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +053093static int do_poll_ioctl(struct comedi_device *dev, unsigned int subd,
94 void *file);
David Schleefed9eccb2008-11-04 20:29:31 -080095
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +053096extern void do_become_nonbusy(struct comedi_device *dev,
97 struct comedi_subdevice *s);
Bill Pemberton34c43922009-03-16 22:05:14 -040098static int do_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
David Schleefed9eccb2008-11-04 20:29:31 -080099
100static int comedi_fasync(int fd, struct file *file, int on);
101
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400102static int is_device_busy(struct comedi_device *dev);
Frank Mori Hess883db3d2009-04-14 11:21:41 -0400103static int resize_async_buffer(struct comedi_device *dev,
104 struct comedi_subdevice *s,
105 struct comedi_async *async, unsigned new_size);
106
107/* declarations for sysfs attribute files */
108static struct device_attribute dev_attr_max_read_buffer_kb;
109static struct device_attribute dev_attr_read_buffer_kb;
110static struct device_attribute dev_attr_max_write_buffer_kb;
111static struct device_attribute dev_attr_write_buffer_kb;
David Schleefed9eccb2008-11-04 20:29:31 -0800112
113#ifdef HAVE_UNLOCKED_IOCTL
114static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800115 unsigned long arg)
David Schleefed9eccb2008-11-04 20:29:31 -0800116#else
117static int comedi_ioctl(struct inode *inode, struct file *file,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800118 unsigned int cmd, unsigned long arg)
David Schleefed9eccb2008-11-04 20:29:31 -0800119#endif
120{
121 const unsigned minor = iminor(file->f_dentry->d_inode);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800122 struct comedi_device_file_info *dev_file_info =
123 comedi_get_device_file_info(minor);
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400124 struct comedi_device *dev;
David Schleefed9eccb2008-11-04 20:29:31 -0800125 int rc;
126
Frank Mori Hess53b670a2008-12-15 13:48:47 +0000127 if (dev_file_info == NULL || dev_file_info->device == NULL)
128 return -ENODEV;
129 dev = dev_file_info->device;
130
David Schleefed9eccb2008-11-04 20:29:31 -0800131 mutex_lock(&dev->mutex);
132
133 /* Device config is special, because it must work on
134 * an unconfigured device. */
135 if (cmd == COMEDI_DEVCONFIG) {
136 rc = do_devconfig_ioctl(dev, (void *)arg);
137 goto done;
138 }
139
140 if (!dev->attached) {
141 DPRINTK("no driver configured on /dev/comedi%i\n", dev->minor);
142 rc = -ENODEV;
143 goto done;
144 }
145
146 switch (cmd) {
147 case COMEDI_BUFCONFIG:
148 rc = do_bufconfig_ioctl(dev, (void *)arg);
149 break;
150 case COMEDI_DEVINFO:
151 rc = do_devinfo_ioctl(dev, (void *)arg, file);
152 break;
153 case COMEDI_SUBDINFO:
154 rc = do_subdinfo_ioctl(dev, (void *)arg, file);
155 break;
156 case COMEDI_CHANINFO:
157 rc = do_chaninfo_ioctl(dev, (void *)arg);
158 break;
159 case COMEDI_RANGEINFO:
160 rc = do_rangeinfo_ioctl(dev, (void *)arg);
161 break;
162 case COMEDI_BUFINFO:
163 rc = do_bufinfo_ioctl(dev, (void *)arg);
164 break;
165 case COMEDI_LOCK:
166 rc = do_lock_ioctl(dev, arg, file);
167 break;
168 case COMEDI_UNLOCK:
169 rc = do_unlock_ioctl(dev, arg, file);
170 break;
171 case COMEDI_CANCEL:
172 rc = do_cancel_ioctl(dev, arg, file);
173 break;
174 case COMEDI_CMD:
175 rc = do_cmd_ioctl(dev, (void *)arg, file);
176 break;
177 case COMEDI_CMDTEST:
178 rc = do_cmdtest_ioctl(dev, (void *)arg, file);
179 break;
180 case COMEDI_INSNLIST:
181 rc = do_insnlist_ioctl(dev, (void *)arg, file);
182 break;
183 case COMEDI_INSN:
184 rc = do_insn_ioctl(dev, (void *)arg, file);
185 break;
186 case COMEDI_POLL:
187 rc = do_poll_ioctl(dev, arg, file);
188 break;
189 default:
190 rc = -ENOTTY;
191 break;
192 }
193
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800194done:
David Schleefed9eccb2008-11-04 20:29:31 -0800195 mutex_unlock(&dev->mutex);
196 return rc;
197}
198
199/*
200 COMEDI_DEVCONFIG
201 device config ioctl
202
203 arg:
204 pointer to devconfig structure
205
206 reads:
207 devconfig structure at arg
208
209 writes:
210 none
211*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530212static int do_devconfig_ioctl(struct comedi_device *dev,
213 struct comedi_devconfig *arg)
David Schleefed9eccb2008-11-04 20:29:31 -0800214{
Bill Pemberton0707bb02009-03-16 22:06:20 -0400215 struct comedi_devconfig it;
David Schleefed9eccb2008-11-04 20:29:31 -0800216 int ret;
217 unsigned char *aux_data = NULL;
218 int aux_len;
219
220 if (!capable(CAP_SYS_ADMIN))
221 return -EPERM;
222
223 if (arg == NULL) {
224 if (is_device_busy(dev))
225 return -EBUSY;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800226 if (dev->attached) {
David Schleefed9eccb2008-11-04 20:29:31 -0800227 struct module *driver_module = dev->driver->module;
228 comedi_device_detach(dev);
229 module_put(driver_module);
230 }
231 return 0;
232 }
233
Bill Pemberton0707bb02009-03-16 22:06:20 -0400234 if (copy_from_user(&it, arg, sizeof(struct comedi_devconfig)))
David Schleefed9eccb2008-11-04 20:29:31 -0800235 return -EFAULT;
236
237 it.board_name[COMEDI_NAMELEN - 1] = 0;
238
239 if (comedi_aux_data(it.options, 0) &&
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800240 it.options[COMEDI_DEVCONF_AUX_DATA_LENGTH]) {
David Schleefed9eccb2008-11-04 20:29:31 -0800241 int bit_shift;
242 aux_len = it.options[COMEDI_DEVCONF_AUX_DATA_LENGTH];
243 if (aux_len < 0)
244 return -EFAULT;
245
246 aux_data = vmalloc(aux_len);
247 if (!aux_data)
248 return -ENOMEM;
249
250 if (copy_from_user(aux_data,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800251 comedi_aux_data(it.options, 0), aux_len)) {
David Schleefed9eccb2008-11-04 20:29:31 -0800252 vfree(aux_data);
253 return -EFAULT;
254 }
255 it.options[COMEDI_DEVCONF_AUX_DATA_LO] =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800256 (unsigned long)aux_data;
David Schleefed9eccb2008-11-04 20:29:31 -0800257 if (sizeof(void *) > sizeof(int)) {
258 bit_shift = sizeof(int) * 8;
259 it.options[COMEDI_DEVCONF_AUX_DATA_HI] =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800260 ((unsigned long)aux_data) >> bit_shift;
David Schleefed9eccb2008-11-04 20:29:31 -0800261 } else
262 it.options[COMEDI_DEVCONF_AUX_DATA_HI] = 0;
263 }
264
265 ret = comedi_device_attach(dev, &it);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800266 if (ret == 0) {
267 if (!try_module_get(dev->driver->module)) {
David Schleefed9eccb2008-11-04 20:29:31 -0800268 comedi_device_detach(dev);
269 return -ENOSYS;
270 }
271 }
272
273 if (aux_data)
274 vfree(aux_data);
275
276 return ret;
277}
278
279/*
280 COMEDI_BUFCONFIG
281 buffer configuration ioctl
282
283 arg:
284 pointer to bufconfig structure
285
286 reads:
287 bufconfig at arg
288
289 writes:
290 modified bufconfig at arg
291
292*/
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400293static int do_bufconfig_ioctl(struct comedi_device *dev, void *arg)
David Schleefed9eccb2008-11-04 20:29:31 -0800294{
Bill Pembertonbe6aba42009-03-16 22:06:37 -0400295 struct comedi_bufconfig bc;
Bill Pembertond1636792009-03-16 22:05:20 -0400296 struct comedi_async *async;
Bill Pemberton34c43922009-03-16 22:05:14 -0400297 struct comedi_subdevice *s;
Frank Mori Hess883db3d2009-04-14 11:21:41 -0400298 int retval = 0;
David Schleefed9eccb2008-11-04 20:29:31 -0800299
Bill Pembertonbe6aba42009-03-16 22:06:37 -0400300 if (copy_from_user(&bc, arg, sizeof(struct comedi_bufconfig)))
David Schleefed9eccb2008-11-04 20:29:31 -0800301 return -EFAULT;
302
303 if (bc.subdevice >= dev->n_subdevices || bc.subdevice < 0)
304 return -EINVAL;
305
306 s = dev->subdevices + bc.subdevice;
307 async = s->async;
308
309 if (!async) {
310 DPRINTK("subdevice does not have async capability\n");
311 bc.size = 0;
312 bc.maximum_size = 0;
313 goto copyback;
314 }
315
316 if (bc.maximum_size) {
317 if (!capable(CAP_SYS_ADMIN))
318 return -EPERM;
319
320 async->max_bufsize = bc.maximum_size;
321 }
322
323 if (bc.size) {
Frank Mori Hess883db3d2009-04-14 11:21:41 -0400324 retval = resize_async_buffer(dev, s, async, bc.size);
325 if (retval < 0)
326 return retval;
David Schleefed9eccb2008-11-04 20:29:31 -0800327 }
328
329 bc.size = async->prealloc_bufsz;
330 bc.maximum_size = async->max_bufsize;
331
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800332copyback:
Bill Pembertonbe6aba42009-03-16 22:06:37 -0400333 if (copy_to_user(arg, &bc, sizeof(struct comedi_bufconfig)))
David Schleefed9eccb2008-11-04 20:29:31 -0800334 return -EFAULT;
335
336 return 0;
337}
338
339/*
340 COMEDI_DEVINFO
341 device info ioctl
342
343 arg:
344 pointer to devinfo structure
345
346 reads:
347 none
348
349 writes:
350 devinfo structure
351
352*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530353static int do_devinfo_ioctl(struct comedi_device *dev,
354 struct comedi_devinfo *arg, struct file *file)
David Schleefed9eccb2008-11-04 20:29:31 -0800355{
Bill Pemberton063db042009-03-16 22:06:15 -0400356 struct comedi_devinfo devinfo;
David Schleefed9eccb2008-11-04 20:29:31 -0800357 const unsigned minor = iminor(file->f_dentry->d_inode);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800358 struct comedi_device_file_info *dev_file_info =
359 comedi_get_device_file_info(minor);
Bill Pemberton34c43922009-03-16 22:05:14 -0400360 struct comedi_subdevice *read_subdev =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800361 comedi_get_read_subdevice(dev_file_info);
Bill Pemberton34c43922009-03-16 22:05:14 -0400362 struct comedi_subdevice *write_subdev =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800363 comedi_get_write_subdevice(dev_file_info);
David Schleefed9eccb2008-11-04 20:29:31 -0800364
365 memset(&devinfo, 0, sizeof(devinfo));
366
367 /* fill devinfo structure */
368 devinfo.version_code = COMEDI_VERSION_CODE;
369 devinfo.n_subdevs = dev->n_subdevices;
370 memcpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN);
371 memcpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN);
372
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800373 if (read_subdev)
David Schleefed9eccb2008-11-04 20:29:31 -0800374 devinfo.read_subdevice = read_subdev - dev->subdevices;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800375 else
David Schleefed9eccb2008-11-04 20:29:31 -0800376 devinfo.read_subdevice = -1;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800377
378 if (write_subdev)
David Schleefed9eccb2008-11-04 20:29:31 -0800379 devinfo.write_subdevice = write_subdev - dev->subdevices;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800380 else
David Schleefed9eccb2008-11-04 20:29:31 -0800381 devinfo.write_subdevice = -1;
David Schleefed9eccb2008-11-04 20:29:31 -0800382
Bill Pemberton063db042009-03-16 22:06:15 -0400383 if (copy_to_user(arg, &devinfo, sizeof(struct comedi_devinfo)))
David Schleefed9eccb2008-11-04 20:29:31 -0800384 return -EFAULT;
385
386 return 0;
387}
388
389/*
390 COMEDI_SUBDINFO
391 subdevice info ioctl
392
393 arg:
394 pointer to array of subdevice info structures
395
396 reads:
397 none
398
399 writes:
400 array of subdevice info structures at arg
401
402*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530403static int do_subdinfo_ioctl(struct comedi_device *dev,
404 struct comedi_subdinfo *arg, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -0800405{
406 int ret, i;
Bill Pembertonbd52efb2009-03-16 22:06:09 -0400407 struct comedi_subdinfo *tmp, *us;
Bill Pemberton34c43922009-03-16 22:05:14 -0400408 struct comedi_subdevice *s;
David Schleefed9eccb2008-11-04 20:29:31 -0800409
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530410 tmp =
411 kcalloc(dev->n_subdevices, sizeof(struct comedi_subdinfo),
412 GFP_KERNEL);
David Schleefed9eccb2008-11-04 20:29:31 -0800413 if (!tmp)
414 return -ENOMEM;
415
416 /* fill subdinfo structs */
417 for (i = 0; i < dev->n_subdevices; i++) {
418 s = dev->subdevices + i;
419 us = tmp + i;
420
421 us->type = s->type;
422 us->n_chan = s->n_chan;
423 us->subd_flags = s->subdev_flags;
424 if (comedi_get_subdevice_runflags(s) & SRF_RUNNING)
425 us->subd_flags |= SDF_RUNNING;
426#define TIMER_nanosec 5 /* backwards compatibility */
427 us->timer_type = TIMER_nanosec;
428 us->len_chanlist = s->len_chanlist;
429 us->maxdata = s->maxdata;
430 if (s->range_table) {
431 us->range_type =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800432 (i << 24) | (0 << 16) | (s->range_table->length);
David Schleefed9eccb2008-11-04 20:29:31 -0800433 } else {
434 us->range_type = 0; /* XXX */
435 }
436 us->flags = s->flags;
437
438 if (s->busy)
439 us->subd_flags |= SDF_BUSY;
440 if (s->busy == file)
441 us->subd_flags |= SDF_BUSY_OWNER;
442 if (s->lock)
443 us->subd_flags |= SDF_LOCKED;
444 if (s->lock == file)
445 us->subd_flags |= SDF_LOCK_OWNER;
446 if (!s->maxdata && s->maxdata_list)
447 us->subd_flags |= SDF_MAXDATA;
448 if (s->flaglist)
449 us->subd_flags |= SDF_FLAGS;
450 if (s->range_table_list)
451 us->subd_flags |= SDF_RANGETYPE;
452 if (s->do_cmd)
453 us->subd_flags |= SDF_CMD;
454
455 if (s->insn_bits != &insn_inval)
456 us->insn_bits_support = COMEDI_SUPPORTED;
457 else
458 us->insn_bits_support = COMEDI_UNSUPPORTED;
459
460 us->settling_time_0 = s->settling_time_0;
461 }
462
463 ret = copy_to_user(arg, tmp,
Bill Pembertonbd52efb2009-03-16 22:06:09 -0400464 dev->n_subdevices * sizeof(struct comedi_subdinfo));
David Schleefed9eccb2008-11-04 20:29:31 -0800465
466 kfree(tmp);
467
468 return ret ? -EFAULT : 0;
469}
470
471/*
472 COMEDI_CHANINFO
473 subdevice info ioctl
474
475 arg:
476 pointer to chaninfo structure
477
478 reads:
479 chaninfo structure at arg
480
481 writes:
482 arrays at elements of chaninfo structure
483
484*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530485static int do_chaninfo_ioctl(struct comedi_device *dev,
486 struct comedi_chaninfo *arg)
David Schleefed9eccb2008-11-04 20:29:31 -0800487{
Bill Pemberton34c43922009-03-16 22:05:14 -0400488 struct comedi_subdevice *s;
Bill Pembertona18b4162009-03-16 22:06:04 -0400489 struct comedi_chaninfo it;
David Schleefed9eccb2008-11-04 20:29:31 -0800490
Bill Pembertona18b4162009-03-16 22:06:04 -0400491 if (copy_from_user(&it, arg, sizeof(struct comedi_chaninfo)))
David Schleefed9eccb2008-11-04 20:29:31 -0800492 return -EFAULT;
493
494 if (it.subdev >= dev->n_subdevices)
495 return -EINVAL;
496 s = dev->subdevices + it.subdev;
497
498 if (it.maxdata_list) {
499 if (s->maxdata || !s->maxdata_list)
500 return -EINVAL;
501 if (copy_to_user(it.maxdata_list, s->maxdata_list,
Bill Pemberton790c5542009-03-16 22:05:02 -0400502 s->n_chan * sizeof(unsigned int)))
David Schleefed9eccb2008-11-04 20:29:31 -0800503 return -EFAULT;
504 }
505
506 if (it.flaglist) {
507 if (!s->flaglist)
508 return -EINVAL;
509 if (copy_to_user(it.flaglist, s->flaglist,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800510 s->n_chan * sizeof(unsigned int)))
David Schleefed9eccb2008-11-04 20:29:31 -0800511 return -EFAULT;
512 }
513
514 if (it.rangelist) {
515 int i;
516
517 if (!s->range_table_list)
518 return -EINVAL;
519 for (i = 0; i < s->n_chan; i++) {
520 int x;
521
522 x = (dev->minor << 28) | (it.subdev << 24) | (i << 16) |
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800523 (s->range_table_list[i]->length);
David Schleefed9eccb2008-11-04 20:29:31 -0800524 put_user(x, it.rangelist + i);
525 }
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800526#if 0
527 if (copy_to_user(it.rangelist, s->range_type_list,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530528 s->n_chan * sizeof(unsigned int)))
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800529 return -EFAULT;
530#endif
David Schleefed9eccb2008-11-04 20:29:31 -0800531 }
532
533 return 0;
534}
535
536 /*
537 COMEDI_BUFINFO
538 buffer information ioctl
539
540 arg:
541 pointer to bufinfo structure
542
543 reads:
544 bufinfo at arg
545
546 writes:
547 modified bufinfo at arg
548
549 */
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400550static int do_bufinfo_ioctl(struct comedi_device *dev, void *arg)
David Schleefed9eccb2008-11-04 20:29:31 -0800551{
Bill Pemberton9aa53392009-03-16 22:06:42 -0400552 struct comedi_bufinfo bi;
Bill Pemberton34c43922009-03-16 22:05:14 -0400553 struct comedi_subdevice *s;
Bill Pembertond1636792009-03-16 22:05:20 -0400554 struct comedi_async *async;
David Schleefed9eccb2008-11-04 20:29:31 -0800555
Bill Pemberton9aa53392009-03-16 22:06:42 -0400556 if (copy_from_user(&bi, arg, sizeof(struct comedi_bufinfo)))
David Schleefed9eccb2008-11-04 20:29:31 -0800557 return -EFAULT;
558
559 if (bi.subdevice >= dev->n_subdevices || bi.subdevice < 0)
560 return -EINVAL;
561
562 s = dev->subdevices + bi.subdevice;
563 async = s->async;
564
565 if (!async) {
566 DPRINTK("subdevice does not have async capability\n");
567 bi.buf_write_ptr = 0;
568 bi.buf_read_ptr = 0;
569 bi.buf_write_count = 0;
570 bi.buf_read_count = 0;
571 goto copyback;
572 }
573
574 if (bi.bytes_read && (s->subdev_flags & SDF_CMD_READ)) {
575 bi.bytes_read = comedi_buf_read_alloc(async, bi.bytes_read);
576 comedi_buf_read_free(async, bi.bytes_read);
577
578 if (!(comedi_get_subdevice_runflags(s) & (SRF_ERROR |
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800579 SRF_RUNNING))
580 && async->buf_write_count == async->buf_read_count) {
David Schleefed9eccb2008-11-04 20:29:31 -0800581 do_become_nonbusy(dev, s);
582 }
583 }
584
585 if (bi.bytes_written && (s->subdev_flags & SDF_CMD_WRITE)) {
586 bi.bytes_written =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800587 comedi_buf_write_alloc(async, bi.bytes_written);
David Schleefed9eccb2008-11-04 20:29:31 -0800588 comedi_buf_write_free(async, bi.bytes_written);
589 }
590
591 bi.buf_write_count = async->buf_write_count;
592 bi.buf_write_ptr = async->buf_write_ptr;
593 bi.buf_read_count = async->buf_read_count;
594 bi.buf_read_ptr = async->buf_read_ptr;
595
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800596copyback:
Bill Pemberton9aa53392009-03-16 22:06:42 -0400597 if (copy_to_user(arg, &bi, sizeof(struct comedi_bufinfo)))
David Schleefed9eccb2008-11-04 20:29:31 -0800598 return -EFAULT;
599
600 return 0;
601}
602
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530603static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
604 unsigned int *data, void *file);
David Schleefed9eccb2008-11-04 20:29:31 -0800605/*
606 * COMEDI_INSNLIST
607 * synchronous instructions
608 *
609 * arg:
610 * pointer to sync cmd structure
611 *
612 * reads:
613 * sync cmd struct at arg
614 * instruction list
615 * data (for writes)
616 *
617 * writes:
618 * data (for reads)
619 */
620/* arbitrary limits */
621#define MAX_SAMPLES 256
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400622static int do_insnlist_ioctl(struct comedi_device *dev, void *arg, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -0800623{
Bill Pembertonda613f42009-03-16 22:05:59 -0400624 struct comedi_insnlist insnlist;
Bill Pemberton90035c02009-03-16 22:05:53 -0400625 struct comedi_insn *insns = NULL;
Bill Pemberton790c5542009-03-16 22:05:02 -0400626 unsigned int *data = NULL;
David Schleefed9eccb2008-11-04 20:29:31 -0800627 int i = 0;
628 int ret = 0;
629
Bill Pembertonda613f42009-03-16 22:05:59 -0400630 if (copy_from_user(&insnlist, arg, sizeof(struct comedi_insnlist)))
David Schleefed9eccb2008-11-04 20:29:31 -0800631 return -EFAULT;
632
Bill Pemberton790c5542009-03-16 22:05:02 -0400633 data = kmalloc(sizeof(unsigned int) * MAX_SAMPLES, GFP_KERNEL);
David Schleefed9eccb2008-11-04 20:29:31 -0800634 if (!data) {
635 DPRINTK("kmalloc failed\n");
636 ret = -ENOMEM;
637 goto error;
638 }
639
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530640 insns =
641 kmalloc(sizeof(struct comedi_insn) * insnlist.n_insns, GFP_KERNEL);
David Schleefed9eccb2008-11-04 20:29:31 -0800642 if (!insns) {
643 DPRINTK("kmalloc failed\n");
644 ret = -ENOMEM;
645 goto error;
646 }
647
648 if (copy_from_user(insns, insnlist.insns,
Bill Pemberton90035c02009-03-16 22:05:53 -0400649 sizeof(struct comedi_insn) * insnlist.n_insns)) {
David Schleefed9eccb2008-11-04 20:29:31 -0800650 DPRINTK("copy_from_user failed\n");
651 ret = -EFAULT;
652 goto error;
653 }
654
655 for (i = 0; i < insnlist.n_insns; i++) {
656 if (insns[i].n > MAX_SAMPLES) {
657 DPRINTK("number of samples too large\n");
658 ret = -EINVAL;
659 goto error;
660 }
661 if (insns[i].insn & INSN_MASK_WRITE) {
662 if (copy_from_user(data, insns[i].data,
Bill Pemberton790c5542009-03-16 22:05:02 -0400663 insns[i].n * sizeof(unsigned int))) {
David Schleefed9eccb2008-11-04 20:29:31 -0800664 DPRINTK("copy_from_user failed\n");
665 ret = -EFAULT;
666 goto error;
667 }
668 }
669 ret = parse_insn(dev, insns + i, data, file);
670 if (ret < 0)
671 goto error;
672 if (insns[i].insn & INSN_MASK_READ) {
673 if (copy_to_user(insns[i].data, data,
Bill Pemberton790c5542009-03-16 22:05:02 -0400674 insns[i].n * sizeof(unsigned int))) {
David Schleefed9eccb2008-11-04 20:29:31 -0800675 DPRINTK("copy_to_user failed\n");
676 ret = -EFAULT;
677 goto error;
678 }
679 }
680 if (need_resched())
681 schedule();
682 }
683
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800684error:
685 kfree(insns);
686 kfree(data);
David Schleefed9eccb2008-11-04 20:29:31 -0800687
688 if (ret < 0)
689 return ret;
690 return i;
691}
692
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530693static int check_insn_config_length(struct comedi_insn *insn,
694 unsigned int *data)
David Schleefed9eccb2008-11-04 20:29:31 -0800695{
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800696 if (insn->n < 1)
697 return -EINVAL;
David Schleefed9eccb2008-11-04 20:29:31 -0800698
699 switch (data[0]) {
700 case INSN_CONFIG_DIO_OUTPUT:
701 case INSN_CONFIG_DIO_INPUT:
702 case INSN_CONFIG_DISARM:
703 case INSN_CONFIG_RESET:
704 if (insn->n == 1)
705 return 0;
706 break;
707 case INSN_CONFIG_ARM:
708 case INSN_CONFIG_DIO_QUERY:
709 case INSN_CONFIG_BLOCK_SIZE:
710 case INSN_CONFIG_FILTER:
711 case INSN_CONFIG_SERIAL_CLOCK:
712 case INSN_CONFIG_BIDIRECTIONAL_DATA:
713 case INSN_CONFIG_ALT_SOURCE:
714 case INSN_CONFIG_SET_COUNTER_MODE:
715 case INSN_CONFIG_8254_READ_STATUS:
716 case INSN_CONFIG_SET_ROUTING:
717 case INSN_CONFIG_GET_ROUTING:
718 case INSN_CONFIG_GET_PWM_STATUS:
719 case INSN_CONFIG_PWM_SET_PERIOD:
720 case INSN_CONFIG_PWM_GET_PERIOD:
721 if (insn->n == 2)
722 return 0;
723 break;
724 case INSN_CONFIG_SET_GATE_SRC:
725 case INSN_CONFIG_GET_GATE_SRC:
726 case INSN_CONFIG_SET_CLOCK_SRC:
727 case INSN_CONFIG_GET_CLOCK_SRC:
728 case INSN_CONFIG_SET_OTHER_SRC:
729 case INSN_CONFIG_GET_COUNTER_STATUS:
730 case INSN_CONFIG_PWM_SET_H_BRIDGE:
731 case INSN_CONFIG_PWM_GET_H_BRIDGE:
732 case INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE:
733 if (insn->n == 3)
734 return 0;
735 break;
736 case INSN_CONFIG_PWM_OUTPUT:
737 case INSN_CONFIG_ANALOG_TRIG:
738 if (insn->n == 5)
739 return 0;
740 break;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530741 /* by default we allow the insn since we don't have checks for
742 * all possible cases yet */
David Schleefed9eccb2008-11-04 20:29:31 -0800743 default:
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700744 printk("comedi: no check for data length of config insn id "
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530745 "%i is implemented.\n"
746 " Add a check to %s in %s.\n"
747 " Assuming n=%i is correct.\n", data[0], __func__,
748 __FILE__, insn->n);
David Schleefed9eccb2008-11-04 20:29:31 -0800749 return 0;
750 break;
751 }
752 return -EINVAL;
753}
754
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530755static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
756 unsigned int *data, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -0800757{
Bill Pemberton34c43922009-03-16 22:05:14 -0400758 struct comedi_subdevice *s;
David Schleefed9eccb2008-11-04 20:29:31 -0800759 int ret = 0;
760 int i;
761
762 if (insn->insn & INSN_MASK_SPECIAL) {
763 /* a non-subdevice instruction */
764
765 switch (insn->insn) {
766 case INSN_GTOD:
767 {
768 struct timeval tv;
769
770 if (insn->n != 2) {
771 ret = -EINVAL;
772 break;
773 }
774
775 do_gettimeofday(&tv);
776 data[0] = tv.tv_sec;
777 data[1] = tv.tv_usec;
778 ret = 2;
779
780 break;
781 }
782 case INSN_WAIT:
783 if (insn->n != 1 || data[0] >= 100000) {
784 ret = -EINVAL;
785 break;
786 }
787 udelay(data[0] / 1000);
788 ret = 1;
789 break;
790 case INSN_INTTRIG:
791 if (insn->n != 1) {
792 ret = -EINVAL;
793 break;
794 }
795 if (insn->subdev >= dev->n_subdevices) {
796 DPRINTK("%d not usable subdevice\n",
797 insn->subdev);
798 ret = -EINVAL;
799 break;
800 }
801 s = dev->subdevices + insn->subdev;
802 if (!s->async) {
803 DPRINTK("no async\n");
804 ret = -EINVAL;
805 break;
806 }
807 if (!s->async->inttrig) {
808 DPRINTK("no inttrig\n");
809 ret = -EAGAIN;
810 break;
811 }
812 ret = s->async->inttrig(dev, s, insn->data[0]);
813 if (ret >= 0)
814 ret = 1;
815 break;
816 default:
817 DPRINTK("invalid insn\n");
818 ret = -EINVAL;
819 break;
820 }
821 } else {
822 /* a subdevice instruction */
Bill Pemberton790c5542009-03-16 22:05:02 -0400823 unsigned int maxdata;
David Schleefed9eccb2008-11-04 20:29:31 -0800824
825 if (insn->subdev >= dev->n_subdevices) {
826 DPRINTK("subdevice %d out of range\n", insn->subdev);
827 ret = -EINVAL;
828 goto out;
829 }
830 s = dev->subdevices + insn->subdev;
831
832 if (s->type == COMEDI_SUBD_UNUSED) {
833 DPRINTK("%d not usable subdevice\n", insn->subdev);
834 ret = -EIO;
835 goto out;
836 }
837
838 /* are we locked? (ioctl lock) */
839 if (s->lock && s->lock != file) {
840 DPRINTK("device locked\n");
841 ret = -EACCES;
842 goto out;
843 }
844
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800845 ret = check_chanlist(s, 1, &insn->chanspec);
846 if (ret < 0) {
David Schleefed9eccb2008-11-04 20:29:31 -0800847 ret = -EINVAL;
848 DPRINTK("bad chanspec\n");
849 goto out;
850 }
851
852 if (s->busy) {
853 ret = -EBUSY;
854 goto out;
855 }
856 /* This looks arbitrary. It is. */
857 s->busy = &parse_insn;
858 switch (insn->insn) {
859 case INSN_READ:
860 ret = s->insn_read(dev, s, insn, data);
861 break;
862 case INSN_WRITE:
863 maxdata = s->maxdata_list
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800864 ? s->maxdata_list[CR_CHAN(insn->chanspec)]
865 : s->maxdata;
David Schleefed9eccb2008-11-04 20:29:31 -0800866 for (i = 0; i < insn->n; ++i) {
867 if (data[i] > maxdata) {
868 ret = -EINVAL;
869 DPRINTK("bad data value(s)\n");
870 break;
871 }
872 }
873 if (ret == 0)
874 ret = s->insn_write(dev, s, insn, data);
875 break;
876 case INSN_BITS:
877 if (insn->n != 2) {
878 ret = -EINVAL;
879 break;
880 }
881 ret = s->insn_bits(dev, s, insn, data);
882 break;
883 case INSN_CONFIG:
884 ret = check_insn_config_length(insn, data);
885 if (ret)
886 break;
887 ret = s->insn_config(dev, s, insn, data);
888 break;
889 default:
890 ret = -EINVAL;
891 break;
892 }
893
894 s->busy = NULL;
895 }
896
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800897out:
David Schleefed9eccb2008-11-04 20:29:31 -0800898 return ret;
899}
900
901/*
902 * COMEDI_INSN
903 * synchronous instructions
904 *
905 * arg:
906 * pointer to insn
907 *
908 * reads:
Bill Pemberton90035c02009-03-16 22:05:53 -0400909 * struct comedi_insn struct at arg
David Schleefed9eccb2008-11-04 20:29:31 -0800910 * data (for writes)
911 *
912 * writes:
913 * data (for reads)
914 */
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400915static int do_insn_ioctl(struct comedi_device *dev, void *arg, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -0800916{
Bill Pemberton90035c02009-03-16 22:05:53 -0400917 struct comedi_insn insn;
Bill Pemberton790c5542009-03-16 22:05:02 -0400918 unsigned int *data = NULL;
David Schleefed9eccb2008-11-04 20:29:31 -0800919 int ret = 0;
920
Bill Pemberton790c5542009-03-16 22:05:02 -0400921 data = kmalloc(sizeof(unsigned int) * MAX_SAMPLES, GFP_KERNEL);
David Schleefed9eccb2008-11-04 20:29:31 -0800922 if (!data) {
923 ret = -ENOMEM;
924 goto error;
925 }
926
Bill Pemberton90035c02009-03-16 22:05:53 -0400927 if (copy_from_user(&insn, arg, sizeof(struct comedi_insn))) {
David Schleefed9eccb2008-11-04 20:29:31 -0800928 ret = -EFAULT;
929 goto error;
930 }
931
932 /* This is where the behavior of insn and insnlist deviate. */
933 if (insn.n > MAX_SAMPLES)
934 insn.n = MAX_SAMPLES;
935 if (insn.insn & INSN_MASK_WRITE) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530936 if (copy_from_user
937 (data, insn.data, insn.n * sizeof(unsigned int))) {
David Schleefed9eccb2008-11-04 20:29:31 -0800938 ret = -EFAULT;
939 goto error;
940 }
941 }
942 ret = parse_insn(dev, &insn, data, file);
943 if (ret < 0)
944 goto error;
945 if (insn.insn & INSN_MASK_READ) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530946 if (copy_to_user
947 (insn.data, data, insn.n * sizeof(unsigned int))) {
David Schleefed9eccb2008-11-04 20:29:31 -0800948 ret = -EFAULT;
949 goto error;
950 }
951 }
952 ret = insn.n;
953
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800954error:
955 kfree(data);
David Schleefed9eccb2008-11-04 20:29:31 -0800956
957 return ret;
958}
959
960/*
961 COMEDI_CMD
962 command ioctl
963
964 arg:
965 pointer to cmd structure
966
967 reads:
968 cmd structure at arg
969 channel/range list
970
971 writes:
972 modified cmd structure at arg
973
974*/
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400975static int do_cmd_ioctl(struct comedi_device *dev, void *arg, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -0800976{
Bill Pembertonea6d0d42009-03-16 22:05:47 -0400977 struct comedi_cmd user_cmd;
Bill Pemberton34c43922009-03-16 22:05:14 -0400978 struct comedi_subdevice *s;
Bill Pembertond1636792009-03-16 22:05:20 -0400979 struct comedi_async *async;
David Schleefed9eccb2008-11-04 20:29:31 -0800980 int ret = 0;
981 unsigned int *chanlist_saver = NULL;
982
Bill Pembertonea6d0d42009-03-16 22:05:47 -0400983 if (copy_from_user(&user_cmd, arg, sizeof(struct comedi_cmd))) {
David Schleefed9eccb2008-11-04 20:29:31 -0800984 DPRINTK("bad cmd address\n");
985 return -EFAULT;
986 }
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -0800987 /* save user's chanlist pointer so it can be restored later */
David Schleefed9eccb2008-11-04 20:29:31 -0800988 chanlist_saver = user_cmd.chanlist;
989
990 if (user_cmd.subdev >= dev->n_subdevices) {
991 DPRINTK("%d no such subdevice\n", user_cmd.subdev);
992 return -ENODEV;
993 }
994
995 s = dev->subdevices + user_cmd.subdev;
996 async = s->async;
997
998 if (s->type == COMEDI_SUBD_UNUSED) {
999 DPRINTK("%d not valid subdevice\n", user_cmd.subdev);
1000 return -EIO;
1001 }
1002
1003 if (!s->do_cmd || !s->do_cmdtest || !s->async) {
1004 DPRINTK("subdevice %i does not support commands\n",
1005 user_cmd.subdev);
1006 return -EIO;
1007 }
1008
1009 /* are we locked? (ioctl lock) */
1010 if (s->lock && s->lock != file) {
1011 DPRINTK("subdevice locked\n");
1012 return -EACCES;
1013 }
1014
1015 /* are we busy? */
1016 if (s->busy) {
1017 DPRINTK("subdevice busy\n");
1018 return -EBUSY;
1019 }
1020 s->busy = file;
1021
1022 /* make sure channel/gain list isn't too long */
1023 if (user_cmd.chanlist_len > s->len_chanlist) {
1024 DPRINTK("channel/gain list too long %u > %d\n",
1025 user_cmd.chanlist_len, s->len_chanlist);
1026 ret = -EINVAL;
1027 goto cleanup;
1028 }
1029
1030 /* make sure channel/gain list isn't too short */
1031 if (user_cmd.chanlist_len < 1) {
1032 DPRINTK("channel/gain list too short %u < 1\n",
1033 user_cmd.chanlist_len);
1034 ret = -EINVAL;
1035 goto cleanup;
1036 }
1037
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001038 kfree(async->cmd.chanlist);
David Schleefed9eccb2008-11-04 20:29:31 -08001039 async->cmd = user_cmd;
1040 async->cmd.data = NULL;
1041 /* load channel/gain list */
1042 async->cmd.chanlist =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001043 kmalloc(async->cmd.chanlist_len * sizeof(int), GFP_KERNEL);
David Schleefed9eccb2008-11-04 20:29:31 -08001044 if (!async->cmd.chanlist) {
1045 DPRINTK("allocation failed\n");
1046 ret = -ENOMEM;
1047 goto cleanup;
1048 }
1049
1050 if (copy_from_user(async->cmd.chanlist, user_cmd.chanlist,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001051 async->cmd.chanlist_len * sizeof(int))) {
David Schleefed9eccb2008-11-04 20:29:31 -08001052 DPRINTK("fault reading chanlist\n");
1053 ret = -EFAULT;
1054 goto cleanup;
1055 }
1056
1057 /* make sure each element in channel/gain list is valid */
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001058 ret = check_chanlist(s, async->cmd.chanlist_len, async->cmd.chanlist);
1059 if (ret < 0) {
David Schleefed9eccb2008-11-04 20:29:31 -08001060 DPRINTK("bad chanlist\n");
1061 goto cleanup;
1062 }
1063
1064 ret = s->do_cmdtest(dev, s, &async->cmd);
1065
1066 if (async->cmd.flags & TRIG_BOGUS || ret) {
1067 DPRINTK("test returned %d\n", ret);
1068 user_cmd = async->cmd;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001069 /* restore chanlist pointer before copying back */
David Schleefed9eccb2008-11-04 20:29:31 -08001070 user_cmd.chanlist = chanlist_saver;
1071 user_cmd.data = NULL;
Bill Pembertonea6d0d42009-03-16 22:05:47 -04001072 if (copy_to_user(arg, &user_cmd, sizeof(struct comedi_cmd))) {
David Schleefed9eccb2008-11-04 20:29:31 -08001073 DPRINTK("fault writing cmd\n");
1074 ret = -EFAULT;
1075 goto cleanup;
1076 }
1077 ret = -EAGAIN;
1078 goto cleanup;
1079 }
1080
1081 if (!async->prealloc_bufsz) {
1082 ret = -ENOMEM;
1083 DPRINTK("no buffer (?)\n");
1084 goto cleanup;
1085 }
1086
1087 comedi_reset_async_buf(async);
1088
1089 async->cb_mask =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001090 COMEDI_CB_EOA | COMEDI_CB_BLOCK | COMEDI_CB_ERROR |
1091 COMEDI_CB_OVERFLOW;
1092 if (async->cmd.flags & TRIG_WAKE_EOS)
David Schleefed9eccb2008-11-04 20:29:31 -08001093 async->cb_mask |= COMEDI_CB_EOS;
David Schleefed9eccb2008-11-04 20:29:31 -08001094
1095 comedi_set_subdevice_runflags(s, ~0, SRF_USER | SRF_RUNNING);
1096
David Schleefed9eccb2008-11-04 20:29:31 -08001097 ret = s->do_cmd(dev, s);
1098 if (ret == 0)
1099 return 0;
1100
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001101cleanup:
David Schleefed9eccb2008-11-04 20:29:31 -08001102 do_become_nonbusy(dev, s);
1103
1104 return ret;
1105}
1106
1107/*
1108 COMEDI_CMDTEST
1109 command testing ioctl
1110
1111 arg:
1112 pointer to cmd structure
1113
1114 reads:
1115 cmd structure at arg
1116 channel/range list
1117
1118 writes:
1119 modified cmd structure at arg
1120
1121*/
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04001122static int do_cmdtest_ioctl(struct comedi_device *dev, void *arg, void *file)
David Schleefed9eccb2008-11-04 20:29:31 -08001123{
Bill Pembertonea6d0d42009-03-16 22:05:47 -04001124 struct comedi_cmd user_cmd;
Bill Pemberton34c43922009-03-16 22:05:14 -04001125 struct comedi_subdevice *s;
David Schleefed9eccb2008-11-04 20:29:31 -08001126 int ret = 0;
1127 unsigned int *chanlist = NULL;
1128 unsigned int *chanlist_saver = NULL;
1129
Bill Pembertonea6d0d42009-03-16 22:05:47 -04001130 if (copy_from_user(&user_cmd, arg, sizeof(struct comedi_cmd))) {
David Schleefed9eccb2008-11-04 20:29:31 -08001131 DPRINTK("bad cmd address\n");
1132 return -EFAULT;
1133 }
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001134 /* save user's chanlist pointer so it can be restored later */
David Schleefed9eccb2008-11-04 20:29:31 -08001135 chanlist_saver = user_cmd.chanlist;
1136
1137 if (user_cmd.subdev >= dev->n_subdevices) {
1138 DPRINTK("%d no such subdevice\n", user_cmd.subdev);
1139 return -ENODEV;
1140 }
1141
1142 s = dev->subdevices + user_cmd.subdev;
1143 if (s->type == COMEDI_SUBD_UNUSED) {
1144 DPRINTK("%d not valid subdevice\n", user_cmd.subdev);
1145 return -EIO;
1146 }
1147
1148 if (!s->do_cmd || !s->do_cmdtest) {
1149 DPRINTK("subdevice %i does not support commands\n",
1150 user_cmd.subdev);
1151 return -EIO;
1152 }
1153
1154 /* make sure channel/gain list isn't too long */
1155 if (user_cmd.chanlist_len > s->len_chanlist) {
1156 DPRINTK("channel/gain list too long %d > %d\n",
1157 user_cmd.chanlist_len, s->len_chanlist);
1158 ret = -EINVAL;
1159 goto cleanup;
1160 }
1161
1162 /* load channel/gain list */
1163 if (user_cmd.chanlist) {
1164 chanlist =
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001165 kmalloc(user_cmd.chanlist_len * sizeof(int), GFP_KERNEL);
David Schleefed9eccb2008-11-04 20:29:31 -08001166 if (!chanlist) {
1167 DPRINTK("allocation failed\n");
1168 ret = -ENOMEM;
1169 goto cleanup;
1170 }
1171
1172 if (copy_from_user(chanlist, user_cmd.chanlist,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001173 user_cmd.chanlist_len * sizeof(int))) {
David Schleefed9eccb2008-11-04 20:29:31 -08001174 DPRINTK("fault reading chanlist\n");
1175 ret = -EFAULT;
1176 goto cleanup;
1177 }
1178
1179 /* make sure each element in channel/gain list is valid */
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001180 ret = check_chanlist(s, user_cmd.chanlist_len, chanlist);
1181 if (ret < 0) {
David Schleefed9eccb2008-11-04 20:29:31 -08001182 DPRINTK("bad chanlist\n");
1183 goto cleanup;
1184 }
1185
1186 user_cmd.chanlist = chanlist;
1187 }
1188
1189 ret = s->do_cmdtest(dev, s, &user_cmd);
1190
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001191 /* restore chanlist pointer before copying back */
David Schleefed9eccb2008-11-04 20:29:31 -08001192 user_cmd.chanlist = chanlist_saver;
1193
Bill Pembertonea6d0d42009-03-16 22:05:47 -04001194 if (copy_to_user(arg, &user_cmd, sizeof(struct comedi_cmd))) {
David Schleefed9eccb2008-11-04 20:29:31 -08001195 DPRINTK("bad cmd address\n");
1196 ret = -EFAULT;
1197 goto cleanup;
1198 }
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001199cleanup:
1200 kfree(chanlist);
David Schleefed9eccb2008-11-04 20:29:31 -08001201
1202 return ret;
1203}
1204
1205/*
1206 COMEDI_LOCK
1207 lock subdevice
1208
1209 arg:
1210 subdevice number
1211
1212 reads:
1213 none
1214
1215 writes:
1216 none
1217
1218*/
1219
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301220static int do_lock_ioctl(struct comedi_device *dev, unsigned int arg,
1221 void *file)
David Schleefed9eccb2008-11-04 20:29:31 -08001222{
1223 int ret = 0;
1224 unsigned long flags;
Bill Pemberton34c43922009-03-16 22:05:14 -04001225 struct comedi_subdevice *s;
David Schleefed9eccb2008-11-04 20:29:31 -08001226
1227 if (arg >= dev->n_subdevices)
1228 return -EINVAL;
1229 s = dev->subdevices + arg;
1230
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001231 spin_lock_irqsave(&s->spin_lock, flags);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001232 if (s->busy || s->lock)
David Schleefed9eccb2008-11-04 20:29:31 -08001233 ret = -EBUSY;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001234 else
David Schleefed9eccb2008-11-04 20:29:31 -08001235 s->lock = file;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001236 spin_unlock_irqrestore(&s->spin_lock, flags);
David Schleefed9eccb2008-11-04 20:29:31 -08001237
1238 if (ret < 0)
1239 return ret;
1240
1241#if 0
1242 if (s->lock_f)
1243 ret = s->lock_f(dev, s);
1244#endif
1245
1246 return ret;
1247}
1248
1249/*
1250 COMEDI_UNLOCK
1251 unlock subdevice
1252
1253 arg:
1254 subdevice number
1255
1256 reads:
1257 none
1258
1259 writes:
1260 none
1261
1262 This function isn't protected by the semaphore, since
1263 we already own the lock.
1264*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301265static int do_unlock_ioctl(struct comedi_device *dev, unsigned int arg,
1266 void *file)
David Schleefed9eccb2008-11-04 20:29:31 -08001267{
Bill Pemberton34c43922009-03-16 22:05:14 -04001268 struct comedi_subdevice *s;
David Schleefed9eccb2008-11-04 20:29:31 -08001269
1270 if (arg >= dev->n_subdevices)
1271 return -EINVAL;
1272 s = dev->subdevices + arg;
1273
1274 if (s->busy)
1275 return -EBUSY;
1276
1277 if (s->lock && s->lock != file)
1278 return -EACCES;
1279
1280 if (s->lock == file) {
1281#if 0
1282 if (s->unlock)
1283 s->unlock(dev, s);
1284#endif
1285
1286 s->lock = NULL;
1287 }
1288
1289 return 0;
1290}
1291
1292/*
1293 COMEDI_CANCEL
1294 cancel acquisition ioctl
1295
1296 arg:
1297 subdevice number
1298
1299 reads:
1300 nothing
1301
1302 writes:
1303 nothing
1304
1305*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301306static int do_cancel_ioctl(struct comedi_device *dev, unsigned int arg,
1307 void *file)
David Schleefed9eccb2008-11-04 20:29:31 -08001308{
Bill Pemberton34c43922009-03-16 22:05:14 -04001309 struct comedi_subdevice *s;
David Schleefed9eccb2008-11-04 20:29:31 -08001310
1311 if (arg >= dev->n_subdevices)
1312 return -EINVAL;
1313 s = dev->subdevices + arg;
1314 if (s->async == NULL)
1315 return -EINVAL;
1316
1317 if (s->lock && s->lock != file)
1318 return -EACCES;
1319
1320 if (!s->busy)
1321 return 0;
1322
1323 if (s->busy != file)
1324 return -EBUSY;
1325
1326 return do_cancel(dev, s);
1327}
1328
1329/*
1330 COMEDI_POLL ioctl
1331 instructs driver to synchronize buffers
1332
1333 arg:
1334 subdevice number
1335
1336 reads:
1337 nothing
1338
1339 writes:
1340 nothing
1341
1342*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301343static int do_poll_ioctl(struct comedi_device *dev, unsigned int arg,
1344 void *file)
David Schleefed9eccb2008-11-04 20:29:31 -08001345{
Bill Pemberton34c43922009-03-16 22:05:14 -04001346 struct comedi_subdevice *s;
David Schleefed9eccb2008-11-04 20:29:31 -08001347
1348 if (arg >= dev->n_subdevices)
1349 return -EINVAL;
1350 s = dev->subdevices + arg;
1351
1352 if (s->lock && s->lock != file)
1353 return -EACCES;
1354
1355 if (!s->busy)
1356 return 0;
1357
1358 if (s->busy != file)
1359 return -EBUSY;
1360
1361 if (s->poll)
1362 return s->poll(dev, s);
1363
1364 return -EINVAL;
1365}
1366
Bill Pemberton34c43922009-03-16 22:05:14 -04001367static int do_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
David Schleefed9eccb2008-11-04 20:29:31 -08001368{
1369 int ret = 0;
1370
1371 if ((comedi_get_subdevice_runflags(s) & SRF_RUNNING) && s->cancel)
1372 ret = s->cancel(dev, s);
1373
1374 do_become_nonbusy(dev, s);
1375
1376 return ret;
1377}
1378
1379void comedi_unmap(struct vm_area_struct *area)
1380{
Bill Pembertond1636792009-03-16 22:05:20 -04001381 struct comedi_async *async;
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04001382 struct comedi_device *dev;
David Schleefed9eccb2008-11-04 20:29:31 -08001383
1384 async = area->vm_private_data;
1385 dev = async->subdevice->device;
1386
1387 mutex_lock(&dev->mutex);
1388 async->mmap_count--;
1389 mutex_unlock(&dev->mutex);
1390}
1391
1392static struct vm_operations_struct comedi_vm_ops = {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301393 .close = comedi_unmap,
David Schleefed9eccb2008-11-04 20:29:31 -08001394};
1395
1396static int comedi_mmap(struct file *file, struct vm_area_struct *vma)
1397{
1398 const unsigned minor = iminor(file->f_dentry->d_inode);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001399 struct comedi_device_file_info *dev_file_info =
1400 comedi_get_device_file_info(minor);
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04001401 struct comedi_device *dev = dev_file_info->device;
Bill Pembertond1636792009-03-16 22:05:20 -04001402 struct comedi_async *async = NULL;
David Schleefed9eccb2008-11-04 20:29:31 -08001403 unsigned long start = vma->vm_start;
1404 unsigned long size;
1405 int n_pages;
1406 int i;
1407 int retval;
Bill Pemberton34c43922009-03-16 22:05:14 -04001408 struct comedi_subdevice *s;
David Schleefed9eccb2008-11-04 20:29:31 -08001409
1410 mutex_lock(&dev->mutex);
1411 if (!dev->attached) {
1412 DPRINTK("no driver configured on comedi%i\n", dev->minor);
1413 retval = -ENODEV;
1414 goto done;
1415 }
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001416 if (vma->vm_flags & VM_WRITE)
David Schleefed9eccb2008-11-04 20:29:31 -08001417 s = comedi_get_write_subdevice(dev_file_info);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001418 else
David Schleefed9eccb2008-11-04 20:29:31 -08001419 s = comedi_get_read_subdevice(dev_file_info);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001420
David Schleefed9eccb2008-11-04 20:29:31 -08001421 if (s == NULL) {
1422 retval = -EINVAL;
1423 goto done;
1424 }
1425 async = s->async;
1426 if (async == NULL) {
1427 retval = -EINVAL;
1428 goto done;
1429 }
1430
1431 if (vma->vm_pgoff != 0) {
1432 DPRINTK("comedi: mmap() offset must be 0.\n");
1433 retval = -EINVAL;
1434 goto done;
1435 }
1436
1437 size = vma->vm_end - vma->vm_start;
1438 if (size > async->prealloc_bufsz) {
1439 retval = -EFAULT;
1440 goto done;
1441 }
1442 if (size & (~PAGE_MASK)) {
1443 retval = -EFAULT;
1444 goto done;
1445 }
1446
1447 n_pages = size >> PAGE_SHIFT;
1448 for (i = 0; i < n_pages; ++i) {
1449 if (remap_pfn_range(vma, start,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301450 page_to_pfn(virt_to_page
1451 (async->buf_page_list
1452 [i].virt_addr)), PAGE_SIZE,
1453 PAGE_SHARED)) {
David Schleefed9eccb2008-11-04 20:29:31 -08001454 retval = -EAGAIN;
1455 goto done;
1456 }
1457 start += PAGE_SIZE;
1458 }
1459
1460 vma->vm_ops = &comedi_vm_ops;
1461 vma->vm_private_data = async;
1462
1463 async->mmap_count++;
1464
1465 retval = 0;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001466done:
David Schleefed9eccb2008-11-04 20:29:31 -08001467 mutex_unlock(&dev->mutex);
1468 return retval;
1469}
1470
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301471static unsigned int comedi_poll(struct file *file, poll_table * wait)
David Schleefed9eccb2008-11-04 20:29:31 -08001472{
1473 unsigned int mask = 0;
1474 const unsigned minor = iminor(file->f_dentry->d_inode);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001475 struct comedi_device_file_info *dev_file_info =
1476 comedi_get_device_file_info(minor);
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04001477 struct comedi_device *dev = dev_file_info->device;
Bill Pemberton34c43922009-03-16 22:05:14 -04001478 struct comedi_subdevice *read_subdev;
1479 struct comedi_subdevice *write_subdev;
David Schleefed9eccb2008-11-04 20:29:31 -08001480
1481 mutex_lock(&dev->mutex);
1482 if (!dev->attached) {
1483 DPRINTK("no driver configured on comedi%i\n", dev->minor);
1484 mutex_unlock(&dev->mutex);
1485 return 0;
1486 }
1487
1488 mask = 0;
1489 read_subdev = comedi_get_read_subdevice(dev_file_info);
1490 if (read_subdev) {
1491 poll_wait(file, &read_subdev->async->wait_head, wait);
1492 if (!read_subdev->busy
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001493 || comedi_buf_read_n_available(read_subdev->async) > 0
1494 || !(comedi_get_subdevice_runflags(read_subdev) &
1495 SRF_RUNNING)) {
David Schleefed9eccb2008-11-04 20:29:31 -08001496 mask |= POLLIN | POLLRDNORM;
1497 }
1498 }
1499 write_subdev = comedi_get_write_subdevice(dev_file_info);
1500 if (write_subdev) {
1501 poll_wait(file, &write_subdev->async->wait_head, wait);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001502 comedi_buf_write_alloc(write_subdev->async,
1503 write_subdev->async->prealloc_bufsz);
David Schleefed9eccb2008-11-04 20:29:31 -08001504 if (!write_subdev->busy
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001505 || !(comedi_get_subdevice_runflags(write_subdev) &
1506 SRF_RUNNING)
1507 || comedi_buf_write_n_allocated(write_subdev->async) >=
1508 bytes_per_sample(write_subdev->async->subdevice)) {
David Schleefed9eccb2008-11-04 20:29:31 -08001509 mask |= POLLOUT | POLLWRNORM;
1510 }
1511 }
1512
1513 mutex_unlock(&dev->mutex);
1514 return mask;
1515}
1516
1517static ssize_t comedi_write(struct file *file, const char *buf, size_t nbytes,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301518 loff_t * offset)
David Schleefed9eccb2008-11-04 20:29:31 -08001519{
Bill Pemberton34c43922009-03-16 22:05:14 -04001520 struct comedi_subdevice *s;
Bill Pembertond1636792009-03-16 22:05:20 -04001521 struct comedi_async *async;
David Schleefed9eccb2008-11-04 20:29:31 -08001522 int n, m, count = 0, retval = 0;
1523 DECLARE_WAITQUEUE(wait, current);
1524 const unsigned minor = iminor(file->f_dentry->d_inode);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001525 struct comedi_device_file_info *dev_file_info =
1526 comedi_get_device_file_info(minor);
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04001527 struct comedi_device *dev = dev_file_info->device;
David Schleefed9eccb2008-11-04 20:29:31 -08001528
1529 if (!dev->attached) {
1530 DPRINTK("no driver configured on comedi%i\n", dev->minor);
1531 retval = -ENODEV;
1532 goto done;
1533 }
1534
1535 s = comedi_get_write_subdevice(dev_file_info);
1536 if (s == NULL) {
1537 retval = -EIO;
1538 goto done;
1539 }
1540 async = s->async;
1541
1542 if (!nbytes) {
1543 retval = 0;
1544 goto done;
1545 }
1546 if (!s->busy) {
1547 retval = 0;
1548 goto done;
1549 }
1550 if (s->busy != file) {
1551 retval = -EACCES;
1552 goto done;
1553 }
1554 add_wait_queue(&async->wait_head, &wait);
1555 while (nbytes > 0 && !retval) {
1556 set_current_state(TASK_INTERRUPTIBLE);
1557
1558 n = nbytes;
1559
1560 m = n;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001561 if (async->buf_write_ptr + m > async->prealloc_bufsz)
David Schleefed9eccb2008-11-04 20:29:31 -08001562 m = async->prealloc_bufsz - async->buf_write_ptr;
David Schleefed9eccb2008-11-04 20:29:31 -08001563 comedi_buf_write_alloc(async, async->prealloc_bufsz);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001564 if (m > comedi_buf_write_n_allocated(async))
David Schleefed9eccb2008-11-04 20:29:31 -08001565 m = comedi_buf_write_n_allocated(async);
David Schleefed9eccb2008-11-04 20:29:31 -08001566 if (m < n)
1567 n = m;
1568
1569 if (n == 0) {
1570 if (!(comedi_get_subdevice_runflags(s) & SRF_RUNNING)) {
1571 if (comedi_get_subdevice_runflags(s) &
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001572 SRF_ERROR) {
David Schleefed9eccb2008-11-04 20:29:31 -08001573 retval = -EPIPE;
1574 } else {
1575 retval = 0;
1576 }
1577 do_become_nonbusy(dev, s);
1578 break;
1579 }
1580 if (file->f_flags & O_NONBLOCK) {
1581 retval = -EAGAIN;
1582 break;
1583 }
1584 if (signal_pending(current)) {
1585 retval = -ERESTARTSYS;
1586 break;
1587 }
1588 schedule();
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001589 if (!s->busy)
David Schleefed9eccb2008-11-04 20:29:31 -08001590 break;
David Schleefed9eccb2008-11-04 20:29:31 -08001591 if (s->busy != file) {
1592 retval = -EACCES;
1593 break;
1594 }
1595 continue;
1596 }
1597
1598 m = copy_from_user(async->prealloc_buf + async->buf_write_ptr,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001599 buf, n);
David Schleefed9eccb2008-11-04 20:29:31 -08001600 if (m) {
1601 n -= m;
1602 retval = -EFAULT;
1603 }
1604 comedi_buf_write_free(async, n);
1605
1606 count += n;
1607 nbytes -= n;
1608
1609 buf += n;
1610 break; /* makes device work like a pipe */
1611 }
1612 set_current_state(TASK_RUNNING);
1613 remove_wait_queue(&async->wait_head, &wait);
1614
1615done:
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001616 return count ? count : retval;
David Schleefed9eccb2008-11-04 20:29:31 -08001617}
1618
1619static ssize_t comedi_read(struct file *file, char *buf, size_t nbytes,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301620 loff_t * offset)
David Schleefed9eccb2008-11-04 20:29:31 -08001621{
Bill Pemberton34c43922009-03-16 22:05:14 -04001622 struct comedi_subdevice *s;
Bill Pembertond1636792009-03-16 22:05:20 -04001623 struct comedi_async *async;
David Schleefed9eccb2008-11-04 20:29:31 -08001624 int n, m, count = 0, retval = 0;
1625 DECLARE_WAITQUEUE(wait, current);
1626 const unsigned minor = iminor(file->f_dentry->d_inode);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001627 struct comedi_device_file_info *dev_file_info =
1628 comedi_get_device_file_info(minor);
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04001629 struct comedi_device *dev = dev_file_info->device;
David Schleefed9eccb2008-11-04 20:29:31 -08001630
1631 if (!dev->attached) {
1632 DPRINTK("no driver configured on comedi%i\n", dev->minor);
1633 retval = -ENODEV;
1634 goto done;
1635 }
1636
1637 s = comedi_get_read_subdevice(dev_file_info);
1638 if (s == NULL) {
1639 retval = -EIO;
1640 goto done;
1641 }
1642 async = s->async;
1643 if (!nbytes) {
1644 retval = 0;
1645 goto done;
1646 }
1647 if (!s->busy) {
1648 retval = 0;
1649 goto done;
1650 }
1651 if (s->busy != file) {
1652 retval = -EACCES;
1653 goto done;
1654 }
1655
1656 add_wait_queue(&async->wait_head, &wait);
1657 while (nbytes > 0 && !retval) {
1658 set_current_state(TASK_INTERRUPTIBLE);
1659
1660 n = nbytes;
1661
1662 m = comedi_buf_read_n_available(async);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001663 /* printk("%d available\n",m); */
1664 if (async->buf_read_ptr + m > async->prealloc_bufsz)
David Schleefed9eccb2008-11-04 20:29:31 -08001665 m = async->prealloc_bufsz - async->buf_read_ptr;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001666 /* printk("%d contiguous\n",m); */
David Schleefed9eccb2008-11-04 20:29:31 -08001667 if (m < n)
1668 n = m;
1669
1670 if (n == 0) {
1671 if (!(comedi_get_subdevice_runflags(s) & SRF_RUNNING)) {
1672 do_become_nonbusy(dev, s);
1673 if (comedi_get_subdevice_runflags(s) &
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001674 SRF_ERROR) {
David Schleefed9eccb2008-11-04 20:29:31 -08001675 retval = -EPIPE;
1676 } else {
1677 retval = 0;
1678 }
1679 break;
1680 }
1681 if (file->f_flags & O_NONBLOCK) {
1682 retval = -EAGAIN;
1683 break;
1684 }
1685 if (signal_pending(current)) {
1686 retval = -ERESTARTSYS;
1687 break;
1688 }
1689 schedule();
1690 if (!s->busy) {
1691 retval = 0;
1692 break;
1693 }
1694 if (s->busy != file) {
1695 retval = -EACCES;
1696 break;
1697 }
1698 continue;
1699 }
1700 m = copy_to_user(buf, async->prealloc_buf +
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001701 async->buf_read_ptr, n);
David Schleefed9eccb2008-11-04 20:29:31 -08001702 if (m) {
1703 n -= m;
1704 retval = -EFAULT;
1705 }
1706
1707 comedi_buf_read_alloc(async, n);
1708 comedi_buf_read_free(async, n);
1709
1710 count += n;
1711 nbytes -= n;
1712
1713 buf += n;
1714 break; /* makes device work like a pipe */
1715 }
1716 if (!(comedi_get_subdevice_runflags(s) & (SRF_ERROR | SRF_RUNNING)) &&
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001717 async->buf_read_count - async->buf_write_count == 0) {
David Schleefed9eccb2008-11-04 20:29:31 -08001718 do_become_nonbusy(dev, s);
1719 }
1720 set_current_state(TASK_RUNNING);
1721 remove_wait_queue(&async->wait_head, &wait);
1722
1723done:
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001724 return count ? count : retval;
David Schleefed9eccb2008-11-04 20:29:31 -08001725}
1726
1727/*
1728 This function restores a subdevice to an idle state.
1729 */
Bill Pemberton34c43922009-03-16 22:05:14 -04001730void do_become_nonbusy(struct comedi_device *dev, struct comedi_subdevice *s)
David Schleefed9eccb2008-11-04 20:29:31 -08001731{
Bill Pembertond1636792009-03-16 22:05:20 -04001732 struct comedi_async *async = s->async;
David Schleefed9eccb2008-11-04 20:29:31 -08001733
1734 comedi_set_subdevice_runflags(s, SRF_RUNNING, 0);
David Schleefed9eccb2008-11-04 20:29:31 -08001735 if (async) {
1736 comedi_reset_async_buf(async);
1737 async->inttrig = NULL;
1738 } else {
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001739 printk(KERN_ERR
1740 "BUG: (?) do_become_nonbusy called with async=0\n");
David Schleefed9eccb2008-11-04 20:29:31 -08001741 }
1742
1743 s->busy = NULL;
1744}
1745
1746static int comedi_open(struct inode *inode, struct file *file)
1747{
David Schleefed9eccb2008-11-04 20:29:31 -08001748 const unsigned minor = iminor(inode);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001749 struct comedi_device_file_info *dev_file_info =
1750 comedi_get_device_file_info(minor);
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301751 struct comedi_device *dev =
1752 dev_file_info ? dev_file_info->device : NULL;
Ian Abbott97920072009-02-09 16:51:38 +00001753
David Schleefed9eccb2008-11-04 20:29:31 -08001754 if (dev == NULL) {
1755 DPRINTK("invalid minor number\n");
1756 return -ENODEV;
1757 }
1758
1759 /* This is slightly hacky, but we want module autoloading
1760 * to work for root.
1761 * case: user opens device, attached -> ok
1762 * case: user opens device, unattached, in_request_module=0 -> autoload
1763 * case: user opens device, unattached, in_request_module=1 -> fail
1764 * case: root opens device, attached -> ok
1765 * case: root opens device, unattached, in_request_module=1 -> ok
1766 * (typically called from modprobe)
1767 * case: root opens device, unattached, in_request_module=0 -> autoload
1768 *
1769 * The last could be changed to "-> ok", which would deny root
1770 * autoloading.
1771 */
1772 mutex_lock(&dev->mutex);
1773 if (dev->attached)
1774 goto ok;
Eric Parisa8f80e82009-08-13 09:44:51 -04001775 if (!capable(CAP_NET_ADMIN) && dev->in_request_module) {
David Schleefed9eccb2008-11-04 20:29:31 -08001776 DPRINTK("in request module\n");
1777 mutex_unlock(&dev->mutex);
1778 return -ENODEV;
1779 }
Eric Parisa8f80e82009-08-13 09:44:51 -04001780 if (capable(CAP_NET_ADMIN) && dev->in_request_module)
David Schleefed9eccb2008-11-04 20:29:31 -08001781 goto ok;
1782
1783 dev->in_request_module = 1;
1784
David Schleefed9eccb2008-11-04 20:29:31 -08001785#ifdef CONFIG_KMOD
1786 mutex_unlock(&dev->mutex);
Ian Abbott56d92c62009-02-09 16:32:12 +00001787 request_module("char-major-%i-%i", COMEDI_MAJOR, dev->minor);
David Schleefed9eccb2008-11-04 20:29:31 -08001788 mutex_lock(&dev->mutex);
1789#endif
1790
1791 dev->in_request_module = 0;
1792
Eric Parisa8f80e82009-08-13 09:44:51 -04001793 if (!dev->attached && !capable(CAP_NET_ADMIN)) {
1794 DPRINTK("not attached and not CAP_NET_ADMIN\n");
David Schleefed9eccb2008-11-04 20:29:31 -08001795 mutex_unlock(&dev->mutex);
1796 return -ENODEV;
1797 }
1798ok:
1799 __module_get(THIS_MODULE);
1800
1801 if (dev->attached) {
1802 if (!try_module_get(dev->driver->module)) {
1803 module_put(THIS_MODULE);
1804 mutex_unlock(&dev->mutex);
1805 return -ENOSYS;
1806 }
1807 }
1808
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001809 if (dev->attached && dev->use_count == 0 && dev->open)
David Schleefed9eccb2008-11-04 20:29:31 -08001810 dev->open(dev);
David Schleefed9eccb2008-11-04 20:29:31 -08001811
1812 dev->use_count++;
1813
1814 mutex_unlock(&dev->mutex);
1815
1816 return 0;
1817}
1818
1819static int comedi_close(struct inode *inode, struct file *file)
1820{
1821 const unsigned minor = iminor(inode);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001822 struct comedi_device_file_info *dev_file_info =
1823 comedi_get_device_file_info(minor);
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04001824 struct comedi_device *dev = dev_file_info->device;
Bill Pemberton34c43922009-03-16 22:05:14 -04001825 struct comedi_subdevice *s = NULL;
David Schleefed9eccb2008-11-04 20:29:31 -08001826 int i;
1827
1828 mutex_lock(&dev->mutex);
1829
1830 if (dev->subdevices) {
1831 for (i = 0; i < dev->n_subdevices; i++) {
1832 s = dev->subdevices + i;
1833
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001834 if (s->busy == file)
David Schleefed9eccb2008-11-04 20:29:31 -08001835 do_cancel(dev, s);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001836 if (s->lock == file)
David Schleefed9eccb2008-11-04 20:29:31 -08001837 s->lock = NULL;
David Schleefed9eccb2008-11-04 20:29:31 -08001838 }
1839 }
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001840 if (dev->attached && dev->use_count == 1 && dev->close)
David Schleefed9eccb2008-11-04 20:29:31 -08001841 dev->close(dev);
David Schleefed9eccb2008-11-04 20:29:31 -08001842
1843 module_put(THIS_MODULE);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001844 if (dev->attached)
David Schleefed9eccb2008-11-04 20:29:31 -08001845 module_put(dev->driver->module);
David Schleefed9eccb2008-11-04 20:29:31 -08001846
1847 dev->use_count--;
1848
1849 mutex_unlock(&dev->mutex);
1850
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001851 if (file->f_flags & FASYNC)
David Schleefed9eccb2008-11-04 20:29:31 -08001852 comedi_fasync(-1, file, 0);
David Schleefed9eccb2008-11-04 20:29:31 -08001853
1854 return 0;
1855}
1856
1857static int comedi_fasync(int fd, struct file *file, int on)
1858{
1859 const unsigned minor = iminor(file->f_dentry->d_inode);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001860 struct comedi_device_file_info *dev_file_info =
1861 comedi_get_device_file_info(minor);
1862
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04001863 struct comedi_device *dev = dev_file_info->device;
David Schleefed9eccb2008-11-04 20:29:31 -08001864
1865 return fasync_helper(fd, file, on, &dev->async_queue);
1866}
1867
1868const struct file_operations comedi_fops = {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301869 .owner = THIS_MODULE,
David Schleefed9eccb2008-11-04 20:29:31 -08001870#ifdef HAVE_UNLOCKED_IOCTL
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301871 .unlocked_ioctl = comedi_unlocked_ioctl,
David Schleefed9eccb2008-11-04 20:29:31 -08001872#else
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301873 .ioctl = comedi_ioctl,
David Schleefed9eccb2008-11-04 20:29:31 -08001874#endif
1875#ifdef HAVE_COMPAT_IOCTL
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301876 .compat_ioctl = comedi_compat_ioctl,
David Schleefed9eccb2008-11-04 20:29:31 -08001877#endif
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301878 .open = comedi_open,
1879 .release = comedi_close,
1880 .read = comedi_read,
1881 .write = comedi_write,
1882 .mmap = comedi_mmap,
1883 .poll = comedi_poll,
1884 .fasync = comedi_fasync,
David Schleefed9eccb2008-11-04 20:29:31 -08001885};
1886
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001887struct class *comedi_class;
David Schleefed9eccb2008-11-04 20:29:31 -08001888static struct cdev comedi_cdev;
1889
1890static void comedi_cleanup_legacy_minors(void)
1891{
1892 unsigned i;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001893
Bernd Porr1dd33ab2008-12-08 23:30:13 +00001894 for (i = 0; i < comedi_num_legacy_minors; i++)
David Schleefed9eccb2008-11-04 20:29:31 -08001895 comedi_free_board_minor(i);
David Schleefed9eccb2008-11-04 20:29:31 -08001896}
1897
1898static int __init comedi_init(void)
1899{
1900 int i;
1901 int retval;
1902
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001903 printk(KERN_INFO "comedi: version " COMEDI_RELEASE
1904 " - http://www.comedi.org\n");
David Schleefed9eccb2008-11-04 20:29:31 -08001905
Frank Mori Hessa3cb7292008-12-15 13:44:45 +00001906 if (comedi_num_legacy_minors < 0 ||
1907 comedi_num_legacy_minors > COMEDI_NUM_BOARD_MINORS) {
1908 printk(KERN_ERR "comedi: error: invalid value for module "
1909 "parameter \"comedi_num_legacy_minors\". Valid values "
1910 "are 0 through %i.\n", COMEDI_NUM_BOARD_MINORS);
1911 return -EINVAL;
1912 }
1913
1914 /*
1915 * comedi is unusable if both comedi_autoconfig and
1916 * comedi_num_legacy_minors are zero, so we might as well adjust the
1917 * defaults in that case
1918 */
1919 if (comedi_autoconfig == 0 && comedi_num_legacy_minors == 0)
1920 comedi_num_legacy_minors = 16;
1921
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001922 memset(comedi_file_info_table, 0,
1923 sizeof(struct comedi_device_file_info *) * COMEDI_NUM_MINORS);
David Schleefed9eccb2008-11-04 20:29:31 -08001924
1925 retval = register_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001926 COMEDI_NUM_MINORS, "comedi");
David Schleefed9eccb2008-11-04 20:29:31 -08001927 if (retval)
1928 return -EIO;
1929 cdev_init(&comedi_cdev, &comedi_fops);
1930 comedi_cdev.owner = THIS_MODULE;
1931 kobject_set_name(&comedi_cdev.kobj, "comedi");
1932 if (cdev_add(&comedi_cdev, MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS)) {
1933 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001934 COMEDI_NUM_MINORS);
David Schleefed9eccb2008-11-04 20:29:31 -08001935 return -EIO;
1936 }
1937 comedi_class = class_create(THIS_MODULE, "comedi");
1938 if (IS_ERR(comedi_class)) {
1939 printk("comedi: failed to create class");
1940 cdev_del(&comedi_cdev);
1941 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001942 COMEDI_NUM_MINORS);
David Schleefed9eccb2008-11-04 20:29:31 -08001943 return PTR_ERR(comedi_class);
1944 }
1945
1946 /* XXX requires /proc interface */
1947 comedi_proc_init();
1948
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001949 /* create devices files for legacy/manual use */
Bernd Porr1dd33ab2008-12-08 23:30:13 +00001950 for (i = 0; i < comedi_num_legacy_minors; i++) {
David Schleefed9eccb2008-11-04 20:29:31 -08001951 int minor;
1952 minor = comedi_alloc_board_minor(NULL);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001953 if (minor < 0) {
David Schleefed9eccb2008-11-04 20:29:31 -08001954 comedi_cleanup_legacy_minors();
1955 cdev_del(&comedi_cdev);
1956 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001957 COMEDI_NUM_MINORS);
David Schleefed9eccb2008-11-04 20:29:31 -08001958 return minor;
1959 }
1960 }
1961
David Schleefed9eccb2008-11-04 20:29:31 -08001962 comedi_register_ioctl32();
1963
1964 return 0;
1965}
1966
1967static void __exit comedi_cleanup(void)
1968{
1969 int i;
1970
1971 comedi_cleanup_legacy_minors();
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001972 for (i = 0; i < COMEDI_NUM_MINORS; ++i)
David Schleefed9eccb2008-11-04 20:29:31 -08001973 BUG_ON(comedi_file_info_table[i]);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001974
David Schleefed9eccb2008-11-04 20:29:31 -08001975 class_destroy(comedi_class);
1976 cdev_del(&comedi_cdev);
1977 unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS);
1978
1979 comedi_proc_cleanup();
1980
David Schleefed9eccb2008-11-04 20:29:31 -08001981 comedi_unregister_ioctl32();
1982}
1983
1984module_init(comedi_init);
1985module_exit(comedi_cleanup);
1986
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04001987void comedi_error(const struct comedi_device *dev, const char *s)
David Schleefed9eccb2008-11-04 20:29:31 -08001988{
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001989 printk("comedi%d: %s: %s\n", dev->minor, dev->driver->driver_name, s);
David Schleefed9eccb2008-11-04 20:29:31 -08001990}
1991
Bill Pemberton34c43922009-03-16 22:05:14 -04001992void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s)
David Schleefed9eccb2008-11-04 20:29:31 -08001993{
Bill Pembertond1636792009-03-16 22:05:20 -04001994 struct comedi_async *async = s->async;
David Schleefed9eccb2008-11-04 20:29:31 -08001995 unsigned runflags = 0;
1996 unsigned runflags_mask = 0;
1997
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08001998 /* DPRINTK("comedi_event 0x%x\n",mask); */
David Schleefed9eccb2008-11-04 20:29:31 -08001999
2000 if ((comedi_get_subdevice_runflags(s) & SRF_RUNNING) == 0)
2001 return;
2002
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302003 if (s->
2004 async->events & (COMEDI_CB_EOA | COMEDI_CB_ERROR |
2005 COMEDI_CB_OVERFLOW)) {
David Schleefed9eccb2008-11-04 20:29:31 -08002006 runflags_mask |= SRF_RUNNING;
2007 }
2008 /* remember if an error event has occured, so an error
2009 * can be returned the next time the user does a read() */
2010 if (s->async->events & (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)) {
2011 runflags_mask |= SRF_ERROR;
2012 runflags |= SRF_ERROR;
2013 }
2014 if (runflags_mask) {
2015 /*sets SRF_ERROR and SRF_RUNNING together atomically */
2016 comedi_set_subdevice_runflags(s, runflags_mask, runflags);
2017 }
2018
2019 if (async->cb_mask & s->async->events) {
2020 if (comedi_get_subdevice_runflags(s) & SRF_USER) {
Greg Kroah-Hartmanfcea1152009-04-27 15:15:30 -07002021 wake_up_interruptible(&async->wait_head);
2022 if (s->subdev_flags & SDF_CMD_READ) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302023 kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
Greg Kroah-Hartmanfcea1152009-04-27 15:15:30 -07002024 }
2025 if (s->subdev_flags & SDF_CMD_WRITE) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302026 kill_fasync(&dev->async_queue, SIGIO, POLL_OUT);
David Schleefed9eccb2008-11-04 20:29:31 -08002027 }
2028 } else {
2029 if (async->cb_func)
2030 async->cb_func(s->async->events, async->cb_arg);
David Schleefed9eccb2008-11-04 20:29:31 -08002031 }
2032 }
2033 s->async->events = 0;
2034}
2035
Bill Pemberton34c43922009-03-16 22:05:14 -04002036void comedi_set_subdevice_runflags(struct comedi_subdevice *s, unsigned mask,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002037 unsigned bits)
David Schleefed9eccb2008-11-04 20:29:31 -08002038{
2039 unsigned long flags;
2040
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002041 spin_lock_irqsave(&s->spin_lock, flags);
David Schleefed9eccb2008-11-04 20:29:31 -08002042 s->runflags &= ~mask;
2043 s->runflags |= (bits & mask);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002044 spin_unlock_irqrestore(&s->spin_lock, flags);
David Schleefed9eccb2008-11-04 20:29:31 -08002045}
2046
Bill Pemberton34c43922009-03-16 22:05:14 -04002047unsigned comedi_get_subdevice_runflags(struct comedi_subdevice *s)
David Schleefed9eccb2008-11-04 20:29:31 -08002048{
2049 unsigned long flags;
2050 unsigned runflags;
2051
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002052 spin_lock_irqsave(&s->spin_lock, flags);
David Schleefed9eccb2008-11-04 20:29:31 -08002053 runflags = s->runflags;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002054 spin_unlock_irqrestore(&s->spin_lock, flags);
David Schleefed9eccb2008-11-04 20:29:31 -08002055 return runflags;
2056}
2057
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04002058static int is_device_busy(struct comedi_device *dev)
David Schleefed9eccb2008-11-04 20:29:31 -08002059{
Bill Pemberton34c43922009-03-16 22:05:14 -04002060 struct comedi_subdevice *s;
David Schleefed9eccb2008-11-04 20:29:31 -08002061 int i;
2062
2063 if (!dev->attached)
2064 return 0;
2065
2066 for (i = 0; i < dev->n_subdevices; i++) {
2067 s = dev->subdevices + i;
2068 if (s->busy)
2069 return 1;
2070 if (s->async && s->async->mmap_count)
2071 return 1;
2072 }
2073
2074 return 0;
2075}
2076
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04002077void comedi_device_init(struct comedi_device *dev)
David Schleefed9eccb2008-11-04 20:29:31 -08002078{
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04002079 memset(dev, 0, sizeof(struct comedi_device));
David Schleefed9eccb2008-11-04 20:29:31 -08002080 spin_lock_init(&dev->spinlock);
2081 mutex_init(&dev->mutex);
2082 dev->minor = -1;
2083}
2084
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04002085void comedi_device_cleanup(struct comedi_device *dev)
David Schleefed9eccb2008-11-04 20:29:31 -08002086{
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002087 if (dev == NULL)
2088 return;
David Schleefed9eccb2008-11-04 20:29:31 -08002089 mutex_lock(&dev->mutex);
2090 comedi_device_detach(dev);
2091 mutex_unlock(&dev->mutex);
2092 mutex_destroy(&dev->mutex);
2093}
2094
2095int comedi_alloc_board_minor(struct device *hardware_device)
2096{
2097 unsigned long flags;
2098 struct comedi_device_file_info *info;
Bill Pemberton0bfbbe82009-03-16 22:05:36 -04002099 struct device *csdev;
David Schleefed9eccb2008-11-04 20:29:31 -08002100 unsigned i;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002101 int retval;
David Schleefed9eccb2008-11-04 20:29:31 -08002102
2103 info = kzalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002104 if (info == NULL)
2105 return -ENOMEM;
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04002106 info->device = kzalloc(sizeof(struct comedi_device), GFP_KERNEL);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002107 if (info->device == NULL) {
David Schleefed9eccb2008-11-04 20:29:31 -08002108 kfree(info);
2109 return -ENOMEM;
2110 }
2111 comedi_device_init(info->device);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002112 spin_lock_irqsave(&comedi_file_info_table_lock, flags);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002113 for (i = 0; i < COMEDI_NUM_BOARD_MINORS; ++i) {
2114 if (comedi_file_info_table[i] == NULL) {
David Schleefed9eccb2008-11-04 20:29:31 -08002115 comedi_file_info_table[i] = info;
2116 break;
2117 }
2118 }
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002119 spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002120 if (i == COMEDI_NUM_BOARD_MINORS) {
David Schleefed9eccb2008-11-04 20:29:31 -08002121 comedi_device_cleanup(info->device);
2122 kfree(info->device);
2123 kfree(info);
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302124 printk(KERN_ERR
2125 "comedi: error: ran out of minor numbers for board device files.\n");
David Schleefed9eccb2008-11-04 20:29:31 -08002126 return -EBUSY;
2127 }
2128 info->device->minor = i;
2129 csdev = COMEDI_DEVICE_CREATE(comedi_class, NULL,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002130 MKDEV(COMEDI_MAJOR, i), NULL,
2131 hardware_device, "comedi%i", i);
2132 if (!IS_ERR(csdev))
David Schleefed9eccb2008-11-04 20:29:31 -08002133 info->device->class_dev = csdev;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002134 dev_set_drvdata(csdev, info);
2135 retval = device_create_file(csdev, &dev_attr_max_read_buffer_kb);
2136 if (retval) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302137 printk(KERN_ERR
2138 "comedi: failed to create sysfs attribute file \"%s\".\n",
2139 dev_attr_max_read_buffer_kb.attr.name);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002140 comedi_free_board_minor(i);
2141 return retval;
2142 }
2143 retval = device_create_file(csdev, &dev_attr_read_buffer_kb);
2144 if (retval) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302145 printk(KERN_ERR
2146 "comedi: failed to create sysfs attribute file \"%s\".\n",
2147 dev_attr_read_buffer_kb.attr.name);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002148 comedi_free_board_minor(i);
2149 return retval;
2150 }
2151 retval = device_create_file(csdev, &dev_attr_max_write_buffer_kb);
2152 if (retval) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302153 printk(KERN_ERR
2154 "comedi: failed to create sysfs attribute file \"%s\".\n",
2155 dev_attr_max_write_buffer_kb.attr.name);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002156 comedi_free_board_minor(i);
2157 return retval;
2158 }
2159 retval = device_create_file(csdev, &dev_attr_write_buffer_kb);
2160 if (retval) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302161 printk(KERN_ERR
2162 "comedi: failed to create sysfs attribute file \"%s\".\n",
2163 dev_attr_write_buffer_kb.attr.name);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002164 comedi_free_board_minor(i);
2165 return retval;
2166 }
David Schleefed9eccb2008-11-04 20:29:31 -08002167 return i;
2168}
2169
2170void comedi_free_board_minor(unsigned minor)
2171{
2172 unsigned long flags;
2173 struct comedi_device_file_info *info;
2174
2175 BUG_ON(minor >= COMEDI_NUM_BOARD_MINORS);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002176 spin_lock_irqsave(&comedi_file_info_table_lock, flags);
David Schleefed9eccb2008-11-04 20:29:31 -08002177 info = comedi_file_info_table[minor];
2178 comedi_file_info_table[minor] = NULL;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002179 spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
David Schleefed9eccb2008-11-04 20:29:31 -08002180
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002181 if (info) {
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04002182 struct comedi_device *dev = info->device;
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002183 if (dev) {
2184 if (dev->class_dev) {
2185 device_destroy(comedi_class,
2186 MKDEV(COMEDI_MAJOR, dev->minor));
David Schleefed9eccb2008-11-04 20:29:31 -08002187 }
2188 comedi_device_cleanup(dev);
2189 kfree(dev);
2190 }
2191 kfree(info);
2192 }
2193}
2194
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002195int comedi_alloc_subdevice_minor(struct comedi_device *dev,
2196 struct comedi_subdevice *s)
David Schleefed9eccb2008-11-04 20:29:31 -08002197{
2198 unsigned long flags;
2199 struct comedi_device_file_info *info;
Bill Pemberton0bfbbe82009-03-16 22:05:36 -04002200 struct device *csdev;
David Schleefed9eccb2008-11-04 20:29:31 -08002201 unsigned i;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002202 int retval;
David Schleefed9eccb2008-11-04 20:29:31 -08002203
2204 info = kmalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002205 if (info == NULL)
2206 return -ENOMEM;
David Schleefed9eccb2008-11-04 20:29:31 -08002207 info->device = dev;
2208 info->read_subdevice = s;
2209 info->write_subdevice = s;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002210 spin_lock_irqsave(&comedi_file_info_table_lock, flags);
Frank Mori Hess4c41f3a2008-12-09 14:47:22 +00002211 for (i = COMEDI_FIRST_SUBDEVICE_MINOR; i < COMEDI_NUM_MINORS; ++i) {
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002212 if (comedi_file_info_table[i] == NULL) {
David Schleefed9eccb2008-11-04 20:29:31 -08002213 comedi_file_info_table[i] = info;
2214 break;
2215 }
2216 }
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002217 spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002218 if (i == COMEDI_NUM_MINORS) {
David Schleefed9eccb2008-11-04 20:29:31 -08002219 kfree(info);
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302220 printk(KERN_ERR
2221 "comedi: error: ran out of minor numbers for board device files.\n");
David Schleefed9eccb2008-11-04 20:29:31 -08002222 return -EBUSY;
2223 }
2224 s->minor = i;
2225 csdev = COMEDI_DEVICE_CREATE(comedi_class, dev->class_dev,
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002226 MKDEV(COMEDI_MAJOR, i), NULL, NULL,
2227 "comedi%i_subd%i", dev->minor,
2228 (int)(s - dev->subdevices));
2229 if (!IS_ERR(csdev))
David Schleefed9eccb2008-11-04 20:29:31 -08002230 s->class_dev = csdev;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002231 dev_set_drvdata(csdev, info);
2232 retval = device_create_file(csdev, &dev_attr_max_read_buffer_kb);
2233 if (retval) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302234 printk(KERN_ERR
2235 "comedi: failed to create sysfs attribute file \"%s\".\n",
2236 dev_attr_max_read_buffer_kb.attr.name);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002237 comedi_free_subdevice_minor(s);
2238 return retval;
2239 }
2240 retval = device_create_file(csdev, &dev_attr_read_buffer_kb);
2241 if (retval) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302242 printk(KERN_ERR
2243 "comedi: failed to create sysfs attribute file \"%s\".\n",
2244 dev_attr_read_buffer_kb.attr.name);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002245 comedi_free_subdevice_minor(s);
2246 return retval;
2247 }
2248 retval = device_create_file(csdev, &dev_attr_max_write_buffer_kb);
2249 if (retval) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302250 printk(KERN_ERR
2251 "comedi: failed to create sysfs attribute file \"%s\".\n",
2252 dev_attr_max_write_buffer_kb.attr.name);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002253 comedi_free_subdevice_minor(s);
2254 return retval;
2255 }
2256 retval = device_create_file(csdev, &dev_attr_write_buffer_kb);
2257 if (retval) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302258 printk(KERN_ERR
2259 "comedi: failed to create sysfs attribute file \"%s\".\n",
2260 dev_attr_write_buffer_kb.attr.name);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002261 comedi_free_subdevice_minor(s);
2262 return retval;
2263 }
David Schleefed9eccb2008-11-04 20:29:31 -08002264 return i;
2265}
2266
Bill Pemberton34c43922009-03-16 22:05:14 -04002267void comedi_free_subdevice_minor(struct comedi_subdevice *s)
David Schleefed9eccb2008-11-04 20:29:31 -08002268{
2269 unsigned long flags;
2270 struct comedi_device_file_info *info;
2271
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002272 if (s == NULL)
2273 return;
2274 if (s->minor < 0)
2275 return;
David Schleefed9eccb2008-11-04 20:29:31 -08002276
2277 BUG_ON(s->minor >= COMEDI_NUM_MINORS);
2278 BUG_ON(s->minor < COMEDI_FIRST_SUBDEVICE_MINOR);
2279
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002280 spin_lock_irqsave(&comedi_file_info_table_lock, flags);
David Schleefed9eccb2008-11-04 20:29:31 -08002281 info = comedi_file_info_table[s->minor];
2282 comedi_file_info_table[s->minor] = NULL;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002283 spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
David Schleefed9eccb2008-11-04 20:29:31 -08002284
Greg Kroah-Hartman476b8472008-11-13 17:05:58 -08002285 if (s->class_dev) {
David Schleefed9eccb2008-11-04 20:29:31 -08002286 device_destroy(comedi_class, MKDEV(COMEDI_MAJOR, s->minor));
2287 s->class_dev = NULL;
2288 }
2289 kfree(info);
2290}
2291
2292struct comedi_device_file_info *comedi_get_device_file_info(unsigned minor)
2293{
2294 unsigned long flags;
2295 struct comedi_device_file_info *info;
2296
2297 BUG_ON(minor >= COMEDI_NUM_MINORS);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002298 spin_lock_irqsave(&comedi_file_info_table_lock, flags);
David Schleefed9eccb2008-11-04 20:29:31 -08002299 info = comedi_file_info_table[minor];
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07002300 spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
David Schleefed9eccb2008-11-04 20:29:31 -08002301 return info;
2302}
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002303
2304static int resize_async_buffer(struct comedi_device *dev,
2305 struct comedi_subdevice *s,
2306 struct comedi_async *async, unsigned new_size)
2307{
2308 int retval;
2309
2310 if (new_size > async->max_bufsize)
2311 return -EPERM;
2312
2313 if (s->busy) {
2314 DPRINTK("subdevice is busy, cannot resize buffer\n");
2315 return -EBUSY;
2316 }
2317 if (async->mmap_count) {
2318 DPRINTK("subdevice is mmapped, cannot resize buffer\n");
2319 return -EBUSY;
2320 }
2321
2322 if (!async->prealloc_buf)
2323 return -EINVAL;
2324
2325 /* make sure buffer is an integral number of pages
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302326 * (we round up) */
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002327 new_size = (new_size + PAGE_SIZE - 1) & PAGE_MASK;
2328
2329 retval = comedi_buf_alloc(dev, s, new_size);
2330 if (retval < 0)
2331 return retval;
2332
2333 if (s->buf_change) {
2334 retval = s->buf_change(dev, s, new_size);
2335 if (retval < 0)
2336 return retval;
2337 }
2338
2339 DPRINTK("comedi%i subd %d buffer resized to %i bytes\n",
Ian Abbottb8b5cd92009-09-21 14:55:23 -04002340 dev->minor, (int)(s - dev->subdevices), async->prealloc_bufsz);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002341 return 0;
2342}
2343
2344/* sysfs attribute files */
2345
2346static const unsigned bytes_per_kibi = 1024;
2347
2348static ssize_t show_max_read_buffer_kb(struct device *dev,
2349 struct device_attribute *attr, char *buf)
2350{
2351 ssize_t retval;
2352 struct comedi_device_file_info *info = dev_get_drvdata(dev);
2353 unsigned max_buffer_size_kb = 0;
2354 struct comedi_subdevice *const read_subdevice =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302355 comedi_get_read_subdevice(info);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002356
2357 mutex_lock(&info->device->mutex);
2358 if (read_subdevice &&
2359 (read_subdevice->subdev_flags & SDF_CMD_READ) &&
2360 read_subdevice->async) {
2361 max_buffer_size_kb = read_subdevice->async->max_bufsize /
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302362 bytes_per_kibi;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002363 }
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302364 retval = snprintf(buf, PAGE_SIZE, "%i\n", max_buffer_size_kb);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002365 mutex_unlock(&info->device->mutex);
2366
2367 return retval;
2368}
2369
2370static ssize_t store_max_read_buffer_kb(struct device *dev,
2371 struct device_attribute *attr,
2372 const char *buf, size_t count)
2373{
2374 struct comedi_device_file_info *info = dev_get_drvdata(dev);
2375 unsigned long new_max_size_kb;
2376 uint64_t new_max_size;
2377 struct comedi_subdevice *const read_subdevice =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302378 comedi_get_read_subdevice(info);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002379
2380 if (strict_strtoul(buf, 10, &new_max_size_kb))
2381 return -EINVAL;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302382 if (new_max_size_kb != (uint32_t) new_max_size_kb)
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002383 return -EINVAL;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302384 new_max_size = ((uint64_t) new_max_size_kb) * bytes_per_kibi;
2385 if (new_max_size != (uint32_t) new_max_size)
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002386 return -EINVAL;
2387
2388 mutex_lock(&info->device->mutex);
2389 if (read_subdevice == NULL ||
2390 (read_subdevice->subdev_flags & SDF_CMD_READ) == 0 ||
2391 read_subdevice->async == NULL) {
2392 mutex_unlock(&info->device->mutex);
2393 return -EINVAL;
2394 }
2395 read_subdevice->async->max_bufsize = new_max_size;
2396 mutex_unlock(&info->device->mutex);
2397
2398 return count;
2399}
2400
2401static struct device_attribute dev_attr_max_read_buffer_kb = {
2402 .attr = {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302403 .name = "max_read_buffer_kb",
2404 .mode = S_IRUGO | S_IWUSR},
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002405 .show = &show_max_read_buffer_kb,
2406 .store = &store_max_read_buffer_kb
2407};
2408
2409static ssize_t show_read_buffer_kb(struct device *dev,
2410 struct device_attribute *attr, char *buf)
2411{
2412 ssize_t retval;
2413 struct comedi_device_file_info *info = dev_get_drvdata(dev);
2414 unsigned buffer_size_kb = 0;
2415 struct comedi_subdevice *const read_subdevice =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302416 comedi_get_read_subdevice(info);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002417
2418 mutex_lock(&info->device->mutex);
2419 if (read_subdevice &&
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302420 (read_subdevice->subdev_flags & SDF_CMD_READ) &&
2421 read_subdevice->async) {
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002422 buffer_size_kb = read_subdevice->async->prealloc_bufsz /
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302423 bytes_per_kibi;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002424 }
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302425 retval = snprintf(buf, PAGE_SIZE, "%i\n", buffer_size_kb);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002426 mutex_unlock(&info->device->mutex);
2427
2428 return retval;
2429}
2430
2431static ssize_t store_read_buffer_kb(struct device *dev,
2432 struct device_attribute *attr,
2433 const char *buf, size_t count)
2434{
2435 struct comedi_device_file_info *info = dev_get_drvdata(dev);
2436 unsigned long new_size_kb;
2437 uint64_t new_size;
2438 int retval;
2439 struct comedi_subdevice *const read_subdevice =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302440 comedi_get_read_subdevice(info);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002441
2442 if (strict_strtoul(buf, 10, &new_size_kb))
2443 return -EINVAL;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302444 if (new_size_kb != (uint32_t) new_size_kb)
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002445 return -EINVAL;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302446 new_size = ((uint64_t) new_size_kb) * bytes_per_kibi;
2447 if (new_size != (uint32_t) new_size)
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002448 return -EINVAL;
2449
2450 mutex_lock(&info->device->mutex);
2451 if (read_subdevice == NULL ||
2452 (read_subdevice->subdev_flags & SDF_CMD_READ) == 0 ||
2453 read_subdevice->async == NULL) {
2454 mutex_unlock(&info->device->mutex);
2455 return -EINVAL;
2456 }
2457 retval = resize_async_buffer(info->device, read_subdevice,
2458 read_subdevice->async, new_size);
2459 mutex_unlock(&info->device->mutex);
2460
2461 if (retval < 0)
2462 return retval;
2463 return count;
2464}
2465
2466static struct device_attribute dev_attr_read_buffer_kb = {
2467 .attr = {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302468 .name = "read_buffer_kb",
2469 .mode = S_IRUGO | S_IWUSR | S_IWGRP},
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002470 .show = &show_read_buffer_kb,
2471 .store = &store_read_buffer_kb
2472};
2473
2474static ssize_t show_max_write_buffer_kb(struct device *dev,
2475 struct device_attribute *attr,
2476 char *buf)
2477{
2478 ssize_t retval;
2479 struct comedi_device_file_info *info = dev_get_drvdata(dev);
2480 unsigned max_buffer_size_kb = 0;
2481 struct comedi_subdevice *const write_subdevice =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302482 comedi_get_write_subdevice(info);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002483
2484 mutex_lock(&info->device->mutex);
2485 if (write_subdevice &&
2486 (write_subdevice->subdev_flags & SDF_CMD_WRITE) &&
2487 write_subdevice->async) {
2488 max_buffer_size_kb = write_subdevice->async->max_bufsize /
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302489 bytes_per_kibi;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002490 }
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302491 retval = snprintf(buf, PAGE_SIZE, "%i\n", max_buffer_size_kb);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002492 mutex_unlock(&info->device->mutex);
2493
2494 return retval;
2495}
2496
2497static ssize_t store_max_write_buffer_kb(struct device *dev,
2498 struct device_attribute *attr,
2499 const char *buf, size_t count)
2500{
2501 struct comedi_device_file_info *info = dev_get_drvdata(dev);
2502 unsigned long new_max_size_kb;
2503 uint64_t new_max_size;
2504 struct comedi_subdevice *const write_subdevice =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302505 comedi_get_write_subdevice(info);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002506
2507 if (strict_strtoul(buf, 10, &new_max_size_kb))
2508 return -EINVAL;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302509 if (new_max_size_kb != (uint32_t) new_max_size_kb)
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002510 return -EINVAL;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302511 new_max_size = ((uint64_t) new_max_size_kb) * bytes_per_kibi;
2512 if (new_max_size != (uint32_t) new_max_size)
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002513 return -EINVAL;
2514
2515 mutex_lock(&info->device->mutex);
2516 if (write_subdevice == NULL ||
2517 (write_subdevice->subdev_flags & SDF_CMD_WRITE) == 0 ||
2518 write_subdevice->async == NULL) {
2519 mutex_unlock(&info->device->mutex);
2520 return -EINVAL;
2521 }
2522 write_subdevice->async->max_bufsize = new_max_size;
2523 mutex_unlock(&info->device->mutex);
2524
2525 return count;
2526}
2527
2528static struct device_attribute dev_attr_max_write_buffer_kb = {
2529 .attr = {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302530 .name = "max_write_buffer_kb",
2531 .mode = S_IRUGO | S_IWUSR},
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002532 .show = &show_max_write_buffer_kb,
2533 .store = &store_max_write_buffer_kb
2534};
2535
2536static ssize_t show_write_buffer_kb(struct device *dev,
2537 struct device_attribute *attr, char *buf)
2538{
2539 ssize_t retval;
2540 struct comedi_device_file_info *info = dev_get_drvdata(dev);
2541 unsigned buffer_size_kb = 0;
2542 struct comedi_subdevice *const write_subdevice =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302543 comedi_get_write_subdevice(info);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002544
2545 mutex_lock(&info->device->mutex);
2546 if (write_subdevice &&
2547 (write_subdevice->subdev_flags & SDF_CMD_WRITE) &&
2548 write_subdevice->async) {
2549 buffer_size_kb = write_subdevice->async->prealloc_bufsz /
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302550 bytes_per_kibi;
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002551 }
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302552 retval = snprintf(buf, PAGE_SIZE, "%i\n", buffer_size_kb);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002553 mutex_unlock(&info->device->mutex);
2554
2555 return retval;
2556}
2557
2558static ssize_t store_write_buffer_kb(struct device *dev,
2559 struct device_attribute *attr,
2560 const char *buf, size_t count)
2561{
2562 struct comedi_device_file_info *info = dev_get_drvdata(dev);
2563 unsigned long new_size_kb;
2564 uint64_t new_size;
2565 int retval;
2566 struct comedi_subdevice *const write_subdevice =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302567 comedi_get_write_subdevice(info);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002568
2569 if (strict_strtoul(buf, 10, &new_size_kb))
2570 return -EINVAL;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302571 if (new_size_kb != (uint32_t) new_size_kb)
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002572 return -EINVAL;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302573 new_size = ((uint64_t) new_size_kb) * bytes_per_kibi;
2574 if (new_size != (uint32_t) new_size)
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002575 return -EINVAL;
2576
2577 mutex_lock(&info->device->mutex);
2578 if (write_subdevice == NULL ||
2579 (write_subdevice->subdev_flags & SDF_CMD_WRITE) == 0 ||
2580 write_subdevice->async == NULL) {
2581 mutex_unlock(&info->device->mutex);
2582 return -EINVAL;
2583 }
2584 retval = resize_async_buffer(info->device, write_subdevice,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302585 write_subdevice->async, new_size);
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002586 mutex_unlock(&info->device->mutex);
2587
2588 if (retval < 0)
2589 return retval;
2590 return count;
2591}
2592
2593static struct device_attribute dev_attr_write_buffer_kb = {
2594 .attr = {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05302595 .name = "write_buffer_kb",
2596 .mode = S_IRUGO | S_IWUSR | S_IWGRP},
Frank Mori Hess883db3d2009-04-14 11:21:41 -04002597 .show = &show_write_buffer_kb,
2598 .store = &store_write_buffer_kb
2599};