Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / 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
19 #include <linux/module.h>
20
21 #include <linux/errno.h>
22 #include <linux/kernel.h>
23 #include <linux/sched.h>
24 #include <linux/fcntl.h>
25 #include <linux/mm.h>
26 #include <linux/io.h>
27
28 #include "../comedi.h"
29 #include "../comedilib.h"
30 #include "../comedidev.h"
31
32 MODULE_AUTHOR("David Schleef <ds@schleef.org>");
33 MODULE_DESCRIPTION("Comedi kernel library");
34 MODULE_LICENSE("GPL");
35
36 struct comedi_device *comedi_open(const char *filename)
37 {
38         struct comedi_device *dev, *retval = NULL;
39         unsigned int minor;
40
41         if (strncmp(filename, "/dev/comedi", 11) != 0)
42                 return NULL;
43
44         if (kstrtouint(filename + 11, 0, &minor))
45                 return NULL;
46
47         if (minor >= COMEDI_NUM_BOARD_MINORS)
48                 return NULL;
49
50         dev = comedi_dev_get_from_minor(minor);
51         if (!dev)
52                 return NULL;
53
54         down_read(&dev->attach_lock);
55         if (dev->attached)
56                 retval = dev;
57         else
58                 retval = NULL;
59         up_read(&dev->attach_lock);
60
61         if (!retval)
62                 comedi_dev_put(dev);
63
64         return retval;
65 }
66 EXPORT_SYMBOL_GPL(comedi_open);
67
68 int comedi_close(struct comedi_device *dev)
69 {
70         comedi_dev_put(dev);
71         return 0;
72 }
73 EXPORT_SYMBOL_GPL(comedi_close);
74
75 static int comedi_do_insn(struct comedi_device *dev,
76                           struct comedi_insn *insn,
77                           unsigned int *data)
78 {
79         struct comedi_subdevice *s;
80         int ret;
81
82         mutex_lock(&dev->mutex);
83
84         if (!dev->attached) {
85                 ret = -EINVAL;
86                 goto error;
87         }
88
89         /* a subdevice instruction */
90         if (insn->subdev >= dev->n_subdevices) {
91                 ret = -EINVAL;
92                 goto error;
93         }
94         s = &dev->subdevices[insn->subdev];
95
96         if (s->type == COMEDI_SUBD_UNUSED) {
97                 dev_err(dev->class_dev,
98                         "%d not usable subdevice\n", insn->subdev);
99                 ret = -EIO;
100                 goto error;
101         }
102
103         /* XXX check lock */
104
105         ret = comedi_check_chanlist(s, 1, &insn->chanspec);
106         if (ret < 0) {
107                 dev_err(dev->class_dev, "bad chanspec\n");
108                 ret = -EINVAL;
109                 goto error;
110         }
111
112         if (s->busy) {
113                 ret = -EBUSY;
114                 goto error;
115         }
116         s->busy = dev;
117
118         switch (insn->insn) {
119         case INSN_BITS:
120                 ret = s->insn_bits(dev, s, insn, data);
121                 break;
122         case INSN_CONFIG:
123                 /* XXX should check instruction length */
124                 ret = s->insn_config(dev, s, insn, data);
125                 break;
126         default:
127                 ret = -EINVAL;
128                 break;
129         }
130
131         s->busy = NULL;
132 error:
133
134         mutex_unlock(&dev->mutex);
135         return ret;
136 }
137
138 int comedi_dio_get_config(struct comedi_device *dev, unsigned int subdev,
139                           unsigned int chan, unsigned int *io)
140 {
141         struct comedi_insn insn;
142         unsigned int data[2];
143         int ret;
144
145         memset(&insn, 0, sizeof(insn));
146         insn.insn = INSN_CONFIG;
147         insn.n = 2;
148         insn.subdev = subdev;
149         insn.chanspec = CR_PACK(chan, 0, 0);
150         data[0] = INSN_CONFIG_DIO_QUERY;
151         data[1] = 0;
152         ret = comedi_do_insn(dev, &insn, data);
153         if (ret >= 0)
154                 *io = data[1];
155         return ret;
156 }
157 EXPORT_SYMBOL_GPL(comedi_dio_get_config);
158
159 int comedi_dio_config(struct comedi_device *dev, unsigned int subdev,
160                       unsigned int chan, unsigned int io)
161 {
162         struct comedi_insn insn;
163
164         memset(&insn, 0, sizeof(insn));
165         insn.insn = INSN_CONFIG;
166         insn.n = 1;
167         insn.subdev = subdev;
168         insn.chanspec = CR_PACK(chan, 0, 0);
169
170         return comedi_do_insn(dev, &insn, &io);
171 }
172 EXPORT_SYMBOL_GPL(comedi_dio_config);
173
174 int comedi_dio_bitfield2(struct comedi_device *dev, unsigned int subdev,
175                          unsigned int mask, unsigned int *bits,
176                          unsigned int base_channel)
177 {
178         struct comedi_insn insn;
179         unsigned int data[2];
180         unsigned int n_chan;
181         unsigned int shift;
182         int ret;
183
184         base_channel = CR_CHAN(base_channel);
185         n_chan = comedi_get_n_channels(dev, subdev);
186         if (base_channel >= n_chan)
187                 return -EINVAL;
188
189         memset(&insn, 0, sizeof(insn));
190         insn.insn = INSN_BITS;
191         insn.chanspec = base_channel;
192         insn.n = 2;
193         insn.subdev = subdev;
194
195         data[0] = mask;
196         data[1] = *bits;
197
198         /*
199          * Most drivers ignore the base channel in insn->chanspec.
200          * Fix this here if the subdevice has <= 32 channels.
201          */
202         if (n_chan <= 32) {
203                 shift = base_channel;
204                 if (shift) {
205                         insn.chanspec = 0;
206                         data[0] <<= shift;
207                         data[1] <<= shift;
208                 }
209         } else {
210                 shift = 0;
211         }
212
213         ret = comedi_do_insn(dev, &insn, data);
214         *bits = data[1] >> shift;
215         return ret;
216 }
217 EXPORT_SYMBOL_GPL(comedi_dio_bitfield2);
218
219 int comedi_find_subdevice_by_type(struct comedi_device *dev, int type,
220                                   unsigned int subd)
221 {
222         struct comedi_subdevice *s;
223         int ret = -ENODEV;
224
225         down_read(&dev->attach_lock);
226         if (dev->attached)
227                 for (; subd < dev->n_subdevices; subd++) {
228                         s = &dev->subdevices[subd];
229                         if (s->type == type) {
230                                 ret = subd;
231                                 break;
232                         }
233                 }
234         up_read(&dev->attach_lock);
235         return ret;
236 }
237 EXPORT_SYMBOL_GPL(comedi_find_subdevice_by_type);
238
239 int comedi_get_n_channels(struct comedi_device *dev, unsigned int subdevice)
240 {
241         int n;
242
243         down_read(&dev->attach_lock);
244         if (!dev->attached || subdevice >= dev->n_subdevices)
245                 n = 0;
246         else
247                 n = dev->subdevices[subdevice].n_chan;
248         up_read(&dev->attach_lock);
249
250         return n;
251 }
252 EXPORT_SYMBOL_GPL(comedi_get_n_channels);