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