kcomedilib_main.c revision 5a0e3ad6af8660be21ca98a971cd00f331318c05
1/*
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 <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
44void *comedi_open(const char *filename)
45{
46	struct comedi_device_file_info *dev_file_info;
47	struct comedi_device *dev;
48	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);
59	if (dev_file_info == NULL)
60		return NULL;
61	dev = dev_file_info->device;
62
63	if (dev == NULL || !dev->attached)
64		return NULL;
65
66	if (!try_module_get(dev->driver->module))
67		return NULL;
68
69	return (void *)dev;
70}
71
72void *comedi_open_old(unsigned int minor)
73{
74	struct comedi_device_file_info *dev_file_info;
75	struct comedi_device *dev;
76
77	if (minor >= COMEDI_NUM_MINORS)
78		return NULL;
79
80	dev_file_info = comedi_get_device_file_info(minor);
81	if (dev_file_info == NULL)
82		return NULL;
83	dev = dev_file_info->device;
84
85	if (dev == NULL || !dev->attached)
86		return NULL;
87
88	return (void *)dev;
89}
90
91int comedi_close(void *d)
92{
93	struct comedi_device *dev = (struct comedi_device *)d;
94
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{
107	printk("%s: unknown error\n", message);
108}
109
110char *comedi_strerror(int err)
111{
112	return "unknown error";
113}
114
115int comedi_fileno(void *d)
116{
117	struct comedi_device *dev = (struct comedi_device *)d;
118
119	/* return something random */
120	return dev->minor;
121}
122
123int comedi_command(void *d, struct comedi_cmd *cmd)
124{
125	struct comedi_device *dev = (struct comedi_device *)d;
126	struct comedi_subdevice *s;
127	struct comedi_async *async;
128	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
152	comedi_set_subdevice_runflags(s, ~0, runflags);
153
154	comedi_reset_async_buf(async);
155
156	return s->do_cmd(dev, s);
157}
158
159int comedi_command_test(void *d, struct comedi_cmd *cmd)
160{
161	struct comedi_device *dev = (struct comedi_device *)d;
162	struct comedi_subdevice *s;
163
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 */
181int comedi_do_insn(void *d, struct comedi_insn *insn)
182{
183	struct comedi_device *dev = (struct comedi_device *)d;
184	struct comedi_subdevice *s;
185	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			}
206			udelay(insn->data[0]);
207			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) {
215				printk("%d not usable subdevice\n",
216				       insn->subdev);
217				ret = -EINVAL;
218				break;
219			}
220			s = dev->subdevices + insn->subdev;
221			if (!s->async) {
222				printk("no async\n");
223				ret = -EINVAL;
224				break;
225			}
226			if (!s->async->inttrig) {
227				printk("no inttrig\n");
228				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) {
247			printk("%d not useable subdevice\n", insn->subdev);
248			ret = -EIO;
249			goto error;
250		}
251
252		/* XXX check lock */
253
254		ret = check_chanlist(s, 1, &insn->chanspec);
255		if (ret < 0) {
256			printk("bad chanspec\n");
257			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) {
293		printk("BUG: result of insn != insn.n\n");
294		ret = -EINVAL;
295		goto error;
296	}
297#endif
298error:
299
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*/
322int comedi_lock(void *d, unsigned int subdevice)
323{
324	struct comedi_device *dev = (struct comedi_device *)d;
325	struct comedi_subdevice *s;
326	unsigned long flags;
327	int ret = 0;
328
329	if (subdevice >= dev->n_subdevices)
330		return -EINVAL;
331
332	s = dev->subdevices + subdevice;
333
334	spin_lock_irqsave(&s->spin_lock, flags);
335
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
346	spin_unlock_irqrestore(&s->spin_lock, flags);
347
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*/
365int comedi_unlock(void *d, unsigned int subdevice)
366{
367	struct comedi_device *dev = (struct comedi_device *)d;
368	struct comedi_subdevice *s;
369	unsigned long flags;
370	struct comedi_async *async;
371	int ret;
372
373	if (subdevice >= dev->n_subdevices)
374		return -EINVAL;
375
376	s = dev->subdevices + subdevice;
377
378	async = s->async;
379
380	spin_lock_irqsave(&s->spin_lock, flags);
381
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
398	spin_unlock_irqrestore(&s->spin_lock, flags);
399
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*/
417int comedi_cancel(void *d, unsigned int subdevice)
418{
419	struct comedi_device *dev = (struct comedi_device *)d;
420	struct comedi_subdevice *s;
421	int ret = 0;
422
423	if (subdevice >= dev->n_subdevices)
424		return -EINVAL;
425
426	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
442	ret = s->cancel(dev, s);
443
444	if (ret)
445		return ret;
446
447	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 */
457int comedi_register_callback(void *d, unsigned int subdevice,
458			     unsigned int mask, int (*cb) (unsigned int,
459							   void *), void *arg)
460{
461	struct comedi_device *dev = (struct comedi_device *)d;
462	struct comedi_subdevice *s;
463	struct comedi_async *async;
464
465	if (subdevice >= dev->n_subdevices)
466		return -EINVAL;
467
468	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
495int comedi_poll(void *d, unsigned int subdevice)
496{
497	struct comedi_device *dev = (struct comedi_device *)d;
498	struct comedi_subdevice *s = dev->subdevices;
499	struct comedi_async *async;
500
501	if (subdevice >= dev->n_subdevices)
502		return -EINVAL;
503
504	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 */
522int comedi_map(void *d, unsigned int subdevice, void *ptr)
523{
524	struct comedi_device *dev = (struct comedi_device *)d;
525	struct comedi_subdevice *s;
526
527	if (subdevice >= dev->n_subdevices)
528		return -EINVAL;
529
530	s = dev->subdevices + subdevice;
531
532	if (!s->async)
533		return -EINVAL;
534
535	if (ptr)
536		*((void **)ptr) = s->async->prealloc_buf;
537
538	/* XXX no reference counting */
539
540	return 0;
541}
542
543/* WARNING: not portable */
544int comedi_unmap(void *d, unsigned int subdevice)
545{
546	struct comedi_device *dev = (struct comedi_device *)d;
547	struct comedi_subdevice *s;
548
549	if (subdevice >= dev->n_subdevices)
550		return -EINVAL;
551
552	s = dev->subdevices + subdevice;
553
554	if (!s->async)
555		return -EINVAL;
556
557	/* XXX no reference counting */
558
559	return 0;
560}
561