kcomedilib_main.c revision 7382e5711f3adc934227473f82156ae1e2d7e562
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 <linux/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}
71EXPORT_SYMBOL(comedi_open);
72
73void *comedi_open_old(unsigned int minor)
74{
75	struct comedi_device_file_info *dev_file_info;
76	struct comedi_device *dev;
77
78	if (minor >= COMEDI_NUM_MINORS)
79		return NULL;
80
81	dev_file_info = comedi_get_device_file_info(minor);
82	if (dev_file_info == NULL)
83		return NULL;
84	dev = dev_file_info->device;
85
86	if (dev == NULL || !dev->attached)
87		return NULL;
88
89	return (void *)dev;
90}
91
92int comedi_close(void *d)
93{
94	struct comedi_device *dev = (struct comedi_device *)d;
95
96	module_put(dev->driver->module);
97
98	return 0;
99}
100EXPORT_SYMBOL(comedi_close);
101
102int comedi_loglevel(int newlevel)
103{
104	return 0;
105}
106
107void comedi_perror(const char *message)
108{
109	printk(KERN_ERR "%s: unknown error\n", message);
110}
111
112char *comedi_strerror(int err)
113{
114	return "unknown error";
115}
116
117int comedi_fileno(void *d)
118{
119	struct comedi_device *dev = (struct comedi_device *)d;
120
121	/* return something random */
122	return dev->minor;
123}
124
125int comedi_command(void *d, struct comedi_cmd *cmd)
126{
127	struct comedi_device *dev = (struct comedi_device *)d;
128	struct comedi_subdevice *s;
129	struct comedi_async *async;
130	unsigned runflags;
131
132	if (cmd->subdev >= dev->n_subdevices)
133		return -ENODEV;
134
135	s = dev->subdevices + cmd->subdev;
136	if (s->type == COMEDI_SUBD_UNUSED)
137		return -EIO;
138
139	async = s->async;
140	if (async == NULL)
141		return -ENODEV;
142
143	if (s->busy)
144		return -EBUSY;
145	s->busy = d;
146
147	if (async->cb_mask & COMEDI_CB_EOS)
148		cmd->flags |= TRIG_WAKE_EOS;
149
150	async->cmd = *cmd;
151
152	runflags = SRF_RUNNING;
153
154	comedi_set_subdevice_runflags(s, ~0, runflags);
155
156	comedi_reset_async_buf(async);
157
158	return s->do_cmd(dev, s);
159}
160
161int comedi_command_test(void *d, struct comedi_cmd *cmd)
162{
163	struct comedi_device *dev = (struct comedi_device *)d;
164	struct comedi_subdevice *s;
165
166	if (cmd->subdev >= dev->n_subdevices)
167		return -ENODEV;
168
169	s = dev->subdevices + cmd->subdev;
170	if (s->type == COMEDI_SUBD_UNUSED)
171		return -EIO;
172
173	if (s->async == NULL)
174		return -ENODEV;
175
176	return s->do_cmdtest(dev, s, cmd);
177}
178
179/*
180 *	COMEDI_INSN
181 *	perform an instruction
182 */
183int comedi_do_insn(void *d, struct comedi_insn *insn)
184{
185	struct comedi_device *dev = (struct comedi_device *)d;
186	struct comedi_subdevice *s;
187	int ret = 0;
188
189	if (insn->insn & INSN_MASK_SPECIAL) {
190		switch (insn->insn) {
191		case INSN_GTOD:
192			{
193				struct timeval tv;
194
195				do_gettimeofday(&tv);
196				insn->data[0] = tv.tv_sec;
197				insn->data[1] = tv.tv_usec;
198				ret = 2;
199
200				break;
201			}
202		case INSN_WAIT:
203			/* XXX isn't the value supposed to be nanosecs? */
204			if (insn->n != 1 || insn->data[0] >= 100) {
205				ret = -EINVAL;
206				break;
207			}
208			udelay(insn->data[0]);
209			ret = 1;
210			break;
211		case INSN_INTTRIG:
212			if (insn->n != 1) {
213				ret = -EINVAL;
214				break;
215			}
216			if (insn->subdev >= dev->n_subdevices) {
217				printk("%d not usable subdevice\n",
218				       insn->subdev);
219				ret = -EINVAL;
220				break;
221			}
222			s = dev->subdevices + insn->subdev;
223			if (!s->async) {
224				printk("no async\n");
225				ret = -EINVAL;
226				break;
227			}
228			if (!s->async->inttrig) {
229				printk("no inttrig\n");
230				ret = -EAGAIN;
231				break;
232			}
233			ret = s->async->inttrig(dev, s, insn->data[0]);
234			if (ret >= 0)
235				ret = 1;
236			break;
237		default:
238			ret = -EINVAL;
239		}
240	} else {
241		/* a subdevice instruction */
242		if (insn->subdev >= dev->n_subdevices) {
243			ret = -EINVAL;
244			goto error;
245		}
246		s = dev->subdevices + insn->subdev;
247
248		if (s->type == COMEDI_SUBD_UNUSED) {
249			printk("%d not useable subdevice\n", insn->subdev);
250			ret = -EIO;
251			goto error;
252		}
253
254		/* XXX check lock */
255
256		ret = comedi_check_chanlist(s, 1, &insn->chanspec);
257		if (ret < 0) {
258			printk("bad chanspec\n");
259			ret = -EINVAL;
260			goto error;
261		}
262
263		if (s->busy) {
264			ret = -EBUSY;
265			goto error;
266		}
267		s->busy = d;
268
269		switch (insn->insn) {
270		case INSN_READ:
271			ret = s->insn_read(dev, s, insn, insn->data);
272			break;
273		case INSN_WRITE:
274			ret = s->insn_write(dev, s, insn, insn->data);
275			break;
276		case INSN_BITS:
277			ret = s->insn_bits(dev, s, insn, insn->data);
278			break;
279		case INSN_CONFIG:
280			/* XXX should check instruction length */
281			ret = s->insn_config(dev, s, insn, insn->data);
282			break;
283		default:
284			ret = -EINVAL;
285			break;
286		}
287
288		s->busy = NULL;
289	}
290	if (ret < 0)
291		goto error;
292#if 0
293	/* XXX do we want this? -- abbotti #if'ed it out for now. */
294	if (ret != insn->n) {
295		printk("BUG: result of insn != insn.n\n");
296		ret = -EINVAL;
297		goto error;
298	}
299#endif
300error:
301
302	return ret;
303}
304
305/*
306	COMEDI_LOCK
307	lock subdevice
308
309	arg:
310		subdevice number
311
312	reads:
313		none
314
315	writes:
316		none
317
318	necessary locking:
319	- ioctl/rt lock  (this type)
320	- lock while subdevice busy
321	- lock while subdevice being programmed
322
323*/
324int comedi_lock(void *d, unsigned int subdevice)
325{
326	struct comedi_device *dev = (struct comedi_device *)d;
327	struct comedi_subdevice *s;
328	unsigned long flags;
329	int ret = 0;
330
331	if (subdevice >= dev->n_subdevices)
332		return -EINVAL;
333
334	s = dev->subdevices + subdevice;
335
336	spin_lock_irqsave(&s->spin_lock, flags);
337
338	if (s->busy) {
339		ret = -EBUSY;
340	} else {
341		if (s->lock) {
342			ret = -EBUSY;
343		} else {
344			s->lock = d;
345		}
346	}
347
348	spin_unlock_irqrestore(&s->spin_lock, flags);
349
350	return ret;
351}
352
353/*
354	COMEDI_UNLOCK
355	unlock subdevice
356
357	arg:
358		subdevice number
359
360	reads:
361		none
362
363	writes:
364		none
365
366*/
367int comedi_unlock(void *d, unsigned int subdevice)
368{
369	struct comedi_device *dev = (struct comedi_device *)d;
370	struct comedi_subdevice *s;
371	unsigned long flags;
372	struct comedi_async *async;
373	int ret;
374
375	if (subdevice >= dev->n_subdevices)
376		return -EINVAL;
377
378	s = dev->subdevices + subdevice;
379
380	async = s->async;
381
382	spin_lock_irqsave(&s->spin_lock, flags);
383
384	if (s->busy) {
385		ret = -EBUSY;
386	} else if (s->lock && s->lock != (void *)d) {
387		ret = -EACCES;
388	} else {
389		s->lock = NULL;
390
391		if (async) {
392			async->cb_mask = 0;
393			async->cb_func = NULL;
394			async->cb_arg = NULL;
395		}
396
397		ret = 0;
398	}
399
400	spin_unlock_irqrestore(&s->spin_lock, flags);
401
402	return ret;
403}
404
405/*
406	COMEDI_CANCEL
407	cancel acquisition ioctl
408
409	arg:
410		subdevice number
411
412	reads:
413		nothing
414
415	writes:
416		nothing
417
418*/
419int comedi_cancel(void *d, unsigned int subdevice)
420{
421	struct comedi_device *dev = (struct comedi_device *)d;
422	struct comedi_subdevice *s;
423	int ret = 0;
424
425	if (subdevice >= dev->n_subdevices)
426		return -EINVAL;
427
428	s = dev->subdevices + subdevice;
429
430	if (s->lock && s->lock != d)
431		return -EACCES;
432
433#if 0
434	if (!s->busy)
435		return 0;
436
437	if (s->busy != d)
438		return -EBUSY;
439#endif
440
441	if (!s->cancel || !s->async)
442		return -EINVAL;
443
444	ret = s->cancel(dev, s);
445
446	if (ret)
447		return ret;
448
449	comedi_set_subdevice_runflags(s, SRF_RUNNING | SRF_RT, 0);
450	s->async->inttrig = NULL;
451	s->busy = NULL;
452
453	return 0;
454}
455
456/*
457   registration of callback functions
458 */
459int comedi_register_callback(void *d, unsigned int subdevice,
460			     unsigned int mask, int (*cb) (unsigned int,
461							   void *), void *arg)
462{
463	struct comedi_device *dev = (struct comedi_device *)d;
464	struct comedi_subdevice *s;
465	struct comedi_async *async;
466
467	if (subdevice >= dev->n_subdevices)
468		return -EINVAL;
469
470	s = dev->subdevices + subdevice;
471
472	async = s->async;
473	if (s->type == COMEDI_SUBD_UNUSED || !async)
474		return -EIO;
475
476	/* are we locked? (ioctl lock) */
477	if (s->lock && s->lock != d)
478		return -EACCES;
479
480	/* are we busy? */
481	if (s->busy)
482		return -EBUSY;
483
484	if (!mask) {
485		async->cb_mask = 0;
486		async->cb_func = NULL;
487		async->cb_arg = NULL;
488	} else {
489		async->cb_mask = mask;
490		async->cb_func = cb;
491		async->cb_arg = arg;
492	}
493
494	return 0;
495}
496
497int comedi_poll(void *d, unsigned int subdevice)
498{
499	struct comedi_device *dev = (struct comedi_device *)d;
500	struct comedi_subdevice *s = dev->subdevices;
501	struct comedi_async *async;
502
503	if (subdevice >= dev->n_subdevices)
504		return -EINVAL;
505
506	s = dev->subdevices + subdevice;
507
508	async = s->async;
509	if (s->type == COMEDI_SUBD_UNUSED || !async)
510		return -EIO;
511
512	/* are we locked? (ioctl lock) */
513	if (s->lock && s->lock != d)
514		return -EACCES;
515
516	/* are we running? XXX wrong? */
517	if (!s->busy)
518		return -EIO;
519
520	return s->poll(dev, s);
521}
522
523/* WARNING: not portable */
524int comedi_map(void *d, unsigned int subdevice, void *ptr)
525{
526	struct comedi_device *dev = (struct comedi_device *)d;
527	struct comedi_subdevice *s;
528
529	if (subdevice >= dev->n_subdevices)
530		return -EINVAL;
531
532	s = dev->subdevices + subdevice;
533
534	if (!s->async)
535		return -EINVAL;
536
537	if (ptr)
538		*((void **)ptr) = s->async->prealloc_buf;
539
540	/* XXX no reference counting */
541
542	return 0;
543}
544
545/* WARNING: not portable */
546int comedi_unmap(void *d, unsigned int subdevice)
547{
548	struct comedi_device *dev = (struct comedi_device *)d;
549	struct comedi_subdevice *s;
550
551	if (subdevice >= dev->n_subdevices)
552		return -EINVAL;
553
554	s = dev->subdevices + subdevice;
555
556	if (!s->async)
557		return -EINVAL;
558
559	/* XXX no reference counting */
560
561	return 0;
562}
563