]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/staging/iio/inkern.c
staging:iio:core add missing increment of loop index in iio_map_array_unregister()
[karo-tx-linux.git] / drivers / staging / 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 "iio.h"
15 #include "iio_core.h"
16 #include "machine.h"
17 #include "driver.h"
18 #include "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,
96                          const char *name)
97 {
98         int i;
99         const struct iio_chan_spec *chan = NULL;
100
101         for (i = 0; i < indio_dev->num_channels; i++)
102                 if (indio_dev->channels[i].datasheet_name &&
103                     strcmp(name, indio_dev->channels[i].datasheet_name) == 0) {
104                         chan = &indio_dev->channels[i];
105                         break;
106                 }
107         return chan;
108 }
109
110
111 struct iio_channel *iio_st_channel_get(const char *name,
112                                        const char *channel_name)
113 {
114         struct iio_map_internal *c_i = NULL, *c = NULL;
115         struct iio_channel *channel;
116
117         if (name == NULL && channel_name == NULL)
118                 return ERR_PTR(-ENODEV);
119
120         /* first find matching entry the channel map */
121         mutex_lock(&iio_map_list_lock);
122         list_for_each_entry(c_i, &iio_map_list, l) {
123                 if ((name && strcmp(name, c_i->map->consumer_dev_name) != 0) ||
124                     (channel_name &&
125                      strcmp(channel_name, c_i->map->consumer_channel) != 0))
126                         continue;
127                 c = c_i;
128                 get_device(&c->indio_dev->dev);
129                 break;
130         }
131         mutex_unlock(&iio_map_list_lock);
132         if (c == NULL)
133                 return ERR_PTR(-ENODEV);
134
135         channel = kmalloc(sizeof(*channel), GFP_KERNEL);
136         if (channel == NULL)
137                 return ERR_PTR(-ENOMEM);
138
139         channel->indio_dev = c->indio_dev;
140
141         if (c->map->adc_channel_label)
142                 channel->channel =
143                         iio_chan_spec_from_name(channel->indio_dev,
144                                                 c->map->adc_channel_label);
145
146         return channel;
147 }
148 EXPORT_SYMBOL_GPL(iio_st_channel_get);
149
150 void iio_st_channel_release(struct iio_channel *channel)
151 {
152         put_device(&channel->indio_dev->dev);
153         kfree(channel);
154 }
155 EXPORT_SYMBOL_GPL(iio_st_channel_release);
156
157 struct iio_channel *iio_st_channel_get_all(const char *name)
158 {
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 (name == NULL)
166                 return ERR_PTR(-EINVAL);
167
168         mutex_lock(&iio_map_list_lock);
169         /* first count the matching maps */
170         list_for_each_entry(c, &iio_map_list, l)
171                 if (name && strcmp(name, c->map->consumer_dev_name) != 0)
172                         continue;
173                 else
174                         nummaps++;
175
176         if (nummaps == 0) {
177                 ret = -ENODEV;
178                 goto error_ret;
179         }
180
181         /* NULL terminated array to save passing size */
182         chans = kzalloc(sizeof(*chans)*(nummaps + 1), GFP_KERNEL);
183         if (chans == NULL) {
184                 ret = -ENOMEM;
185                 goto error_ret;
186         }
187
188         /* for each map fill in the chans element */
189         list_for_each_entry(c, &iio_map_list, l) {
190                 if (name && strcmp(name, c->map->consumer_dev_name) != 0)
191                         continue;
192                 chans[mapind].indio_dev = c->indio_dev;
193                 chans[mapind].channel =
194                         iio_chan_spec_from_name(chans[mapind].indio_dev,
195                                                 c->map->adc_channel_label);
196                 if (chans[mapind].channel == NULL) {
197                         ret = -EINVAL;
198                         put_device(&chans[mapind].indio_dev->dev);
199                         goto error_free_chans;
200                 }
201                 get_device(&chans[mapind].indio_dev->dev);
202                 mapind++;
203         }
204         mutex_unlock(&iio_map_list_lock);
205         if (mapind == 0) {
206                 ret = -ENODEV;
207                 goto error_free_chans;
208         }
209         return chans;
210
211 error_free_chans:
212         for (i = 0; i < nummaps; i++)
213                 if (chans[i].indio_dev)
214                         put_device(&chans[i].indio_dev->dev);
215         kfree(chans);
216 error_ret:
217         mutex_unlock(&iio_map_list_lock);
218
219         return ERR_PTR(ret);
220 }
221 EXPORT_SYMBOL_GPL(iio_st_channel_get_all);
222
223 void iio_st_channel_release_all(struct iio_channel *channels)
224 {
225         struct iio_channel *chan = &channels[0];
226
227         while (chan->indio_dev) {
228                 put_device(&chan->indio_dev->dev);
229                 chan++;
230         }
231         kfree(channels);
232 }
233 EXPORT_SYMBOL_GPL(iio_st_channel_release_all);
234
235 int iio_st_read_channel_raw(struct iio_channel *chan, int *val)
236 {
237         int val2, ret;
238
239         mutex_lock(&chan->indio_dev->info_exist_lock);
240         if (chan->indio_dev->info == NULL) {
241                 ret = -ENODEV;
242                 goto err_unlock;
243         }
244
245         ret = chan->indio_dev->info->read_raw(chan->indio_dev, chan->channel,
246                                               val, &val2, 0);
247 err_unlock:
248         mutex_unlock(&chan->indio_dev->info_exist_lock);
249
250         return ret;
251 }
252 EXPORT_SYMBOL_GPL(iio_st_read_channel_raw);
253
254 int iio_st_read_channel_scale(struct iio_channel *chan, int *val, int *val2)
255 {
256         int ret;
257
258         mutex_lock(&chan->indio_dev->info_exist_lock);
259         if (chan->indio_dev->info == NULL) {
260                 ret = -ENODEV;
261                 goto err_unlock;
262         }
263
264         ret = chan->indio_dev->info->read_raw(chan->indio_dev,
265                                               chan->channel,
266                                               val, val2,
267                                               IIO_CHAN_INFO_SCALE);
268 err_unlock:
269         mutex_unlock(&chan->indio_dev->info_exist_lock);
270
271         return ret;
272 }
273 EXPORT_SYMBOL_GPL(iio_st_read_channel_scale);
274
275 int iio_st_get_channel_type(struct iio_channel *chan,
276                             enum iio_chan_type *type)
277 {
278         int ret = 0;
279         /* Need to verify underlying driver has not gone away */
280
281         mutex_lock(&chan->indio_dev->info_exist_lock);
282         if (chan->indio_dev->info == NULL) {
283                 ret = -ENODEV;
284                 goto err_unlock;
285         }
286
287         *type = chan->channel->type;
288 err_unlock:
289         mutex_unlock(&chan->indio_dev->info_exist_lock);
290
291         return ret;
292 }
293 EXPORT_SYMBOL_GPL(iio_st_get_channel_type);