blob: ec7525a4f4f19537915131f60012a7a2596a6aca [file] [log] [blame]
Paul Cercueilbb4401d2014-02-28 16:10:49 +01001/*
2 * libiio - Library for interfacing industrial I/O (IIO) devices
3 *
4 * Copyright (C) 2014 Analog Devices, Inc.
5 * Author: Paul Cercueil <paul.cercueil@analog.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * */
18
Paul Cercueil0b2ce712014-02-17 15:04:18 +010019#include "debug.h"
20#include "iio-private.h"
21
Paul Cercueil1be57832014-03-11 16:27:16 +010022#include <dirent.h>
Paul Cercueil0b2ce712014-02-17 15:04:18 +010023#include <errno.h>
24#include <stdbool.h>
Paul Cercueil1be57832014-03-11 16:27:16 +010025#include <stddef.h>
Paul Cercueil0b2ce712014-02-17 15:04:18 +010026#include <stdio.h>
27#include <string.h>
Paul Cercueil1be57832014-03-11 16:27:16 +010028#include <sys/types.h>
29#include <sys/stat.h>
30#include <unistd.h>
Paul Cercueil0b2ce712014-02-17 15:04:18 +010031
32#define ARRAY_SIZE(x) (sizeof(x) ? sizeof(x) / sizeof((x)[0]) : 0)
33
Paul Cercueil167d3112014-02-18 12:23:53 +010034struct fn_map {
Paul Cercueil167d3112014-02-18 12:23:53 +010035 const char *attr;
36 char *filename;
37};
38
Paul Cercueileaab6582014-02-21 09:35:59 +010039struct iio_device_pdata {
Paul Cercueil44010ea2014-02-21 11:47:04 +010040 FILE *f;
Paul Cercueile6ebf492014-04-02 13:57:36 +020041 unsigned int samples_count;
Paul Cercueileaab6582014-02-21 09:35:59 +010042};
43
44struct iio_channel_pdata {
Paul Cercueila604e9a2014-02-21 09:50:25 +010045 struct fn_map *maps;
46 size_t nb_maps;
Paul Cercueileaab6582014-02-21 09:35:59 +010047};
48
Paul Cercueil0b2ce712014-02-17 15:04:18 +010049static const char * const device_attrs_blacklist[] = {
50 "dev",
51 "uevent",
52};
53
54static const char * const modifier_names[] = {
55 [IIO_MOD_X] = "x",
56 [IIO_MOD_Y] = "y",
57 [IIO_MOD_Z] = "z",
58 [IIO_MOD_ROOT_SUM_SQUARED_X_Y] = "sqrt(x^2+y^2)",
59 [IIO_MOD_SUM_SQUARED_X_Y_Z] = "x^2+y^2+z^2",
60 [IIO_MOD_LIGHT_BOTH] = "both",
61 [IIO_MOD_LIGHT_IR] = "ir",
62 [IIO_MOD_LIGHT_CLEAR] = "clear",
63 [IIO_MOD_LIGHT_RED] = "red",
64 [IIO_MOD_LIGHT_GREEN] = "green",
65 [IIO_MOD_LIGHT_BLUE] = "blue",
66};
67
Paul Cercueil0b2ce712014-02-17 15:04:18 +010068static void local_shutdown(struct iio_context *ctx)
69{
Paul Cercueil167d3112014-02-18 12:23:53 +010070 unsigned int i;
71
Paul Cercueileaab6582014-02-21 09:35:59 +010072 /* First, free the backend data stored in every device structure */
73 for (i = 0; i < ctx->nb_devices; i++) {
74 unsigned int j;
75 struct iio_device *dev = ctx->devices[i];
76 free(dev->pdata);
77
78 /* Free backend data stored in every channel structure */
79 for (j = 0; j < dev->nb_channels; j++) {
Paul Cercueila604e9a2014-02-21 09:50:25 +010080 unsigned int k;
Paul Cercueileaab6582014-02-21 09:35:59 +010081 struct iio_channel *chn = dev->channels[j];
Paul Cercueila604e9a2014-02-21 09:50:25 +010082 struct iio_channel_pdata *ch_pdata = chn->pdata;
83
84 for (k = 0; k < ch_pdata->nb_maps; k++)
85 free(ch_pdata->maps[k].filename);
86 if (ch_pdata->nb_maps)
87 free(ch_pdata->maps);
88 free(ch_pdata);
Paul Cercueileaab6582014-02-21 09:35:59 +010089 }
90 }
Paul Cercueil0b2ce712014-02-17 15:04:18 +010091}
92
93/** Shrinks the first nb characters of a string
94 * e.g. strcut("foobar", 4) replaces the content with "ar". */
95static void strcut(char *str, int nb)
96{
97 char *ptr = str + nb;
98 while (*ptr)
99 *str++ = *ptr++;
100 *str = 0;
101}
102
103static int set_channel_name(struct iio_channel *chn)
104{
105 if (chn->nb_attrs < 2)
106 return 0;
107
108 while (true) {
109 bool can_fix = true;
110 unsigned int i, len;
111 char *name;
112 const char *attr0 = chn->attrs[0];
113 const char *ptr = strchr(attr0, '_');
114 if (!ptr)
115 break;
116
117 len = ptr - attr0;
118 for (i = 1; can_fix && i < chn->nb_attrs; i++)
119 can_fix = !strncmp(attr0, chn->attrs[i], len);
120
121 if (!can_fix)
122 break;
123
124 if (chn->name) {
Paul Cercueil8c29e412014-04-07 09:46:45 +0200125 size_t nlen = strlen(chn->name) + len + 2;
126 name = malloc(nlen);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100127 if (!name)
128 return -ENOMEM;
Paul Cercueil8c29e412014-04-07 09:46:45 +0200129 snprintf(name, nlen, "%s_%.*s", chn->name, len, attr0);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100130 DEBUG("Fixing name of channel %s from %s to %s\n",
131 chn->id, chn->name, name);
Paul Cercueilbb618272014-02-20 11:35:52 +0100132 free(chn->name);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100133 } else {
134 name = malloc(len + 2);
135 if (!name)
136 return -ENOMEM;
Paul Cercueil8c29e412014-04-07 09:46:45 +0200137 snprintf(name, len + 2, "%.*s", len, attr0);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100138 DEBUG("Setting name of channel %s to %s\n",
139 chn->id, name);
140 }
141 chn->name = name;
142
143 /* Shrink the attribute name */
144 for (i = 0; i < chn->nb_attrs; i++)
Paul Cercueilbb618272014-02-20 11:35:52 +0100145 strcut(chn->attrs[i], len + 1);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100146 }
147
148 if (chn->name) {
149 unsigned int i;
150 for (i = 0; i < ARRAY_SIZE(modifier_names); i++) {
151 unsigned int len;
152
153 if (!modifier_names[i])
154 continue;
155
156 len = strlen(modifier_names[i]);
157 if (!strncmp(chn->name, modifier_names[i], len)) {
158 if (chn->name[len]) {
159 /* Shrink the modifier from the extended name */
Paul Cercueilbb618272014-02-20 11:35:52 +0100160 strcut(chn->name, len + 1);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100161 } else {
Paul Cercueilbb618272014-02-20 11:35:52 +0100162 free(chn->name);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100163 chn->name = NULL;
164 }
165 chn->modifier = i;
166 DEBUG("Detected modifier for channel %s: %s\n",
167 chn->id, modifier_names[i]);
168 break;
169 }
170 }
171 }
172 return 0;
173}
174
Paul Cercueil45c575d2014-03-20 15:14:01 +0100175static ssize_t local_read(const struct iio_device *dev,
176 void *dst, size_t len, uint32_t *mask, size_t words)
Paul Cercueil44010ea2014-02-21 11:47:04 +0100177{
178 ssize_t ret;
Paul Cercueilff778232014-03-24 14:23:08 +0100179 FILE *f = dev->pdata->f;
Paul Cercueil44010ea2014-02-21 11:47:04 +0100180 if (!f)
181 return -EBADF;
Paul Cercueilff778232014-03-24 14:23:08 +0100182 if (words != dev->words)
Paul Cercueil45c575d2014-03-20 15:14:01 +0100183 return -EINVAL;
184
Paul Cercueilff778232014-03-24 14:23:08 +0100185 memcpy(mask, dev->mask, words);
Paul Cercueil44010ea2014-02-21 11:47:04 +0100186 ret = fread(dst, 1, len, f);
187 if (ret)
188 return ret;
189 else if (feof(f))
190 return 0;
191 else
192 return -EIO;
193}
194
195static ssize_t local_write(const struct iio_device *dev,
196 const void *src, size_t len)
197{
198 ssize_t ret;
199 FILE *f = dev->pdata->f;
200 if (!f)
201 return -EBADF;
202 ret = fwrite(src, 1, len, f);
203 if (ret)
204 return ret;
205 else if (feof(f))
206 return 0;
207 else
208 return -EIO;
209}
210
Paul Cercueil167d3112014-02-18 12:23:53 +0100211static ssize_t local_read_dev_attr(const struct iio_device *dev,
212 const char *attr, char *dst, size_t len)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100213{
Paul Cercueil3e898302014-02-17 16:17:11 +0100214 FILE *f;
215 char buf[1024];
216 ssize_t ret;
217
Paul Cercueil1be57832014-03-11 16:27:16 +0100218 snprintf(buf, sizeof(buf), "/sys/bus/iio/devices/%s/%s", dev->id, attr);
Paul Cercueil3e898302014-02-17 16:17:11 +0100219 f = fopen(buf, "r");
220 if (!f)
221 return -errno;
222
223 ret = fread(dst, 1, len, f);
224 if (ret > 0)
225 dst[ret - 1] = '\0';
Paul Cercueil96dfedd2014-03-11 09:53:09 +0100226 fflush(f);
227 if (ferror(f))
228 ret = -EBUSY;
Paul Cercueil3e898302014-02-17 16:17:11 +0100229 fclose(f);
Paul Cercueil6e7f79e2014-04-04 12:27:24 +0200230 return ret ? ret : -EIO;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100231}
232
Paul Cercueil167d3112014-02-18 12:23:53 +0100233static ssize_t local_write_dev_attr(const struct iio_device *dev,
234 const char *attr, const char *src)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100235{
Paul Cercueil3e898302014-02-17 16:17:11 +0100236 FILE *f;
237 char buf[1024];
238 ssize_t ret;
239 size_t len = strlen(src) + 1;
240
Paul Cercueil1be57832014-03-11 16:27:16 +0100241 snprintf(buf, sizeof(buf), "/sys/bus/iio/devices/%s/%s", dev->id, attr);
Paul Cercueilc35d1ac2014-03-10 12:33:32 +0100242 f = fopen(buf, "r+");
Paul Cercueil3e898302014-02-17 16:17:11 +0100243 if (!f)
244 return -errno;
245
246 ret = fwrite(src, 1, len, f);
Paul Cercueil96dfedd2014-03-11 09:53:09 +0100247 fflush(f);
248 if (ferror(f))
249 ret = -EBUSY;
Paul Cercueil3e898302014-02-17 16:17:11 +0100250 fclose(f);
Paul Cercueil6e7f79e2014-04-04 12:27:24 +0200251 return ret ? ret : -EIO;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100252}
253
Paul Cercueil167d3112014-02-18 12:23:53 +0100254static const char * get_filename(const struct iio_channel *chn,
255 const char *attr)
256{
Paul Cercueila604e9a2014-02-21 09:50:25 +0100257 struct iio_channel_pdata *pdata = chn->pdata;
Paul Cercueil167d3112014-02-18 12:23:53 +0100258 struct fn_map *maps = pdata->maps;
259 unsigned int i;
260 for (i = 0; i < pdata->nb_maps; i++)
Paul Cercueila604e9a2014-02-21 09:50:25 +0100261 if (!strcmp(attr, maps[i].attr))
Paul Cercueil167d3112014-02-18 12:23:53 +0100262 return maps[i].filename;
263 return attr;
264}
265
266static ssize_t local_read_chn_attr(const struct iio_channel *chn,
267 const char *attr, char *dst, size_t len)
268{
269 attr = get_filename(chn, attr);
270 return local_read_dev_attr(chn->dev, attr, dst, len);
271}
272
273static ssize_t local_write_chn_attr(const struct iio_channel *chn,
274 const char *attr, const char *src)
275{
276 attr = get_filename(chn, attr);
277 return local_write_dev_attr(chn->dev, attr, src);
278}
279
Paul Cercueilff778232014-03-24 14:23:08 +0100280static int channel_write_state(const struct iio_channel *chn)
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100281{
Paul Cercueilff778232014-03-24 14:23:08 +0100282 char *en = iio_channel_is_enabled(chn) ? "1" : "0";
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100283 ssize_t ret = local_write_chn_attr(chn, "en", en);
284 if (ret < 0)
285 return (int) ret;
286 else
287 return 0;
288}
289
Paul Cercueile6ebf492014-04-02 13:57:36 +0200290static int local_open(const struct iio_device *dev,
291 size_t samples_count, uint32_t *mask, size_t nb)
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100292{
293 unsigned int i;
294 int ret;
295 char buf[1024];
296 struct iio_device_pdata *pdata = dev->pdata;
297 if (pdata->f)
298 return -EBUSY;
299
Paul Cercueilff778232014-03-24 14:23:08 +0100300 if (nb != dev->words)
Paul Cercueile1311222014-03-12 15:46:16 +0100301 return -EINVAL;
302
Paul Cercueil48e39452014-03-14 09:37:53 +0100303 ret = local_write_dev_attr(dev, "buffer/enable", "0");
Paul Cercueilf0aeae32014-04-02 13:52:26 +0200304 if (ret < 0)
Paul Cercueil48e39452014-03-14 09:37:53 +0100305 return ret;
306
Paul Cercueile6ebf492014-04-02 13:57:36 +0200307 if (samples_count) {
Paul Cercueil8c29e412014-04-07 09:46:45 +0200308 snprintf(buf, sizeof(buf),
309 "%lu", (unsigned long) samples_count);
Paul Cercueile6ebf492014-04-02 13:57:36 +0200310 ret = local_write_dev_attr(dev, "buffer/length", buf);
311 if (ret < 0)
312 return ret;
313 }
314
Paul Cercueil8c29e412014-04-07 09:46:45 +0200315 snprintf(buf, sizeof(buf), "/dev/%s", dev->id);
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100316 pdata->f = fopen(buf, "r+");
317 if (!pdata->f)
318 return -errno;
319
Paul Cercueilff778232014-03-24 14:23:08 +0100320 memcpy(dev->mask, mask, nb);
321
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100322 /* Enable channels */
323 for (i = 0; i < dev->nb_channels; i++) {
324 struct iio_channel *chn = dev->channels[i];
Paul Cercueile1311222014-03-12 15:46:16 +0100325 if (chn->index >= 0) {
Paul Cercueilff778232014-03-24 14:23:08 +0100326 ret = channel_write_state(chn);
Paul Cercueilf0aeae32014-04-02 13:52:26 +0200327 if (ret < 0)
Paul Cercueile1311222014-03-12 15:46:16 +0100328 goto err_close;
329 }
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100330 }
331
Paul Cercueile6ebf492014-04-02 13:57:36 +0200332 pdata->samples_count = samples_count;
333
334 /* If opened with samples_count == 0, we probably want DDS mode;
335 * then the buffer will only be enabled when closing the device. */
336 if (samples_count > 0)
337 ret = local_write_dev_attr(dev, "buffer/enable", "1");
Paul Cercueilf0aeae32014-04-02 13:52:26 +0200338 if (ret < 0)
Paul Cercueil45c575d2014-03-20 15:14:01 +0100339 goto err_close;
340
Paul Cercueil45c575d2014-03-20 15:14:01 +0100341 return 0;
Paul Cercueile1311222014-03-12 15:46:16 +0100342err_close:
343 fclose(pdata->f);
344 pdata->f = NULL;
345 return ret;
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100346}
347
348static int local_close(const struct iio_device *dev)
349{
350 struct iio_device_pdata *pdata = dev->pdata;
Paul Cercueilfc8ec4a2014-03-17 16:42:22 +0100351 int ret;
352
353 if (!pdata->f)
354 return -EBADF;
355
356 ret = fclose(pdata->f);
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100357 if (ret)
358 return ret;
359
360 pdata->f = NULL;
Paul Cercueile6ebf492014-04-02 13:57:36 +0200361 ret = local_write_dev_attr(dev, "buffer/enable",
362 pdata->samples_count ? "0" : "1");
Paul Cercueilf0aeae32014-04-02 13:52:26 +0200363 if (ret < 0)
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100364 return ret;
365 else
366 return 0;
367}
368
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100369static int local_get_trigger(const struct iio_device *dev,
370 const struct iio_device **trigger)
371{
372 char buf[1024];
373 unsigned int i;
374 ssize_t nb = local_read_dev_attr(dev, "trigger/current_trigger",
375 buf, sizeof(buf));
Paul Cercueild09322a2014-04-02 13:50:18 +0200376 if (nb < 0) {
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100377 *trigger = NULL;
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100378 return (int) nb;
Paul Cercueild09322a2014-04-02 13:50:18 +0200379 }
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100380
381 nb = dev->ctx->nb_devices;
Paul Cercueil4e2f6652014-04-04 12:41:45 +0200382 for (i = 0; i < (size_t) nb; i++) {
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100383 const struct iio_device *cur = dev->ctx->devices[i];
384 if (cur->name && !strcmp(cur->name, buf)) {
385 *trigger = cur;
386 return 0;
387 }
388 }
389 return -ENXIO;
390}
391
392static int local_set_trigger(const struct iio_device *dev,
393 const struct iio_device *trigger)
394{
395 ssize_t nb;
396 const char *value = trigger ? trigger->name : "";
397 nb = local_write_dev_attr(dev, "trigger/current_trigger", value);
398 if (nb < 0)
399 return (int) nb;
400 else
401 return 0;
402}
403
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100404static bool is_channel(const char *attr)
405{
406 unsigned int i;
407 char *ptr = NULL;
Paul Cercueilec6857d2014-03-11 17:23:49 +0100408 if (!strncmp(attr, "in_timestamp_", sizeof("in_timestamp_") - 1))
409 return true;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100410 if (!strncmp(attr, "in_", 3))
411 ptr = strchr(attr + 3, '_');
412 else if (!strncmp(attr, "out_", 4))
413 ptr = strchr(attr + 4, '_');
414 if (!ptr)
415 return false;
416 if (*(ptr - 1) >= '0' && *(ptr - 1) <= '9')
417 return true;
418 for (i = 0; i < ARRAY_SIZE(modifier_names); i++)
419 if (modifier_names[i] && !strncmp(ptr + 1, modifier_names[i],
420 strlen(modifier_names[i])))
421 return true;
422 return false;
423}
424
425static char * get_channel_id(const char *attr)
426{
427 char *res, *ptr;
428 unsigned int i;
429
430 attr = strchr(attr, '_') + 1;
431 ptr = strchr(attr, '_');
432 for (i = 0; i < ARRAY_SIZE(modifier_names); i++) {
433 if (modifier_names[i] && !strncmp(ptr + 1, modifier_names[i],
434 strlen(modifier_names[i]))) {
435 ptr = strchr(ptr + 1, '_');
436 break;
437 }
438 }
439
440 res = malloc(ptr - attr + 1);
441 if (!res)
442 return NULL;
443
444 memcpy(res, attr, ptr - attr);
445 res[ptr - attr] = 0;
446 return res;
447}
448
449static char * get_short_attr_name(const char *attr)
450{
451 char *ptr = strchr(attr, '_') + 1;
452 ptr = strchr(ptr, '_') + 1;
453 return strdup(ptr);
454}
455
456static int read_device_name(struct iio_device *dev)
457{
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100458 char buf[1024];
Paul Cercueil8c29e412014-04-07 09:46:45 +0200459 ssize_t ret = iio_device_attr_read(dev, "name", buf, sizeof(buf));
Paul Cercueil30430c72014-02-17 16:52:37 +0100460 if (ret < 0)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100461 return ret;
Paul Cercueil30430c72014-02-17 16:52:37 +0100462 else if (ret == 0)
463 return -EIO;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100464
Paul Cercueil30430c72014-02-17 16:52:37 +0100465 dev->name = strdup(buf);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100466 if (!dev->name)
467 return -ENOMEM;
Paul Cercueil30430c72014-02-17 16:52:37 +0100468 else
469 return 0;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100470}
471
472static int add_attr_to_device(struct iio_device *dev, const char *attr)
473{
Paul Cercueilbb618272014-02-20 11:35:52 +0100474 char **attrs, *name;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100475 unsigned int i;
476
477 for (i = 0; i < ARRAY_SIZE(device_attrs_blacklist); i++)
478 if (!strcmp(device_attrs_blacklist[i], attr))
479 return 0;
480
481 if (!strcmp(attr, "name"))
482 return read_device_name(dev);
483
484 name = strdup(attr);
485 if (!name)
486 return -ENOMEM;
487
488 attrs = realloc(dev->attrs, (1 + dev->nb_attrs) * sizeof(char *));
489 if (!attrs) {
490 free(name);
491 return -ENOMEM;
492 }
493
494 attrs[dev->nb_attrs++] = name;
495 dev->attrs = attrs;
496 DEBUG("Added attr \'%s\' to device \'%s\'\n", attr, dev->id);
497 return 0;
498}
499
Paul Cercueile3fbd952014-03-11 16:58:33 +0100500static int add_attr_to_channel(struct iio_channel *chn,
501 const char *attr, const char *path)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100502{
Paul Cercueila604e9a2014-02-21 09:50:25 +0100503 struct iio_channel_pdata *pdata = chn->pdata;
Paul Cercueil167d3112014-02-18 12:23:53 +0100504 struct fn_map *maps;
Paul Cercueilbb618272014-02-20 11:35:52 +0100505 char **attrs, *fn, *name = get_short_attr_name(attr);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100506 if (!name)
507 return -ENOMEM;
508
Paul Cercueile3fbd952014-03-11 16:58:33 +0100509 fn = strdup(path);
Paul Cercueil167d3112014-02-18 12:23:53 +0100510 if (!fn)
511 goto err_free_name;
512
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100513 attrs = realloc(chn->attrs, (1 + chn->nb_attrs) * sizeof(char *));
Paul Cercueil167d3112014-02-18 12:23:53 +0100514 if (!attrs)
515 goto err_free_fn;
516
517 maps = realloc(pdata->maps,
518 (1 + pdata->nb_maps) * sizeof(struct fn_map));
519 if (!maps)
520 goto err_update_maps;
521
Paul Cercueil167d3112014-02-18 12:23:53 +0100522 maps[pdata->nb_maps].attr = name;
523 maps[pdata->nb_maps++].filename = fn;
524 pdata->maps = maps;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100525
526 attrs[chn->nb_attrs++] = name;
527 chn->attrs = attrs;
528 DEBUG("Added attr \'%s\' to channel \'%s\'\n", name, chn->id);
529 return 0;
Paul Cercueil167d3112014-02-18 12:23:53 +0100530
531err_update_maps:
532 /* the first realloc succeeded so we must update chn->attrs
533 * even if an error occured later */
534 chn->attrs = attrs;
535err_free_fn:
536 free(fn);
537err_free_name:
538 free(name);
539 return -ENOMEM;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100540}
541
542static int add_channel_to_device(struct iio_device *dev,
543 struct iio_channel *chn)
544{
545 struct iio_channel **channels = realloc(dev->channels,
546 (dev->nb_channels + 1) * sizeof(struct iio_channel *));
547 if (!channels)
548 return -ENOMEM;
549
550 channels[dev->nb_channels++] = chn;
551 dev->channels = channels;
552 DEBUG("Added channel \'%s\' to device \'%s\'\n", chn->id, dev->id);
553 return 0;
554}
555
556static int add_device_to_context(struct iio_context *ctx,
557 struct iio_device *dev)
558{
559 struct iio_device **devices = realloc(ctx->devices,
560 (ctx->nb_devices + 1) * sizeof(struct iio_device *));
561 if (!devices)
562 return -ENOMEM;
563
564 devices[ctx->nb_devices++] = dev;
565 ctx->devices = devices;
566 DEBUG("Added device \'%s\' to context \'%s\'\n", dev->id, ctx->name);
567 return 0;
568}
569
570static bool is_global_attr(struct iio_channel *chn, const char *attr)
571{
572 unsigned int i, len;
573 char *ptr;
574
Paul Cercueil35a01312014-02-20 10:56:57 +0100575 if (!chn->is_output && !strncmp(attr, "in_", 3))
576 attr += 3;
577 else if (chn->is_output && !strncmp(attr, "out_", 4))
578 attr += 4;
579 else
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100580 return false;
581
582 ptr = strchr(attr, '_');
583 if (!ptr)
584 return false;
585
586 len = ptr - attr;
587
588 if (strncmp(chn->id, attr, len))
589 return false;
590
591 DEBUG("Found match: %s and %s\n", chn->id, attr);
592 if (chn->id[len] >= '0' && chn->id[len] <= '9')
593 return true;
594 else if (chn->id[len] != '_')
595 return false;
596
597 for (i = 0; i < ARRAY_SIZE(modifier_names); i++)
598 if (modifier_names[i] &&
599 !strncmp(chn->id + len + 1, modifier_names[i],
600 strlen(modifier_names[i])))
601 return true;
602
603 return false;
604}
605
606static int detect_and_move_global_attrs(struct iio_device *dev)
607{
608 unsigned int i;
Paul Cercueilbb618272014-02-20 11:35:52 +0100609 char **ptr = dev->attrs;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100610
611 for (i = 0; i < dev->nb_attrs; i++) {
612 unsigned int j;
613 bool global = false;
614 const char *attr = dev->attrs[i];
615
616 for (j = 0; j < dev->nb_channels; j++) {
617 struct iio_channel *chn = dev->channels[j];
618 if (is_global_attr(chn, attr)) {
619 int ret;
620 global = true;
Paul Cercueile3fbd952014-03-11 16:58:33 +0100621 ret = add_attr_to_channel(chn, attr, attr);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100622 if (ret)
623 return ret;
624 }
625 }
626
627 if (global) {
Paul Cercueilbb618272014-02-20 11:35:52 +0100628 free(dev->attrs[i]);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100629 dev->attrs[i] = NULL;
630 }
631 }
632
633 for (i = 0; i < dev->nb_attrs; i++) {
634 if (dev->attrs[i])
635 *ptr++ = dev->attrs[i];
636 }
637
638 dev->nb_attrs = ptr - dev->attrs;
639 return 0;
640}
641
642static struct iio_channel *create_channel(struct iio_device *dev,
Paul Cercueile3fbd952014-03-11 16:58:33 +0100643 char *id, const char *attr, const char *path)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100644{
645 struct iio_channel *chn = calloc(1, sizeof(*chn));
646 if (!chn)
647 return NULL;
648
Paul Cercueileaab6582014-02-21 09:35:59 +0100649 chn->pdata = calloc(1, sizeof(*chn->pdata));
650 if (!dev->pdata)
651 goto err_free_chn;
652
653 if (!strncmp(attr, "out_", 4))
Paul Cercueil35a01312014-02-20 10:56:57 +0100654 chn->is_output = true;
Paul Cercueileaab6582014-02-21 09:35:59 +0100655 else if (strncmp(attr, "in_", 3))
656 goto err_free_pdata;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100657
658 chn->dev = dev;
659 chn->id = id;
660 chn->modifier = IIO_NO_MOD;
661
Paul Cercueile3fbd952014-03-11 16:58:33 +0100662 if (!add_attr_to_channel(chn, attr, path))
Paul Cercueileaab6582014-02-21 09:35:59 +0100663 return chn;
664
665err_free_pdata:
666 free(chn->pdata);
667err_free_chn:
668 free(chn);
669 return NULL;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100670}
671
Paul Cercueile3fbd952014-03-11 16:58:33 +0100672static int add_attr_or_channel_helper(struct iio_device *dev,
673 const char *path, const char *prefix)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100674{
Paul Cercueil1be57832014-03-11 16:27:16 +0100675 int ret;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100676 unsigned int i;
Paul Cercueil1be57832014-03-11 16:27:16 +0100677 struct iio_channel *chn;
Paul Cercueile3fbd952014-03-11 16:58:33 +0100678 char buf[1024], *channel_id;
Paul Cercueil1be57832014-03-11 16:27:16 +0100679 const char *name = strrchr(path, '/') + 1;
Paul Cercueile3fbd952014-03-11 16:58:33 +0100680 if (!prefix) {
681 path = name;
682 } else {
683 snprintf(buf, sizeof(buf), "%s/%s", prefix, name);
684 path = buf;
685 }
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100686
Paul Cercueil1be57832014-03-11 16:27:16 +0100687 if (!is_channel(name))
688 return add_attr_to_device(dev, name);
689
690 channel_id = get_channel_id(name);
691 if (!channel_id)
692 return -ENOMEM;
693
694 for (i = 0; i < dev->nb_channels; i++) {
695 chn = dev->channels[i];
Paul Cercueil5248e792014-03-31 14:39:10 +0200696 if (!strcmp(chn->id, channel_id)
697 && chn->is_output == (name[0] == 'o')) {
Paul Cercueil1be57832014-03-11 16:27:16 +0100698 free(channel_id);
Paul Cercueile3fbd952014-03-11 16:58:33 +0100699 return add_attr_to_channel(chn, name, path);
Paul Cercueil1be57832014-03-11 16:27:16 +0100700 }
701 }
702
Paul Cercueile3fbd952014-03-11 16:58:33 +0100703 chn = create_channel(dev, channel_id, name, path);
Paul Cercueil1be57832014-03-11 16:27:16 +0100704 if (!chn) {
705 free(channel_id);
706 return -ENXIO;
707 }
708 ret = add_channel_to_device(dev, chn);
709 if (ret)
710 free_channel(chn);
711 return ret;
712}
713
Paul Cercueile3fbd952014-03-11 16:58:33 +0100714static int add_attr_or_channel(void *d, const char *path)
715{
716 return add_attr_or_channel_helper((struct iio_device *) d, path, NULL);
717}
718
719static int add_scan_element(void *d, const char *path)
720{
721 return add_attr_or_channel_helper((struct iio_device *) d,
722 path, "scan_elements");
723}
724
Paul Cercueil1be57832014-03-11 16:27:16 +0100725static int foreach_in_dir(void *d, const char *path, bool is_dir,
726 int (*callback)(void *, const char *))
727{
728 long name_max;
729 struct dirent *entry, *result;
730 DIR *dir = opendir(path);
731 if (!dir)
732 return -errno;
733
734 name_max = pathconf(path, _PC_NAME_MAX);
735 if (name_max == -1)
736 name_max = 255;
737 entry = malloc(offsetof(struct dirent, d_name) + name_max + 1);
738 if (!entry) {
739 closedir(dir);
740 return -ENOMEM;
741 }
742
743 while (true) {
744 struct stat st;
745 char buf[1024];
746 int ret = readdir_r(dir, entry, &result);
747 if (ret) {
748 strerror_r(ret, buf, sizeof(buf));
749 ERROR("Unable to open directory %s: %s\n", path, buf);
750 free(entry);
751 closedir(dir);
752 return ret;
753 }
754 if (!result)
755 break;
756
757 snprintf(buf, sizeof(buf), "%s/%s", path, entry->d_name);
758 if (stat(buf, &st) < 0) {
759 ret = -errno;
Paul Cercueilc21300c2014-03-13 09:54:14 +0100760 strerror_r(errno, buf, sizeof(buf));
Paul Cercueil1be57832014-03-11 16:27:16 +0100761 ERROR("Unable to stat file: %s\n", buf);
762 free(entry);
763 closedir(dir);
764 return ret;
765 }
766
767 if (is_dir && S_ISDIR(st.st_mode) && entry->d_name[0] != '.')
768 ret = callback(d, buf);
769 else if (!is_dir && S_ISREG(st.st_mode))
770 ret = callback(d, buf);
771 else
772 continue;
773
774 if (ret < 0) {
775 free(entry);
776 closedir(dir);
777 return ret;
778 }
779 }
780
781 free(entry);
782 closedir(dir);
783 return 0;
784}
785
Paul Cercueile3fbd952014-03-11 16:58:33 +0100786static int add_scan_elements(struct iio_device *dev, const char *devpath)
787{
788 struct stat st;
789 char buf[1024];
790 snprintf(buf, sizeof(buf), "%s/scan_elements", devpath);
791
792 if (!stat(buf, &st) && S_ISDIR(st.st_mode)) {
793 int ret = foreach_in_dir(dev, buf, false, add_scan_element);
794 if (ret < 0)
795 return ret;
796 }
797
798 return 0;
799}
800
Paul Cercueil1be57832014-03-11 16:27:16 +0100801static int create_device(void *d, const char *path)
802{
Paul Cercueilff778232014-03-24 14:23:08 +0100803 uint32_t *mask = NULL;
Paul Cercueil1be57832014-03-11 16:27:16 +0100804 unsigned int i;
805 int ret;
806 struct iio_context *ctx = d;
807 struct iio_device *dev = calloc(1, sizeof(*dev));
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100808 if (!dev)
Paul Cercueil1be57832014-03-11 16:27:16 +0100809 return -ENOMEM;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100810
Paul Cercueileaab6582014-02-21 09:35:59 +0100811 dev->pdata = calloc(1, sizeof(*dev->pdata));
812 if (!dev->pdata) {
813 free(dev);
Paul Cercueil1be57832014-03-11 16:27:16 +0100814 return -ENOMEM;
Paul Cercueileaab6582014-02-21 09:35:59 +0100815 }
816
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100817 dev->ctx = ctx;
Paul Cercueil1be57832014-03-11 16:27:16 +0100818 dev->id = strdup(strrchr(path, '/') + 1);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100819 if (!dev->id) {
Paul Cercueileaab6582014-02-21 09:35:59 +0100820 free(dev->pdata);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100821 free(dev);
Paul Cercueil1be57832014-03-11 16:27:16 +0100822 return -ENOMEM;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100823 }
824
Paul Cercueil1be57832014-03-11 16:27:16 +0100825 ret = foreach_in_dir(dev, path, false, add_attr_or_channel);
826 if (ret < 0) {
827 free_device(dev);
828 return ret;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100829 }
830
Paul Cercueil54913ac2014-03-31 14:48:19 +0200831 for (i = 0; i < dev->nb_channels; i++)
832 set_channel_name(dev->channels[i]);
833
834 ret = detect_and_move_global_attrs(dev);
Paul Cercueile3fbd952014-03-11 16:58:33 +0100835 if (ret < 0) {
836 free_device(dev);
837 return ret;
838 }
839
Paul Cercueil54913ac2014-03-31 14:48:19 +0200840 ret = add_scan_elements(dev, path);
Paul Cercueil1be57832014-03-11 16:27:16 +0100841 if (ret < 0) {
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100842 free_device(dev);
Paul Cercueil1be57832014-03-11 16:27:16 +0100843 return ret;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100844 }
845
Paul Cercueilff778232014-03-24 14:23:08 +0100846 dev->words = (dev->nb_channels + 31) / 32;
847 if (dev->words) {
848 mask = calloc(dev->words, sizeof(*mask));
Paul Cercueil45c575d2014-03-20 15:14:01 +0100849 if (!mask) {
850 free_device(dev);
851 return ret;
852 }
Paul Cercueil45c575d2014-03-20 15:14:01 +0100853 }
854
Paul Cercueilff778232014-03-24 14:23:08 +0100855 dev->mask = mask;
Paul Cercueil45c575d2014-03-20 15:14:01 +0100856
Paul Cercueil1be57832014-03-11 16:27:16 +0100857 ret = add_device_to_context(ctx, dev);
858 if (ret < 0)
859 free_device(dev);
860 return ret;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100861}
862
863static struct iio_backend_ops local_ops = {
Paul Cercueil44010ea2014-02-21 11:47:04 +0100864 .open = local_open,
865 .close = local_close,
866 .read = local_read,
867 .write = local_write,
Paul Cercueil167d3112014-02-18 12:23:53 +0100868 .read_device_attr = local_read_dev_attr,
869 .write_device_attr = local_write_dev_attr,
870 .read_channel_attr = local_read_chn_attr,
871 .write_channel_attr = local_write_chn_attr,
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100872 .get_trigger = local_get_trigger,
873 .set_trigger = local_set_trigger,
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100874 .shutdown = local_shutdown,
875};
876
877struct iio_context * iio_create_local_context(void)
878{
Paul Cercueil1be57832014-03-11 16:27:16 +0100879 int ret;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100880 struct iio_context *ctx = calloc(1, sizeof(*ctx));
881 if (!ctx) {
882 ERROR("Unable to allocate memory\n");
883 return NULL;
884 }
885
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100886 ctx->ops = &local_ops;
887 ctx->name = "local";
888
Paul Cercueil1be57832014-03-11 16:27:16 +0100889 ret = foreach_in_dir(ctx, "/sys/bus/iio/devices", true, create_device);
890 if (ret < 0) {
891 char buf[1024];
Paul Cercueil4c6729d2014-04-04 17:24:41 +0200892 strerror_r(-ret, buf, sizeof(buf));
Paul Cercueil1be57832014-03-11 16:27:16 +0100893 ERROR("Unable to create context: %s\n", buf);
894 iio_context_destroy(ctx);
Paul Cercueil4c6729d2014-04-04 17:24:41 +0200895 return NULL;
896 }
897
898 ret = iio_context_init(ctx);
899 if (ret < 0) {
900 char buf[1024];
901 strerror_r(-ret, buf, sizeof(buf));
902 ERROR("Unable to initialize context: %s\n", buf);
903 iio_context_destroy(ctx);
Paul Cercueil1be57832014-03-11 16:27:16 +0100904 ctx = NULL;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100905 }
Paul Cercueilae88fde2014-03-12 11:47:10 +0100906
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100907 return ctx;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100908}