]> Pileus Git - ~andy/linux/blob - drivers/iio/inkern.c
iio: Simplify iio_map_array_unregister API
[~andy/linux] / drivers / iio / inkern.c
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 /*
58  * Remove all map entries associated with the given iio device
59  */
60 int iio_map_array_unregister(struct iio_dev *indio_dev)
61 {
62         int ret = -ENODEV;
63         struct iio_map_internal *mapi;
64         struct list_head *pos, *tmp;
65
66         mutex_lock(&iio_map_list_lock);
67         list_for_each_safe(pos, tmp, &iio_map_list) {
68                 mapi = list_entry(pos, struct iio_map_internal, l);
69                 if (indio_dev == mapi->indio_dev) {
70                         list_del(&mapi->l);
71                         kfree(mapi);
72                         ret = 0;
73                 }
74         }
75         mutex_unlock(&iio_map_list_lock);
76         return ret;
77 }
78 EXPORT_SYMBOL_GPL(iio_map_array_unregister);
79
80 static const struct iio_chan_spec
81 *iio_chan_spec_from_name(const struct iio_dev *indio_dev, const char *name)
82 {
83         int i;
84         const struct iio_chan_spec *chan = NULL;
85
86         for (i = 0; i < indio_dev->num_channels; i++)
87                 if (indio_dev->channels[i].datasheet_name &&
88                     strcmp(name, indio_dev->channels[i].datasheet_name) == 0) {
89                         chan = &indio_dev->channels[i];
90                         break;
91                 }
92         return chan;
93 }
94
95
96 struct iio_channel *iio_channel_get(const char *name, const char *channel_name)
97 {
98         struct iio_map_internal *c_i = NULL, *c = NULL;
99         struct iio_channel *channel;
100         int err;
101
102         if (name == NULL && channel_name == NULL)
103                 return ERR_PTR(-ENODEV);
104
105         /* first find matching entry the channel map */
106         mutex_lock(&iio_map_list_lock);
107         list_for_each_entry(c_i, &iio_map_list, l) {
108                 if ((name && strcmp(name, c_i->map->consumer_dev_name) != 0) ||
109                     (channel_name &&
110                      strcmp(channel_name, c_i->map->consumer_channel) != 0))
111                         continue;
112                 c = c_i;
113                 iio_device_get(c->indio_dev);
114                 break;
115         }
116         mutex_unlock(&iio_map_list_lock);
117         if (c == NULL)
118                 return ERR_PTR(-ENODEV);
119
120         channel = kzalloc(sizeof(*channel), GFP_KERNEL);
121         if (channel == NULL) {
122                 err = -ENOMEM;
123                 goto error_no_mem;
124         }
125
126         channel->indio_dev = c->indio_dev;
127
128         if (c->map->adc_channel_label) {
129                 channel->channel =
130                         iio_chan_spec_from_name(channel->indio_dev,
131                                                 c->map->adc_channel_label);
132
133                 if (channel->channel == NULL) {
134                         err = -EINVAL;
135                         goto error_no_chan;
136                 }
137         }
138
139         return channel;
140
141 error_no_chan:
142         kfree(channel);
143 error_no_mem:
144         iio_device_put(c->indio_dev);
145         return ERR_PTR(err);
146 }
147 EXPORT_SYMBOL_GPL(iio_channel_get);
148
149 void iio_channel_release(struct iio_channel *channel)
150 {
151         iio_device_put(channel->indio_dev);
152         kfree(channel);
153 }
154 EXPORT_SYMBOL_GPL(iio_channel_release);
155
156 struct iio_channel *iio_channel_get_all(struct device *dev)
157 {
158         const char *name;
159         struct iio_channel *chans;
160         struct iio_map_internal *c = NULL;
161         int nummaps = 0;
162         int mapind = 0;
163         int i, ret;
164
165         if (dev == NULL)
166                 return ERR_PTR(-EINVAL);
167         name = dev_name(dev);
168
169         mutex_lock(&iio_map_list_lock);
170         /* first count the matching maps */
171         list_for_each_entry(c, &iio_map_list, l)
172                 if (name && strcmp(name, c->map->consumer_dev_name) != 0)
173                         continue;
174                 else
175                         nummaps++;
176
177         if (nummaps == 0) {
178                 ret = -ENODEV;
179                 goto error_ret;
180         }
181
182         /* NULL terminated array to save passing size */
183         chans = kzalloc(sizeof(*chans)*(nummaps + 1), GFP_KERNEL);
184         if (chans == NULL) {
185                 ret = -ENOMEM;
186                 goto error_ret;
187         }
188
189         /* for each map fill in the chans element */
190         list_for_each_entry(c, &iio_map_list, l) {
191                 if (name && strcmp(name, c->map->consumer_dev_name) != 0)
192                         continue;
193                 chans[mapind].indio_dev = c->indio_dev;
194                 chans[mapind].data = c->map->consumer_data;
195                 chans[mapind].channel =
196                         iio_chan_spec_from_name(chans[mapind].indio_dev,
197                                                 c->map->adc_channel_label);
198                 if (chans[mapind].channel == NULL) {
199                         ret = -EINVAL;
200                         goto error_free_chans;
201                 }
202                 iio_device_get(chans[mapind].indio_dev);
203                 mapind++;
204         }
205         if (mapind == 0) {
206                 ret = -ENODEV;
207                 goto error_free_chans;
208         }
209         mutex_unlock(&iio_map_list_lock);
210
211         return chans;
212
213 error_free_chans:
214         for (i = 0; i < nummaps; i++)
215                 iio_device_put(chans[i].indio_dev);
216         kfree(chans);
217 error_ret:
218         mutex_unlock(&iio_map_list_lock);
219
220         return ERR_PTR(ret);
221 }
222 EXPORT_SYMBOL_GPL(iio_channel_get_all);
223
224 void iio_channel_release_all(struct iio_channel *channels)
225 {
226         struct iio_channel *chan = &channels[0];
227
228         while (chan->indio_dev) {
229                 iio_device_put(chan->indio_dev);
230                 chan++;
231         }
232         kfree(channels);
233 }
234 EXPORT_SYMBOL_GPL(iio_channel_release_all);
235
236 static int iio_channel_read(struct iio_channel *chan, int *val, int *val2,
237         enum iio_chan_info_enum info)
238 {
239         int unused;
240
241         if (val2 == NULL)
242                 val2 = &unused;
243
244         return chan->indio_dev->info->read_raw(chan->indio_dev, chan->channel,
245                                                 val, val2, info);
246 }
247
248 int iio_read_channel_raw(struct iio_channel *chan, int *val)
249 {
250         int ret;
251
252         mutex_lock(&chan->indio_dev->info_exist_lock);
253         if (chan->indio_dev->info == NULL) {
254                 ret = -ENODEV;
255                 goto err_unlock;
256         }
257
258         ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
259 err_unlock:
260         mutex_unlock(&chan->indio_dev->info_exist_lock);
261
262         return ret;
263 }
264 EXPORT_SYMBOL_GPL(iio_read_channel_raw);
265
266 static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan,
267         int raw, int *processed, unsigned int scale)
268 {
269         int scale_type, scale_val, scale_val2, offset;
270         s64 raw64 = raw;
271         int ret;
272
273         ret = iio_channel_read(chan, &offset, NULL, IIO_CHAN_INFO_SCALE);
274         if (ret == 0)
275                 raw64 += offset;
276
277         scale_type = iio_channel_read(chan, &scale_val, &scale_val2,
278                                         IIO_CHAN_INFO_SCALE);
279         if (scale_type < 0)
280                 return scale_type;
281
282         switch (scale_type) {
283         case IIO_VAL_INT:
284                 *processed = raw64 * scale_val;
285                 break;
286         case IIO_VAL_INT_PLUS_MICRO:
287                 if (scale_val2 < 0)
288                         *processed = -raw64 * scale_val;
289                 else
290                         *processed = raw64 * scale_val;
291                 *processed += div_s64(raw64 * (s64)scale_val2 * scale,
292                                       1000000LL);
293                 break;
294         case IIO_VAL_INT_PLUS_NANO:
295                 if (scale_val2 < 0)
296                         *processed = -raw64 * scale_val;
297                 else
298                         *processed = raw64 * scale_val;
299                 *processed += div_s64(raw64 * (s64)scale_val2 * scale,
300                                       1000000000LL);
301                 break;
302         case IIO_VAL_FRACTIONAL:
303                 *processed = div_s64(raw64 * (s64)scale_val * scale,
304                                      scale_val2);
305                 break;
306         case IIO_VAL_FRACTIONAL_LOG2:
307                 *processed = (raw64 * (s64)scale_val * scale) >> scale_val2;
308                 break;
309         default:
310                 return -EINVAL;
311         }
312
313         return 0;
314 }
315
316 int iio_convert_raw_to_processed(struct iio_channel *chan, int raw,
317         int *processed, unsigned int scale)
318 {
319         int ret;
320
321         mutex_lock(&chan->indio_dev->info_exist_lock);
322         if (chan->indio_dev->info == NULL) {
323                 ret = -ENODEV;
324                 goto err_unlock;
325         }
326
327         ret = iio_convert_raw_to_processed_unlocked(chan, raw, processed,
328                                                         scale);
329 err_unlock:
330         mutex_unlock(&chan->indio_dev->info_exist_lock);
331
332         return ret;
333 }
334 EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed);
335
336 int iio_read_channel_processed(struct iio_channel *chan, int *val)
337 {
338         int ret;
339
340         mutex_lock(&chan->indio_dev->info_exist_lock);
341         if (chan->indio_dev->info == NULL) {
342                 ret = -ENODEV;
343                 goto err_unlock;
344         }
345
346         if (iio_channel_has_info(chan->channel, IIO_CHAN_INFO_PROCESSED)) {
347                 ret = iio_channel_read(chan, val, NULL,
348                                        IIO_CHAN_INFO_PROCESSED);
349         } else {
350                 ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
351                 if (ret < 0)
352                         goto err_unlock;
353                 ret = iio_convert_raw_to_processed_unlocked(chan, *val, val, 1);
354         }
355
356 err_unlock:
357         mutex_unlock(&chan->indio_dev->info_exist_lock);
358
359         return ret;
360 }
361 EXPORT_SYMBOL_GPL(iio_read_channel_processed);
362
363 int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2)
364 {
365         int ret;
366
367         mutex_lock(&chan->indio_dev->info_exist_lock);
368         if (chan->indio_dev->info == NULL) {
369                 ret = -ENODEV;
370                 goto err_unlock;
371         }
372
373         ret = iio_channel_read(chan, val, val2, IIO_CHAN_INFO_SCALE);
374 err_unlock:
375         mutex_unlock(&chan->indio_dev->info_exist_lock);
376
377         return ret;
378 }
379 EXPORT_SYMBOL_GPL(iio_read_channel_scale);
380
381 int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type)
382 {
383         int ret = 0;
384         /* Need to verify underlying driver has not gone away */
385
386         mutex_lock(&chan->indio_dev->info_exist_lock);
387         if (chan->indio_dev->info == NULL) {
388                 ret = -ENODEV;
389                 goto err_unlock;
390         }
391
392         *type = chan->channel->type;
393 err_unlock:
394         mutex_unlock(&chan->indio_dev->info_exist_lock);
395
396         return ret;
397 }
398 EXPORT_SYMBOL_GPL(iio_get_channel_type);