Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux...
[sfrench/cifs-2.6.git] / drivers / staging / comedi / kcomedilib / kcomedilib_main.c
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
41 MODULE_AUTHOR("David Schleef <ds@schleef.org>");
42 MODULE_DESCRIPTION("Comedi kernel library");
43 MODULE_LICENSE("GPL");
44
45 void *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
73 void *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
92 int 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
101 int comedi_loglevel(int newlevel)
102 {
103         return 0;
104 }
105
106 void comedi_perror(const char *message)
107 {
108         rt_printk("%s: unknown error\n", message);
109 }
110
111 char *comedi_strerror(int err)
112 {
113         return "unknown error";
114 }
115
116 int 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
124 int 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 #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
164 int comedi_command_test(void *d, struct comedi_cmd *cmd)
165 {
166         struct comedi_device *dev = (struct comedi_device *) d;
167         struct 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  */
186 int comedi_do_insn(void *d, struct comedi_insn *insn)
187 {
188         struct comedi_device *dev = (struct comedi_device *) d;
189         struct 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
259                 ret = check_chanlist(s, 1, &insn->chanspec);
260                 if (ret < 0) {
261                         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 */
327 int comedi_lock(void *d, unsigned int subdevice)
328 {
329         struct comedi_device *dev = (struct comedi_device *) d;
330         struct 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 */
370 int comedi_unlock(void *d, unsigned int subdevice)
371 {
372         struct comedi_device *dev = (struct comedi_device *) d;
373         struct comedi_subdevice *s;
374         unsigned long flags;
375         struct 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 */
422 int comedi_cancel(void *d, unsigned int subdevice)
423 {
424         struct comedi_device *dev = (struct comedi_device *) d;
425         struct 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
447         ret = s->cancel(dev, s);
448
449         if (ret)
450                 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  */
467 int comedi_register_callback(void *d, unsigned int subdevice,
468         unsigned int mask, int (*cb) (unsigned int, void *), void *arg)
469 {
470         struct comedi_device *dev = (struct comedi_device *) d;
471         struct comedi_subdevice *s;
472         struct 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
504 int comedi_poll(void *d, unsigned int subdevice)
505 {
506         struct comedi_device *dev = (struct comedi_device *) d;
507         struct comedi_subdevice *s = dev->subdevices;
508         struct 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 */
531 int comedi_map(void *d, unsigned int subdevice, void *ptr)
532 {
533         struct comedi_device *dev = (struct comedi_device *) d;
534         struct 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         /* XXX no reference counting */
548
549         return 0;
550 }
551
552 /* WARNING: not portable */
553 int comedi_unmap(void *d, unsigned int subdevice)
554 {
555         struct comedi_device *dev = (struct comedi_device *) d;
556         struct comedi_subdevice *s;
557
558         if (subdevice >= dev->n_subdevices)
559                 return -EINVAL;
560
561         s = dev->subdevices + subdevice;
562
563         if (!s->async)
564                 return -EINVAL;
565
566         /* XXX no reference counting */
567
568         return 0;
569 }