xref: /openbmc/linux/drivers/iio/inkern.c (revision 95e9fd10)
1 /* The industrial I/O core in kernel channel mapping
2  *
3  * Copyright (c) 2011 Jonathan Cameron
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 as published by
7  * the Free Software Foundation.
8  */
9 #include <linux/err.h>
10 #include <linux/export.h>
11 #include <linux/slab.h>
12 #include <linux/mutex.h>
13 
14 #include <linux/iio/iio.h>
15 #include "iio_core.h"
16 #include <linux/iio/machine.h>
17 #include <linux/iio/driver.h>
18 #include <linux/iio/consumer.h>
19 
20 struct iio_map_internal {
21 	struct iio_dev *indio_dev;
22 	struct iio_map *map;
23 	struct list_head l;
24 };
25 
26 static LIST_HEAD(iio_map_list);
27 static DEFINE_MUTEX(iio_map_list_lock);
28 
29 int iio_map_array_register(struct iio_dev *indio_dev, struct iio_map *maps)
30 {
31 	int i = 0, ret = 0;
32 	struct iio_map_internal *mapi;
33 
34 	if (maps == NULL)
35 		return 0;
36 
37 	mutex_lock(&iio_map_list_lock);
38 	while (maps[i].consumer_dev_name != NULL) {
39 		mapi = kzalloc(sizeof(*mapi), GFP_KERNEL);
40 		if (mapi == NULL) {
41 			ret = -ENOMEM;
42 			goto error_ret;
43 		}
44 		mapi->map = &maps[i];
45 		mapi->indio_dev = indio_dev;
46 		list_add(&mapi->l, &iio_map_list);
47 		i++;
48 	}
49 error_ret:
50 	mutex_unlock(&iio_map_list_lock);
51 
52 	return ret;
53 }
54 EXPORT_SYMBOL_GPL(iio_map_array_register);
55 
56 
57 /* Assumes the exact same array (e.g. memory locations)
58  * used at unregistration as used at registration rather than
59  * more complex checking of contents.
60  */
61 int iio_map_array_unregister(struct iio_dev *indio_dev,
62 			     struct iio_map *maps)
63 {
64 	int i = 0, ret = 0;
65 	bool found_it;
66 	struct iio_map_internal *mapi;
67 
68 	if (maps == NULL)
69 		return 0;
70 
71 	mutex_lock(&iio_map_list_lock);
72 	while (maps[i].consumer_dev_name != NULL) {
73 		found_it = false;
74 		list_for_each_entry(mapi, &iio_map_list, l)
75 			if (&maps[i] == mapi->map) {
76 				list_del(&mapi->l);
77 				kfree(mapi);
78 				found_it = true;
79 				break;
80 			}
81 		if (found_it == false) {
82 			ret = -ENODEV;
83 			goto error_ret;
84 		}
85 		i++;
86 	}
87 error_ret:
88 	mutex_unlock(&iio_map_list_lock);
89 
90 	return ret;
91 }
92 EXPORT_SYMBOL_GPL(iio_map_array_unregister);
93 
94 static const struct iio_chan_spec
95 *iio_chan_spec_from_name(const struct iio_dev *indio_dev, const char *name)
96 {
97 	int i;
98 	const struct iio_chan_spec *chan = NULL;
99 
100 	for (i = 0; i < indio_dev->num_channels; i++)
101 		if (indio_dev->channels[i].datasheet_name &&
102 		    strcmp(name, indio_dev->channels[i].datasheet_name) == 0) {
103 			chan = &indio_dev->channels[i];
104 			break;
105 		}
106 	return chan;
107 }
108 
109 
110 struct iio_channel *iio_channel_get(const char *name, const char *channel_name)
111 {
112 	struct iio_map_internal *c_i = NULL, *c = NULL;
113 	struct iio_channel *channel;
114 
115 	if (name == NULL && channel_name == NULL)
116 		return ERR_PTR(-ENODEV);
117 
118 	/* first find matching entry the channel map */
119 	mutex_lock(&iio_map_list_lock);
120 	list_for_each_entry(c_i, &iio_map_list, l) {
121 		if ((name && strcmp(name, c_i->map->consumer_dev_name) != 0) ||
122 		    (channel_name &&
123 		     strcmp(channel_name, c_i->map->consumer_channel) != 0))
124 			continue;
125 		c = c_i;
126 		iio_device_get(c->indio_dev);
127 		break;
128 	}
129 	mutex_unlock(&iio_map_list_lock);
130 	if (c == NULL)
131 		return ERR_PTR(-ENODEV);
132 
133 	channel = kmalloc(sizeof(*channel), GFP_KERNEL);
134 	if (channel == NULL)
135 		return ERR_PTR(-ENOMEM);
136 
137 	channel->indio_dev = c->indio_dev;
138 
139 	if (c->map->adc_channel_label)
140 		channel->channel =
141 			iio_chan_spec_from_name(channel->indio_dev,
142 						c->map->adc_channel_label);
143 
144 	return channel;
145 }
146 EXPORT_SYMBOL_GPL(iio_channel_get);
147 
148 void iio_channel_release(struct iio_channel *channel)
149 {
150 	iio_device_put(channel->indio_dev);
151 	kfree(channel);
152 }
153 EXPORT_SYMBOL_GPL(iio_channel_release);
154 
155 struct iio_channel *iio_channel_get_all(const char *name)
156 {
157 	struct iio_channel *chans;
158 	struct iio_map_internal *c = NULL;
159 	int nummaps = 0;
160 	int mapind = 0;
161 	int i, ret;
162 
163 	if (name == NULL)
164 		return ERR_PTR(-EINVAL);
165 
166 	mutex_lock(&iio_map_list_lock);
167 	/* first count the matching maps */
168 	list_for_each_entry(c, &iio_map_list, l)
169 		if (name && strcmp(name, c->map->consumer_dev_name) != 0)
170 			continue;
171 		else
172 			nummaps++;
173 
174 	if (nummaps == 0) {
175 		ret = -ENODEV;
176 		goto error_ret;
177 	}
178 
179 	/* NULL terminated array to save passing size */
180 	chans = kzalloc(sizeof(*chans)*(nummaps + 1), GFP_KERNEL);
181 	if (chans == NULL) {
182 		ret = -ENOMEM;
183 		goto error_ret;
184 	}
185 
186 	/* for each map fill in the chans element */
187 	list_for_each_entry(c, &iio_map_list, l) {
188 		if (name && strcmp(name, c->map->consumer_dev_name) != 0)
189 			continue;
190 		chans[mapind].indio_dev = c->indio_dev;
191 		chans[mapind].channel =
192 			iio_chan_spec_from_name(chans[mapind].indio_dev,
193 						c->map->adc_channel_label);
194 		if (chans[mapind].channel == NULL) {
195 			ret = -EINVAL;
196 			goto error_free_chans;
197 		}
198 		iio_device_get(chans[mapind].indio_dev);
199 		mapind++;
200 	}
201 	if (mapind == 0) {
202 		ret = -ENODEV;
203 		goto error_free_chans;
204 	}
205 	mutex_unlock(&iio_map_list_lock);
206 
207 	return chans;
208 
209 error_free_chans:
210 	for (i = 0; i < nummaps; i++)
211 		iio_device_put(chans[i].indio_dev);
212 	kfree(chans);
213 error_ret:
214 	mutex_unlock(&iio_map_list_lock);
215 
216 	return ERR_PTR(ret);
217 }
218 EXPORT_SYMBOL_GPL(iio_channel_get_all);
219 
220 void iio_channel_release_all(struct iio_channel *channels)
221 {
222 	struct iio_channel *chan = &channels[0];
223 
224 	while (chan->indio_dev) {
225 		iio_device_put(chan->indio_dev);
226 		chan++;
227 	}
228 	kfree(channels);
229 }
230 EXPORT_SYMBOL_GPL(iio_channel_release_all);
231 
232 int iio_read_channel_raw(struct iio_channel *chan, int *val)
233 {
234 	int val2, ret;
235 
236 	mutex_lock(&chan->indio_dev->info_exist_lock);
237 	if (chan->indio_dev->info == NULL) {
238 		ret = -ENODEV;
239 		goto err_unlock;
240 	}
241 
242 	ret = chan->indio_dev->info->read_raw(chan->indio_dev, chan->channel,
243 					      val, &val2, 0);
244 err_unlock:
245 	mutex_unlock(&chan->indio_dev->info_exist_lock);
246 
247 	return ret;
248 }
249 EXPORT_SYMBOL_GPL(iio_read_channel_raw);
250 
251 int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2)
252 {
253 	int ret;
254 
255 	mutex_lock(&chan->indio_dev->info_exist_lock);
256 	if (chan->indio_dev->info == NULL) {
257 		ret = -ENODEV;
258 		goto err_unlock;
259 	}
260 
261 	ret = chan->indio_dev->info->read_raw(chan->indio_dev,
262 					      chan->channel,
263 					      val, val2,
264 					      IIO_CHAN_INFO_SCALE);
265 err_unlock:
266 	mutex_unlock(&chan->indio_dev->info_exist_lock);
267 
268 	return ret;
269 }
270 EXPORT_SYMBOL_GPL(iio_read_channel_scale);
271 
272 int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type)
273 {
274 	int ret = 0;
275 	/* Need to verify underlying driver has not gone away */
276 
277 	mutex_lock(&chan->indio_dev->info_exist_lock);
278 	if (chan->indio_dev->info == NULL) {
279 		ret = -ENODEV;
280 		goto err_unlock;
281 	}
282 
283 	*type = chan->channel->type;
284 err_unlock:
285 	mutex_unlock(&chan->indio_dev->info_exist_lock);
286 
287 	return ret;
288 }
289 EXPORT_SYMBOL_GPL(iio_get_channel_type);
290