blob: 288fef4fcbcc345788cc46e87c5cdd1de00b66cb [file] [log] [blame]
David Schleefb79a7a22008-11-14 15:58:23 -08001/*
2 kcomedilib/kcomedilib.c
3 a comedlib interface for kernel modules
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#define __NO_VERSION__
25#include <linux/module.h>
26
27#include <linux/errno.h>
28#include <linux/kernel.h>
29#include <linux/sched.h>
30#include <linux/fcntl.h>
31#include <linux/delay.h>
32#include <linux/ioport.h>
33#include <linux/mm.h>
David Schleefb79a7a22008-11-14 15:58:23 -080034#include <asm/io.h>
35
36#include "../comedi.h"
37#include "../comedilib.h"
38#include "../comedidev.h"
39
40MODULE_AUTHOR("David Schleef <ds@schleef.org>");
41MODULE_DESCRIPTION("Comedi kernel library");
42MODULE_LICENSE("GPL");
43
Bill Pemberton0b3fb272009-03-16 22:04:56 -040044void *comedi_open(const char *filename)
David Schleefb79a7a22008-11-14 15:58:23 -080045{
46 struct comedi_device_file_info *dev_file_info;
Bill Pemberton71b5f4f2009-03-16 22:05:08 -040047 struct comedi_device *dev;
David Schleefb79a7a22008-11-14 15:58:23 -080048 unsigned int minor;
49
50 if (strncmp(filename, "/dev/comedi", 11) != 0)
51 return NULL;
52
53 minor = simple_strtoul(filename + 11, NULL, 0);
54
55 if (minor >= COMEDI_NUM_BOARD_MINORS)
56 return NULL;
57
58 dev_file_info = comedi_get_device_file_info(minor);
Bill Pemberton6a98d362009-03-16 22:03:45 -040059 if (dev_file_info == NULL)
David Schleefb79a7a22008-11-14 15:58:23 -080060 return NULL;
61 dev = dev_file_info->device;
62
Bill Pemberton6a98d362009-03-16 22:03:45 -040063 if (dev == NULL || !dev->attached)
David Schleefb79a7a22008-11-14 15:58:23 -080064 return NULL;
65
66 if (!try_module_get(dev->driver->module))
67 return NULL;
68
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +053069 return (void *)dev;
David Schleefb79a7a22008-11-14 15:58:23 -080070}
71
Bill Pemberton0b3fb272009-03-16 22:04:56 -040072void *comedi_open_old(unsigned int minor)
David Schleefb79a7a22008-11-14 15:58:23 -080073{
74 struct comedi_device_file_info *dev_file_info;
Bill Pemberton71b5f4f2009-03-16 22:05:08 -040075 struct comedi_device *dev;
David Schleefb79a7a22008-11-14 15:58:23 -080076
77 if (minor >= COMEDI_NUM_MINORS)
78 return NULL;
79
80 dev_file_info = comedi_get_device_file_info(minor);
Bill Pemberton6a98d362009-03-16 22:03:45 -040081 if (dev_file_info == NULL)
David Schleefb79a7a22008-11-14 15:58:23 -080082 return NULL;
83 dev = dev_file_info->device;
84
Bill Pemberton6a98d362009-03-16 22:03:45 -040085 if (dev == NULL || !dev->attached)
David Schleefb79a7a22008-11-14 15:58:23 -080086 return NULL;
87
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +053088 return (void *)dev;
David Schleefb79a7a22008-11-14 15:58:23 -080089}
90
Bill Pemberton0b3fb272009-03-16 22:04:56 -040091int comedi_close(void *d)
David Schleefb79a7a22008-11-14 15:58:23 -080092{
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +053093 struct comedi_device *dev = (struct comedi_device *)d;
David Schleefb79a7a22008-11-14 15:58:23 -080094
95 module_put(dev->driver->module);
96
97 return 0;
98}
99
100int comedi_loglevel(int newlevel)
101{
102 return 0;
103}
104
105void comedi_perror(const char *message)
106{
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700107 printk("%s: unknown error\n", message);
David Schleefb79a7a22008-11-14 15:58:23 -0800108}
109
110char *comedi_strerror(int err)
111{
112 return "unknown error";
113}
114
Bill Pemberton0b3fb272009-03-16 22:04:56 -0400115int comedi_fileno(void *d)
David Schleefb79a7a22008-11-14 15:58:23 -0800116{
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530117 struct comedi_device *dev = (struct comedi_device *)d;
David Schleefb79a7a22008-11-14 15:58:23 -0800118
119 /* return something random */
120 return dev->minor;
121}
122
Bill Pembertonea6d0d42009-03-16 22:05:47 -0400123int comedi_command(void *d, struct comedi_cmd *cmd)
David Schleefb79a7a22008-11-14 15:58:23 -0800124{
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530125 struct comedi_device *dev = (struct comedi_device *)d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400126 struct comedi_subdevice *s;
Bill Pembertond1636792009-03-16 22:05:20 -0400127 struct comedi_async *async;
David Schleefb79a7a22008-11-14 15:58:23 -0800128 unsigned runflags;
129
130 if (cmd->subdev >= dev->n_subdevices)
131 return -ENODEV;
132
133 s = dev->subdevices + cmd->subdev;
134 if (s->type == COMEDI_SUBD_UNUSED)
135 return -EIO;
136
137 async = s->async;
138 if (async == NULL)
139 return -ENODEV;
140
141 if (s->busy)
142 return -EBUSY;
143 s->busy = d;
144
145 if (async->cb_mask & COMEDI_CB_EOS)
146 cmd->flags |= TRIG_WAKE_EOS;
147
148 async->cmd = *cmd;
149
150 runflags = SRF_RUNNING;
151
David Schleefb79a7a22008-11-14 15:58:23 -0800152 comedi_set_subdevice_runflags(s, ~0, runflags);
153
154 comedi_reset_async_buf(async);
155
156 return s->do_cmd(dev, s);
157}
158
Bill Pembertonea6d0d42009-03-16 22:05:47 -0400159int comedi_command_test(void *d, struct comedi_cmd *cmd)
David Schleefb79a7a22008-11-14 15:58:23 -0800160{
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530161 struct comedi_device *dev = (struct comedi_device *)d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400162 struct comedi_subdevice *s;
David Schleefb79a7a22008-11-14 15:58:23 -0800163
164 if (cmd->subdev >= dev->n_subdevices)
165 return -ENODEV;
166
167 s = dev->subdevices + cmd->subdev;
168 if (s->type == COMEDI_SUBD_UNUSED)
169 return -EIO;
170
171 if (s->async == NULL)
172 return -ENODEV;
173
174 return s->do_cmdtest(dev, s, cmd);
175}
176
177/*
178 * COMEDI_INSN
179 * perform an instruction
180 */
Bill Pemberton90035c02009-03-16 22:05:53 -0400181int comedi_do_insn(void *d, struct comedi_insn *insn)
David Schleefb79a7a22008-11-14 15:58:23 -0800182{
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530183 struct comedi_device *dev = (struct comedi_device *)d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400184 struct comedi_subdevice *s;
David Schleefb79a7a22008-11-14 15:58:23 -0800185 int ret = 0;
186
187 if (insn->insn & INSN_MASK_SPECIAL) {
188 switch (insn->insn) {
189 case INSN_GTOD:
190 {
191 struct timeval tv;
192
193 do_gettimeofday(&tv);
194 insn->data[0] = tv.tv_sec;
195 insn->data[1] = tv.tv_usec;
196 ret = 2;
197
198 break;
199 }
200 case INSN_WAIT:
201 /* XXX isn't the value supposed to be nanosecs? */
202 if (insn->n != 1 || insn->data[0] >= 100) {
203 ret = -EINVAL;
204 break;
205 }
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700206 udelay(insn->data[0]);
David Schleefb79a7a22008-11-14 15:58:23 -0800207 ret = 1;
208 break;
209 case INSN_INTTRIG:
210 if (insn->n != 1) {
211 ret = -EINVAL;
212 break;
213 }
214 if (insn->subdev >= dev->n_subdevices) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700215 printk("%d not usable subdevice\n",
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530216 insn->subdev);
David Schleefb79a7a22008-11-14 15:58:23 -0800217 ret = -EINVAL;
218 break;
219 }
220 s = dev->subdevices + insn->subdev;
221 if (!s->async) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700222 printk("no async\n");
David Schleefb79a7a22008-11-14 15:58:23 -0800223 ret = -EINVAL;
224 break;
225 }
226 if (!s->async->inttrig) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700227 printk("no inttrig\n");
David Schleefb79a7a22008-11-14 15:58:23 -0800228 ret = -EAGAIN;
229 break;
230 }
231 ret = s->async->inttrig(dev, s, insn->data[0]);
232 if (ret >= 0)
233 ret = 1;
234 break;
235 default:
236 ret = -EINVAL;
237 }
238 } else {
239 /* a subdevice instruction */
240 if (insn->subdev >= dev->n_subdevices) {
241 ret = -EINVAL;
242 goto error;
243 }
244 s = dev->subdevices + insn->subdev;
245
246 if (s->type == COMEDI_SUBD_UNUSED) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700247 printk("%d not useable subdevice\n", insn->subdev);
David Schleefb79a7a22008-11-14 15:58:23 -0800248 ret = -EIO;
249 goto error;
250 }
251
252 /* XXX check lock */
253
Bill Pemberton197c82b2009-03-16 22:03:51 -0400254 ret = check_chanlist(s, 1, &insn->chanspec);
255 if (ret < 0) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700256 printk("bad chanspec\n");
David Schleefb79a7a22008-11-14 15:58:23 -0800257 ret = -EINVAL;
258 goto error;
259 }
260
261 if (s->busy) {
262 ret = -EBUSY;
263 goto error;
264 }
265 s->busy = d;
266
267 switch (insn->insn) {
268 case INSN_READ:
269 ret = s->insn_read(dev, s, insn, insn->data);
270 break;
271 case INSN_WRITE:
272 ret = s->insn_write(dev, s, insn, insn->data);
273 break;
274 case INSN_BITS:
275 ret = s->insn_bits(dev, s, insn, insn->data);
276 break;
277 case INSN_CONFIG:
278 /* XXX should check instruction length */
279 ret = s->insn_config(dev, s, insn, insn->data);
280 break;
281 default:
282 ret = -EINVAL;
283 break;
284 }
285
286 s->busy = NULL;
287 }
288 if (ret < 0)
289 goto error;
290#if 0
291 /* XXX do we want this? -- abbotti #if'ed it out for now. */
292 if (ret != insn->n) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700293 printk("BUG: result of insn != insn.n\n");
David Schleefb79a7a22008-11-14 15:58:23 -0800294 ret = -EINVAL;
295 goto error;
296 }
297#endif
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530298error:
David Schleefb79a7a22008-11-14 15:58:23 -0800299
300 return ret;
301}
302
303/*
304 COMEDI_LOCK
305 lock subdevice
306
307 arg:
308 subdevice number
309
310 reads:
311 none
312
313 writes:
314 none
315
316 necessary locking:
317 - ioctl/rt lock (this type)
318 - lock while subdevice busy
319 - lock while subdevice being programmed
320
321*/
Bill Pemberton0b3fb272009-03-16 22:04:56 -0400322int comedi_lock(void *d, unsigned int subdevice)
David Schleefb79a7a22008-11-14 15:58:23 -0800323{
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530324 struct comedi_device *dev = (struct comedi_device *)d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400325 struct comedi_subdevice *s;
David Schleefb79a7a22008-11-14 15:58:23 -0800326 unsigned long flags;
327 int ret = 0;
328
Bill Pemberton82675f32009-03-16 22:04:23 -0400329 if (subdevice >= dev->n_subdevices)
David Schleefb79a7a22008-11-14 15:58:23 -0800330 return -EINVAL;
Bill Pemberton82675f32009-03-16 22:04:23 -0400331
David Schleefb79a7a22008-11-14 15:58:23 -0800332 s = dev->subdevices + subdevice;
333
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700334 spin_lock_irqsave(&s->spin_lock, flags);
David Schleefb79a7a22008-11-14 15:58:23 -0800335
336 if (s->busy) {
337 ret = -EBUSY;
338 } else {
339 if (s->lock) {
340 ret = -EBUSY;
341 } else {
342 s->lock = d;
343 }
344 }
345
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700346 spin_unlock_irqrestore(&s->spin_lock, flags);
David Schleefb79a7a22008-11-14 15:58:23 -0800347
348 return ret;
349}
350
351/*
352 COMEDI_UNLOCK
353 unlock subdevice
354
355 arg:
356 subdevice number
357
358 reads:
359 none
360
361 writes:
362 none
363
364*/
Bill Pemberton0b3fb272009-03-16 22:04:56 -0400365int comedi_unlock(void *d, unsigned int subdevice)
David Schleefb79a7a22008-11-14 15:58:23 -0800366{
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530367 struct comedi_device *dev = (struct comedi_device *)d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400368 struct comedi_subdevice *s;
David Schleefb79a7a22008-11-14 15:58:23 -0800369 unsigned long flags;
Bill Pembertond1636792009-03-16 22:05:20 -0400370 struct comedi_async *async;
David Schleefb79a7a22008-11-14 15:58:23 -0800371 int ret;
372
Bill Pemberton82675f32009-03-16 22:04:23 -0400373 if (subdevice >= dev->n_subdevices)
David Schleefb79a7a22008-11-14 15:58:23 -0800374 return -EINVAL;
Bill Pemberton82675f32009-03-16 22:04:23 -0400375
David Schleefb79a7a22008-11-14 15:58:23 -0800376 s = dev->subdevices + subdevice;
377
378 async = s->async;
379
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700380 spin_lock_irqsave(&s->spin_lock, flags);
David Schleefb79a7a22008-11-14 15:58:23 -0800381
382 if (s->busy) {
383 ret = -EBUSY;
384 } else if (s->lock && s->lock != (void *)d) {
385 ret = -EACCES;
386 } else {
387 s->lock = NULL;
388
389 if (async) {
390 async->cb_mask = 0;
391 async->cb_func = NULL;
392 async->cb_arg = NULL;
393 }
394
395 ret = 0;
396 }
397
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700398 spin_unlock_irqrestore(&s->spin_lock, flags);
David Schleefb79a7a22008-11-14 15:58:23 -0800399
400 return ret;
401}
402
403/*
404 COMEDI_CANCEL
405 cancel acquisition ioctl
406
407 arg:
408 subdevice number
409
410 reads:
411 nothing
412
413 writes:
414 nothing
415
416*/
Bill Pemberton0b3fb272009-03-16 22:04:56 -0400417int comedi_cancel(void *d, unsigned int subdevice)
David Schleefb79a7a22008-11-14 15:58:23 -0800418{
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530419 struct comedi_device *dev = (struct comedi_device *)d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400420 struct comedi_subdevice *s;
David Schleefb79a7a22008-11-14 15:58:23 -0800421 int ret = 0;
422
Bill Pemberton82675f32009-03-16 22:04:23 -0400423 if (subdevice >= dev->n_subdevices)
David Schleefb79a7a22008-11-14 15:58:23 -0800424 return -EINVAL;
Bill Pemberton82675f32009-03-16 22:04:23 -0400425
David Schleefb79a7a22008-11-14 15:58:23 -0800426 s = dev->subdevices + subdevice;
427
428 if (s->lock && s->lock != d)
429 return -EACCES;
430
431#if 0
432 if (!s->busy)
433 return 0;
434
435 if (s->busy != d)
436 return -EBUSY;
437#endif
438
439 if (!s->cancel || !s->async)
440 return -EINVAL;
441
Bill Pemberton197c82b2009-03-16 22:03:51 -0400442 ret = s->cancel(dev, s);
443
444 if (ret)
David Schleefb79a7a22008-11-14 15:58:23 -0800445 return ret;
446
David Schleefb79a7a22008-11-14 15:58:23 -0800447 comedi_set_subdevice_runflags(s, SRF_RUNNING | SRF_RT, 0);
448 s->async->inttrig = NULL;
449 s->busy = NULL;
450
451 return 0;
452}
453
454/*
455 registration of callback functions
456 */
Bill Pemberton0b3fb272009-03-16 22:04:56 -0400457int comedi_register_callback(void *d, unsigned int subdevice,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530458 unsigned int mask, int (*cb) (unsigned int,
459 void *), void *arg)
David Schleefb79a7a22008-11-14 15:58:23 -0800460{
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530461 struct comedi_device *dev = (struct comedi_device *)d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400462 struct comedi_subdevice *s;
Bill Pembertond1636792009-03-16 22:05:20 -0400463 struct comedi_async *async;
David Schleefb79a7a22008-11-14 15:58:23 -0800464
Bill Pemberton82675f32009-03-16 22:04:23 -0400465 if (subdevice >= dev->n_subdevices)
David Schleefb79a7a22008-11-14 15:58:23 -0800466 return -EINVAL;
Bill Pemberton82675f32009-03-16 22:04:23 -0400467
David Schleefb79a7a22008-11-14 15:58:23 -0800468 s = dev->subdevices + subdevice;
469
470 async = s->async;
471 if (s->type == COMEDI_SUBD_UNUSED || !async)
472 return -EIO;
473
474 /* are we locked? (ioctl lock) */
475 if (s->lock && s->lock != d)
476 return -EACCES;
477
478 /* are we busy? */
479 if (s->busy)
480 return -EBUSY;
481
482 if (!mask) {
483 async->cb_mask = 0;
484 async->cb_func = NULL;
485 async->cb_arg = NULL;
486 } else {
487 async->cb_mask = mask;
488 async->cb_func = cb;
489 async->cb_arg = arg;
490 }
491
492 return 0;
493}
494
Bill Pemberton0b3fb272009-03-16 22:04:56 -0400495int comedi_poll(void *d, unsigned int subdevice)
David Schleefb79a7a22008-11-14 15:58:23 -0800496{
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530497 struct comedi_device *dev = (struct comedi_device *)d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400498 struct comedi_subdevice *s = dev->subdevices;
Bill Pembertond1636792009-03-16 22:05:20 -0400499 struct comedi_async *async;
David Schleefb79a7a22008-11-14 15:58:23 -0800500
Bill Pemberton82675f32009-03-16 22:04:23 -0400501 if (subdevice >= dev->n_subdevices)
David Schleefb79a7a22008-11-14 15:58:23 -0800502 return -EINVAL;
Bill Pemberton82675f32009-03-16 22:04:23 -0400503
David Schleefb79a7a22008-11-14 15:58:23 -0800504 s = dev->subdevices + subdevice;
505
506 async = s->async;
507 if (s->type == COMEDI_SUBD_UNUSED || !async)
508 return -EIO;
509
510 /* are we locked? (ioctl lock) */
511 if (s->lock && s->lock != d)
512 return -EACCES;
513
514 /* are we running? XXX wrong? */
515 if (!s->busy)
516 return -EIO;
517
518 return s->poll(dev, s);
519}
520
521/* WARNING: not portable */
Bill Pemberton0b3fb272009-03-16 22:04:56 -0400522int comedi_map(void *d, unsigned int subdevice, void *ptr)
David Schleefb79a7a22008-11-14 15:58:23 -0800523{
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530524 struct comedi_device *dev = (struct comedi_device *)d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400525 struct comedi_subdevice *s;
David Schleefb79a7a22008-11-14 15:58:23 -0800526
Bill Pemberton82675f32009-03-16 22:04:23 -0400527 if (subdevice >= dev->n_subdevices)
David Schleefb79a7a22008-11-14 15:58:23 -0800528 return -EINVAL;
Bill Pemberton82675f32009-03-16 22:04:23 -0400529
David Schleefb79a7a22008-11-14 15:58:23 -0800530 s = dev->subdevices + subdevice;
531
532 if (!s->async)
533 return -EINVAL;
534
Bill Pemberton82675f32009-03-16 22:04:23 -0400535 if (ptr)
David Schleefb79a7a22008-11-14 15:58:23 -0800536 *((void **)ptr) = s->async->prealloc_buf;
David Schleefb79a7a22008-11-14 15:58:23 -0800537
538 /* XXX no reference counting */
539
540 return 0;
541}
542
543/* WARNING: not portable */
Bill Pemberton0b3fb272009-03-16 22:04:56 -0400544int comedi_unmap(void *d, unsigned int subdevice)
David Schleefb79a7a22008-11-14 15:58:23 -0800545{
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530546 struct comedi_device *dev = (struct comedi_device *)d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400547 struct comedi_subdevice *s;
David Schleefb79a7a22008-11-14 15:58:23 -0800548
Bill Pemberton82675f32009-03-16 22:04:23 -0400549 if (subdevice >= dev->n_subdevices)
David Schleefb79a7a22008-11-14 15:58:23 -0800550 return -EINVAL;
Bill Pemberton82675f32009-03-16 22:04:23 -0400551
David Schleefb79a7a22008-11-14 15:58:23 -0800552 s = dev->subdevices + subdevice;
553
554 if (!s->async)
555 return -EINVAL;
556
557 /* XXX no reference counting */
558
559 return 0;
560}