blob: 94a5067f6bd5269fc85c1ae35d2b4eea16626485 [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>
34#include <linux/slab.h>
35#include <asm/io.h>
36
37#include "../comedi.h"
38#include "../comedilib.h"
39#include "../comedidev.h"
40
41MODULE_AUTHOR("David Schleef <ds@schleef.org>");
42MODULE_DESCRIPTION("Comedi kernel library");
43MODULE_LICENSE("GPL");
44
45comedi_t *comedi_open(const char *filename)
46{
47 struct comedi_device_file_info *dev_file_info;
48 comedi_device *dev;
49 unsigned int minor;
50
51 if (strncmp(filename, "/dev/comedi", 11) != 0)
52 return NULL;
53
54 minor = simple_strtoul(filename + 11, NULL, 0);
55
56 if (minor >= COMEDI_NUM_BOARD_MINORS)
57 return NULL;
58
59 dev_file_info = comedi_get_device_file_info(minor);
Bill Pemberton6a98d362009-03-16 22:03:45 -040060 if (dev_file_info == NULL)
David Schleefb79a7a22008-11-14 15:58:23 -080061 return NULL;
62 dev = dev_file_info->device;
63
Bill Pemberton6a98d362009-03-16 22:03:45 -040064 if (dev == NULL || !dev->attached)
David Schleefb79a7a22008-11-14 15:58:23 -080065 return NULL;
66
67 if (!try_module_get(dev->driver->module))
68 return NULL;
69
70 return (comedi_t *) dev;
71}
72
73comedi_t *comedi_open_old(unsigned int minor)
74{
75 struct comedi_device_file_info *dev_file_info;
76 comedi_device *dev;
77
78 if (minor >= COMEDI_NUM_MINORS)
79 return NULL;
80
81 dev_file_info = comedi_get_device_file_info(minor);
Bill Pemberton6a98d362009-03-16 22:03:45 -040082 if (dev_file_info == NULL)
David Schleefb79a7a22008-11-14 15:58:23 -080083 return NULL;
84 dev = dev_file_info->device;
85
Bill Pemberton6a98d362009-03-16 22:03:45 -040086 if (dev == NULL || !dev->attached)
David Schleefb79a7a22008-11-14 15:58:23 -080087 return NULL;
88
89 return (comedi_t *) dev;
90}
91
Bill Pembertone473e912009-03-16 22:03:29 -040092int comedi_close(comedi_t *d)
David Schleefb79a7a22008-11-14 15:58:23 -080093{
94 comedi_device *dev = (comedi_device *) d;
95
96 module_put(dev->driver->module);
97
98 return 0;
99}
100
101int comedi_loglevel(int newlevel)
102{
103 return 0;
104}
105
106void comedi_perror(const char *message)
107{
108 rt_printk("%s: unknown error\n", message);
109}
110
111char *comedi_strerror(int err)
112{
113 return "unknown error";
114}
115
Bill Pembertone473e912009-03-16 22:03:29 -0400116int comedi_fileno(comedi_t *d)
David Schleefb79a7a22008-11-14 15:58:23 -0800117{
118 comedi_device *dev = (comedi_device *) d;
119
120 /* return something random */
121 return dev->minor;
122}
123
Bill Pembertone473e912009-03-16 22:03:29 -0400124int comedi_command(comedi_t *d, comedi_cmd *cmd)
David Schleefb79a7a22008-11-14 15:58:23 -0800125{
126 comedi_device *dev = (comedi_device *) d;
127 comedi_subdevice *s;
128 comedi_async *async;
129 unsigned runflags;
130
131 if (cmd->subdev >= dev->n_subdevices)
132 return -ENODEV;
133
134 s = dev->subdevices + cmd->subdev;
135 if (s->type == COMEDI_SUBD_UNUSED)
136 return -EIO;
137
138 async = s->async;
139 if (async == NULL)
140 return -ENODEV;
141
142 if (s->busy)
143 return -EBUSY;
144 s->busy = d;
145
146 if (async->cb_mask & COMEDI_CB_EOS)
147 cmd->flags |= TRIG_WAKE_EOS;
148
149 async->cmd = *cmd;
150
151 runflags = SRF_RUNNING;
152
153#ifdef CONFIG_COMEDI_RT
154 if (comedi_switch_to_rt(dev) == 0)
155 runflags |= SRF_RT;
156#endif
157 comedi_set_subdevice_runflags(s, ~0, runflags);
158
159 comedi_reset_async_buf(async);
160
161 return s->do_cmd(dev, s);
162}
163
Bill Pembertone473e912009-03-16 22:03:29 -0400164int comedi_command_test(comedi_t *d, comedi_cmd *cmd)
David Schleefb79a7a22008-11-14 15:58:23 -0800165{
166 comedi_device *dev = (comedi_device *) d;
167 comedi_subdevice *s;
168
169 if (cmd->subdev >= dev->n_subdevices)
170 return -ENODEV;
171
172 s = dev->subdevices + cmd->subdev;
173 if (s->type == COMEDI_SUBD_UNUSED)
174 return -EIO;
175
176 if (s->async == NULL)
177 return -ENODEV;
178
179 return s->do_cmdtest(dev, s, cmd);
180}
181
182/*
183 * COMEDI_INSN
184 * perform an instruction
185 */
Bill Pembertone473e912009-03-16 22:03:29 -0400186int comedi_do_insn(comedi_t *d, comedi_insn *insn)
David Schleefb79a7a22008-11-14 15:58:23 -0800187{
188 comedi_device *dev = (comedi_device *) d;
189 comedi_subdevice *s;
190 int ret = 0;
191
192 if (insn->insn & INSN_MASK_SPECIAL) {
193 switch (insn->insn) {
194 case INSN_GTOD:
195 {
196 struct timeval tv;
197
198 do_gettimeofday(&tv);
199 insn->data[0] = tv.tv_sec;
200 insn->data[1] = tv.tv_usec;
201 ret = 2;
202
203 break;
204 }
205 case INSN_WAIT:
206 /* XXX isn't the value supposed to be nanosecs? */
207 if (insn->n != 1 || insn->data[0] >= 100) {
208 ret = -EINVAL;
209 break;
210 }
211 comedi_udelay(insn->data[0]);
212 ret = 1;
213 break;
214 case INSN_INTTRIG:
215 if (insn->n != 1) {
216 ret = -EINVAL;
217 break;
218 }
219 if (insn->subdev >= dev->n_subdevices) {
220 rt_printk("%d not usable subdevice\n",
221 insn->subdev);
222 ret = -EINVAL;
223 break;
224 }
225 s = dev->subdevices + insn->subdev;
226 if (!s->async) {
227 rt_printk("no async\n");
228 ret = -EINVAL;
229 break;
230 }
231 if (!s->async->inttrig) {
232 rt_printk("no inttrig\n");
233 ret = -EAGAIN;
234 break;
235 }
236 ret = s->async->inttrig(dev, s, insn->data[0]);
237 if (ret >= 0)
238 ret = 1;
239 break;
240 default:
241 ret = -EINVAL;
242 }
243 } else {
244 /* a subdevice instruction */
245 if (insn->subdev >= dev->n_subdevices) {
246 ret = -EINVAL;
247 goto error;
248 }
249 s = dev->subdevices + insn->subdev;
250
251 if (s->type == COMEDI_SUBD_UNUSED) {
252 rt_printk("%d not useable subdevice\n", insn->subdev);
253 ret = -EIO;
254 goto error;
255 }
256
257 /* XXX check lock */
258
Bill Pemberton197c82b2009-03-16 22:03:51 -0400259 ret = check_chanlist(s, 1, &insn->chanspec);
260 if (ret < 0) {
David Schleefb79a7a22008-11-14 15:58:23 -0800261 rt_printk("bad chanspec\n");
262 ret = -EINVAL;
263 goto error;
264 }
265
266 if (s->busy) {
267 ret = -EBUSY;
268 goto error;
269 }
270 s->busy = d;
271
272 switch (insn->insn) {
273 case INSN_READ:
274 ret = s->insn_read(dev, s, insn, insn->data);
275 break;
276 case INSN_WRITE:
277 ret = s->insn_write(dev, s, insn, insn->data);
278 break;
279 case INSN_BITS:
280 ret = s->insn_bits(dev, s, insn, insn->data);
281 break;
282 case INSN_CONFIG:
283 /* XXX should check instruction length */
284 ret = s->insn_config(dev, s, insn, insn->data);
285 break;
286 default:
287 ret = -EINVAL;
288 break;
289 }
290
291 s->busy = NULL;
292 }
293 if (ret < 0)
294 goto error;
295#if 0
296 /* XXX do we want this? -- abbotti #if'ed it out for now. */
297 if (ret != insn->n) {
298 rt_printk("BUG: result of insn != insn.n\n");
299 ret = -EINVAL;
300 goto error;
301 }
302#endif
303 error:
304
305 return ret;
306}
307
308/*
309 COMEDI_LOCK
310 lock subdevice
311
312 arg:
313 subdevice number
314
315 reads:
316 none
317
318 writes:
319 none
320
321 necessary locking:
322 - ioctl/rt lock (this type)
323 - lock while subdevice busy
324 - lock while subdevice being programmed
325
326*/
Bill Pembertone473e912009-03-16 22:03:29 -0400327int comedi_lock(comedi_t *d, unsigned int subdevice)
David Schleefb79a7a22008-11-14 15:58:23 -0800328{
329 comedi_device *dev = (comedi_device *) d;
330 comedi_subdevice *s;
331 unsigned long flags;
332 int ret = 0;
333
334 if (subdevice >= dev->n_subdevices) {
335 return -EINVAL;
336 }
337 s = dev->subdevices + subdevice;
338
339 comedi_spin_lock_irqsave(&s->spin_lock, flags);
340
341 if (s->busy) {
342 ret = -EBUSY;
343 } else {
344 if (s->lock) {
345 ret = -EBUSY;
346 } else {
347 s->lock = d;
348 }
349 }
350
351 comedi_spin_unlock_irqrestore(&s->spin_lock, flags);
352
353 return ret;
354}
355
356/*
357 COMEDI_UNLOCK
358 unlock subdevice
359
360 arg:
361 subdevice number
362
363 reads:
364 none
365
366 writes:
367 none
368
369*/
Bill Pembertone473e912009-03-16 22:03:29 -0400370int comedi_unlock(comedi_t *d, unsigned int subdevice)
David Schleefb79a7a22008-11-14 15:58:23 -0800371{
372 comedi_device *dev = (comedi_device *) d;
373 comedi_subdevice *s;
374 unsigned long flags;
375 comedi_async *async;
376 int ret;
377
378 if (subdevice >= dev->n_subdevices) {
379 return -EINVAL;
380 }
381 s = dev->subdevices + subdevice;
382
383 async = s->async;
384
385 comedi_spin_lock_irqsave(&s->spin_lock, flags);
386
387 if (s->busy) {
388 ret = -EBUSY;
389 } else if (s->lock && s->lock != (void *)d) {
390 ret = -EACCES;
391 } else {
392 s->lock = NULL;
393
394 if (async) {
395 async->cb_mask = 0;
396 async->cb_func = NULL;
397 async->cb_arg = NULL;
398 }
399
400 ret = 0;
401 }
402
403 comedi_spin_unlock_irqrestore(&s->spin_lock, flags);
404
405 return ret;
406}
407
408/*
409 COMEDI_CANCEL
410 cancel acquisition ioctl
411
412 arg:
413 subdevice number
414
415 reads:
416 nothing
417
418 writes:
419 nothing
420
421*/
Bill Pembertone473e912009-03-16 22:03:29 -0400422int comedi_cancel(comedi_t *d, unsigned int subdevice)
David Schleefb79a7a22008-11-14 15:58:23 -0800423{
424 comedi_device *dev = (comedi_device *) d;
425 comedi_subdevice *s;
426 int ret = 0;
427
428 if (subdevice >= dev->n_subdevices) {
429 return -EINVAL;
430 }
431 s = dev->subdevices + subdevice;
432
433 if (s->lock && s->lock != d)
434 return -EACCES;
435
436#if 0
437 if (!s->busy)
438 return 0;
439
440 if (s->busy != d)
441 return -EBUSY;
442#endif
443
444 if (!s->cancel || !s->async)
445 return -EINVAL;
446
Bill Pemberton197c82b2009-03-16 22:03:51 -0400447 ret = s->cancel(dev, s);
448
449 if (ret)
David Schleefb79a7a22008-11-14 15:58:23 -0800450 return ret;
451
452#ifdef CONFIG_COMEDI_RT
453 if (comedi_get_subdevice_runflags(s) & SRF_RT) {
454 comedi_switch_to_non_rt(dev);
455 }
456#endif
457 comedi_set_subdevice_runflags(s, SRF_RUNNING | SRF_RT, 0);
458 s->async->inttrig = NULL;
459 s->busy = NULL;
460
461 return 0;
462}
463
464/*
465 registration of callback functions
466 */
Bill Pembertone473e912009-03-16 22:03:29 -0400467int comedi_register_callback(comedi_t *d, unsigned int subdevice,
David Schleefb79a7a22008-11-14 15:58:23 -0800468 unsigned int mask, int (*cb) (unsigned int, void *), void *arg)
469{
470 comedi_device *dev = (comedi_device *) d;
471 comedi_subdevice *s;
472 comedi_async *async;
473
474 if (subdevice >= dev->n_subdevices) {
475 return -EINVAL;
476 }
477 s = dev->subdevices + subdevice;
478
479 async = s->async;
480 if (s->type == COMEDI_SUBD_UNUSED || !async)
481 return -EIO;
482
483 /* are we locked? (ioctl lock) */
484 if (s->lock && s->lock != d)
485 return -EACCES;
486
487 /* are we busy? */
488 if (s->busy)
489 return -EBUSY;
490
491 if (!mask) {
492 async->cb_mask = 0;
493 async->cb_func = NULL;
494 async->cb_arg = NULL;
495 } else {
496 async->cb_mask = mask;
497 async->cb_func = cb;
498 async->cb_arg = arg;
499 }
500
501 return 0;
502}
503
Bill Pembertone473e912009-03-16 22:03:29 -0400504int comedi_poll(comedi_t *d, unsigned int subdevice)
David Schleefb79a7a22008-11-14 15:58:23 -0800505{
506 comedi_device *dev = (comedi_device *) d;
507 comedi_subdevice *s = dev->subdevices;
508 comedi_async *async;
509
510 if (subdevice >= dev->n_subdevices) {
511 return -EINVAL;
512 }
513 s = dev->subdevices + subdevice;
514
515 async = s->async;
516 if (s->type == COMEDI_SUBD_UNUSED || !async)
517 return -EIO;
518
519 /* are we locked? (ioctl lock) */
520 if (s->lock && s->lock != d)
521 return -EACCES;
522
523 /* are we running? XXX wrong? */
524 if (!s->busy)
525 return -EIO;
526
527 return s->poll(dev, s);
528}
529
530/* WARNING: not portable */
Bill Pembertone473e912009-03-16 22:03:29 -0400531int comedi_map(comedi_t *d, unsigned int subdevice, void *ptr)
David Schleefb79a7a22008-11-14 15:58:23 -0800532{
533 comedi_device *dev = (comedi_device *) d;
534 comedi_subdevice *s;
535
536 if (subdevice >= dev->n_subdevices) {
537 return -EINVAL;
538 }
539 s = dev->subdevices + subdevice;
540
541 if (!s->async)
542 return -EINVAL;
543
544 if (ptr) {
545 *((void **)ptr) = s->async->prealloc_buf;
546 }
547
548 /* XXX no reference counting */
549
550 return 0;
551}
552
553/* WARNING: not portable */
Bill Pembertone473e912009-03-16 22:03:29 -0400554int comedi_unmap(comedi_t *d, unsigned int subdevice)
David Schleefb79a7a22008-11-14 15:58:23 -0800555{
556 comedi_device *dev = (comedi_device *) d;
557 comedi_subdevice *s;
558
559 if (subdevice >= dev->n_subdevices) {
560 return -EINVAL;
561 }
562 s = dev->subdevices + subdevice;
563
564 if (!s->async)
565 return -EINVAL;
566
567 /* XXX no reference counting */
568
569 return 0;
570}