blob: 76bf5619fdd522f121793b994331236268298c0b [file] [log] [blame]
David Schleefb79a7a22008-11-14 15:58:23 -08001/*
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.
David Schleefb79a7a22008-11-14 15:58:23 -080017*/
18
David Schleefb79a7a22008-11-14 15:58:23 -080019#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>
David Schleefb79a7a22008-11-14 15:58:23 -080025#include <linux/mm.h>
Greg Kroah-Hartman7382e572010-05-01 13:08:06 -070026#include <linux/io.h>
David Schleefb79a7a22008-11-14 15:58:23 -080027
28#include "../comedi.h"
29#include "../comedilib.h"
30#include "../comedidev.h"
31
32MODULE_AUTHOR("David Schleef <ds@schleef.org>");
33MODULE_DESCRIPTION("Comedi kernel library");
34MODULE_LICENSE("GPL");
35
Greg Kroah-Hartman472dfe72010-05-03 15:01:50 -070036struct comedi_device *comedi_open(const char *filename)
David Schleefb79a7a22008-11-14 15:58:23 -080037{
Ian Abbott799a66b2013-11-08 15:03:38 +000038 struct comedi_device *dev, *retval = NULL;
David Schleefb79a7a22008-11-14 15:58:23 -080039 unsigned int minor;
40
41 if (strncmp(filename, "/dev/comedi", 11) != 0)
42 return NULL;
43
Chase Southwoode6bed032014-02-14 21:59:40 -060044 if (kstrtouint(filename + 11, 0, &minor))
45 return NULL;
David Schleefb79a7a22008-11-14 15:58:23 -080046
47 if (minor >= COMEDI_NUM_BOARD_MINORS)
48 return NULL;
49
Ian Abbott799a66b2013-11-08 15:03:38 +000050 dev = comedi_dev_get_from_minor(minor);
51 if (!dev)
David Schleefb79a7a22008-11-14 15:58:23 -080052 return NULL;
53
Ian Abbott69e2387f2013-11-08 15:03:39 +000054 down_read(&dev->attach_lock);
55 if (dev->attached)
Ian Abbott799a66b2013-11-08 15:03:38 +000056 retval = dev;
57 else
58 retval = NULL;
Ian Abbott69e2387f2013-11-08 15:03:39 +000059 up_read(&dev->attach_lock);
David Schleefb79a7a22008-11-14 15:58:23 -080060
H Hartley Sweeten4c36fdd2015-03-04 12:15:41 -070061 if (!retval)
Ian Abbott799a66b2013-11-08 15:03:38 +000062 comedi_dev_put(dev);
63
64 return retval;
David Schleefb79a7a22008-11-14 15:58:23 -080065}
H Hartley Sweeten5660e742013-04-12 10:11:54 -070066EXPORT_SYMBOL_GPL(comedi_open);
David Schleefb79a7a22008-11-14 15:58:23 -080067
Ian Abbott69e2387f2013-11-08 15:03:39 +000068int comedi_close(struct comedi_device *dev)
David Schleefb79a7a22008-11-14 15:58:23 -080069{
Ian Abbott799a66b2013-11-08 15:03:38 +000070 comedi_dev_put(dev);
David Schleefb79a7a22008-11-14 15:58:23 -080071 return 0;
72}
H Hartley Sweeten5660e742013-04-12 10:11:54 -070073EXPORT_SYMBOL_GPL(comedi_close);
David Schleefb79a7a22008-11-14 15:58:23 -080074
H Hartley Sweeten1f5cc352012-09-19 17:26:53 -070075static int comedi_do_insn(struct comedi_device *dev,
76 struct comedi_insn *insn,
77 unsigned int *data)
David Schleefb79a7a22008-11-14 15:58:23 -080078{
Bill Pemberton34c43922009-03-16 22:05:14 -040079 struct comedi_subdevice *s;
Ian Abbott69e2387f2013-11-08 15:03:39 +000080 int ret;
81
82 mutex_lock(&dev->mutex);
83
84 if (!dev->attached) {
85 ret = -EINVAL;
86 goto error;
87 }
David Schleefb79a7a22008-11-14 15:58:23 -080088
Greg Kroah-Hartman3781bc52010-05-03 14:54:34 -070089 /* a subdevice instruction */
90 if (insn->subdev >= dev->n_subdevices) {
David Schleefb79a7a22008-11-14 15:58:23 -080091 ret = -EINVAL;
92 goto error;
93 }
H Hartley Sweeten5818e702012-09-05 18:59:26 -070094 s = &dev->subdevices[insn->subdev];
Greg Kroah-Hartman3781bc52010-05-03 14:54:34 -070095
96 if (s->type == COMEDI_SUBD_UNUSED) {
YAMANE Toshiaki49e8e442012-10-05 09:07:00 +090097 dev_err(dev->class_dev,
Gustavo A. R. Silva53d484b2015-01-11 15:46:26 -060098 "%d not usable subdevice\n", insn->subdev);
Greg Kroah-Hartman3781bc52010-05-03 14:54:34 -070099 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) {
YAMANE Toshiaki49e8e442012-10-05 09:07:00 +0900107 dev_err(dev->class_dev, "bad chanspec\n");
Greg Kroah-Hartman3781bc52010-05-03 14:54:34 -0700108 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:
H Hartley Sweeten1f5cc352012-09-19 17:26:53 -0700120 ret = s->insn_bits(dev, s, insn, data);
Greg Kroah-Hartman3781bc52010-05-03 14:54:34 -0700121 break;
122 case INSN_CONFIG:
123 /* XXX should check instruction length */
H Hartley Sweeten1f5cc352012-09-19 17:26:53 -0700124 ret = s->insn_config(dev, s, insn, data);
Greg Kroah-Hartman3781bc52010-05-03 14:54:34 -0700125 break;
126 default:
127 ret = -EINVAL;
128 break;
129 }
130
131 s->busy = NULL;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530132error:
David Schleefb79a7a22008-11-14 15:58:23 -0800133
Ian Abbott69e2387f2013-11-08 15:03:39 +0000134 mutex_unlock(&dev->mutex);
David Schleefb79a7a22008-11-14 15:58:23 -0800135 return ret;
136}
137
Ian Abbott16d2d3c2013-08-23 14:45:07 +0100138int 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}
157EXPORT_SYMBOL_GPL(comedi_dio_get_config);
158
Greg Kroah-Hartman472dfe72010-05-03 15:01:50 -0700159int comedi_dio_config(struct comedi_device *dev, unsigned int subdev,
160 unsigned int chan, unsigned int io)
Greg Kroah-Hartmana1525752010-05-03 14:44:55 -0700161{
162 struct comedi_insn insn;
163
164 memset(&insn, 0, sizeof(insn));
165 insn.insn = INSN_CONFIG;
166 insn.n = 1;
Greg Kroah-Hartmana1525752010-05-03 14:44:55 -0700167 insn.subdev = subdev;
168 insn.chanspec = CR_PACK(chan, 0, 0);
169
H Hartley Sweeten1f5cc352012-09-19 17:26:53 -0700170 return comedi_do_insn(dev, &insn, &io);
Greg Kroah-Hartmana1525752010-05-03 14:44:55 -0700171}
H Hartley Sweeten5660e742013-04-12 10:11:54 -0700172EXPORT_SYMBOL_GPL(comedi_dio_config);
Greg Kroah-Hartmana1525752010-05-03 14:44:55 -0700173
Ian Abbott0f3ce1a2013-08-23 14:45:08 +0100174int comedi_dio_bitfield2(struct comedi_device *dev, unsigned int subdev,
175 unsigned int mask, unsigned int *bits,
176 unsigned int base_channel)
Greg Kroah-Hartmana1525752010-05-03 14:44:55 -0700177{
178 struct comedi_insn insn;
179 unsigned int data[2];
Ian Abbott0f3ce1a2013-08-23 14:45:08 +0100180 unsigned int n_chan;
181 unsigned int shift;
Greg Kroah-Hartmana1525752010-05-03 14:44:55 -0700182 int ret;
183
Ian Abbott0f3ce1a2013-08-23 14:45:08 +0100184 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
Greg Kroah-Hartmana1525752010-05-03 14:44:55 -0700189 memset(&insn, 0, sizeof(insn));
190 insn.insn = INSN_BITS;
Ian Abbott0f3ce1a2013-08-23 14:45:08 +0100191 insn.chanspec = base_channel;
Greg Kroah-Hartmana1525752010-05-03 14:44:55 -0700192 insn.n = 2;
Greg Kroah-Hartmana1525752010-05-03 14:44:55 -0700193 insn.subdev = subdev;
194
195 data[0] = mask;
196 data[1] = *bits;
197
Ian Abbott0f3ce1a2013-08-23 14:45:08 +0100198 /*
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
H Hartley Sweeten1f5cc352012-09-19 17:26:53 -0700213 ret = comedi_do_insn(dev, &insn, data);
Ian Abbott0f3ce1a2013-08-23 14:45:08 +0100214 *bits = data[1] >> shift;
Greg Kroah-Hartmana1525752010-05-03 14:44:55 -0700215 return ret;
216}
Ian Abbott0f3ce1a2013-08-23 14:45:08 +0100217EXPORT_SYMBOL_GPL(comedi_dio_bitfield2);
Greg Kroah-Hartmana1525752010-05-03 14:44:55 -0700218
Greg Kroah-Hartman472dfe72010-05-03 15:01:50 -0700219int comedi_find_subdevice_by_type(struct comedi_device *dev, int type,
220 unsigned int subd)
Greg Kroah-Hartmana1525752010-05-03 14:44:55 -0700221{
H Hartley Sweeten5818e702012-09-05 18:59:26 -0700222 struct comedi_subdevice *s;
Ian Abbott69e2387f2013-11-08 15:03:39 +0000223 int ret = -ENODEV;
H Hartley Sweeten5818e702012-09-05 18:59:26 -0700224
Ian Abbott69e2387f2013-11-08 15:03:39 +0000225 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;
Greg Kroah-Hartmana1525752010-05-03 14:44:55 -0700236}
H Hartley Sweeten5660e742013-04-12 10:11:54 -0700237EXPORT_SYMBOL_GPL(comedi_find_subdevice_by_type);
Greg Kroah-Hartmana1525752010-05-03 14:44:55 -0700238
Greg Kroah-Hartman472dfe72010-05-03 15:01:50 -0700239int comedi_get_n_channels(struct comedi_device *dev, unsigned int subdevice)
Greg Kroah-Hartmana1525752010-05-03 14:44:55 -0700240{
Ian Abbott69e2387f2013-11-08 15:03:39 +0000241 int n;
Greg Kroah-Hartmana1525752010-05-03 14:44:55 -0700242
Ian Abbott69e2387f2013-11-08 15:03:39 +0000243 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;
Greg Kroah-Hartmana1525752010-05-03 14:44:55 -0700251}
H Hartley Sweeten5660e742013-04-12 10:11:54 -0700252EXPORT_SYMBOL_GPL(comedi_get_n_channels);