blob: 1ee3b6398dbd2f238eb63aa411f0b1a2dcd19b2e [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,
Paul Cercueil50c762a2014-04-14 15:55:43 +0200212 const char *attr, char *dst, size_t len, bool is_debug)
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 Cercueil50c762a2014-04-14 15:55:43 +0200218 if (is_debug)
219 snprintf(buf, sizeof(buf), "/sys/kernel/debug/iio/%s/%s",
220 dev->id, attr);
221 else
222 snprintf(buf, sizeof(buf), "/sys/bus/iio/devices/%s/%s",
223 dev->id, attr);
Paul Cercueil3e898302014-02-17 16:17:11 +0100224 f = fopen(buf, "r");
225 if (!f)
226 return -errno;
227
228 ret = fread(dst, 1, len, f);
229 if (ret > 0)
230 dst[ret - 1] = '\0';
Paul Cercueil96dfedd2014-03-11 09:53:09 +0100231 fflush(f);
232 if (ferror(f))
233 ret = -EBUSY;
Paul Cercueil3e898302014-02-17 16:17:11 +0100234 fclose(f);
Paul Cercueil6e7f79e2014-04-04 12:27:24 +0200235 return ret ? ret : -EIO;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100236}
237
Paul Cercueil167d3112014-02-18 12:23:53 +0100238static ssize_t local_write_dev_attr(const struct iio_device *dev,
Paul Cercueil50c762a2014-04-14 15:55:43 +0200239 const char *attr, const char *src, bool is_debug)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100240{
Paul Cercueil3e898302014-02-17 16:17:11 +0100241 FILE *f;
242 char buf[1024];
243 ssize_t ret;
244 size_t len = strlen(src) + 1;
245
Paul Cercueil50c762a2014-04-14 15:55:43 +0200246 if (is_debug)
247 snprintf(buf, sizeof(buf), "/sys/kernel/debug/iio/%s/%s",
248 dev->id, attr);
249 else
250 snprintf(buf, sizeof(buf), "/sys/bus/iio/devices/%s/%s",
251 dev->id, attr);
Paul Cercueilc35d1ac2014-03-10 12:33:32 +0100252 f = fopen(buf, "r+");
Paul Cercueil3e898302014-02-17 16:17:11 +0100253 if (!f)
254 return -errno;
255
256 ret = fwrite(src, 1, len, f);
Paul Cercueil96dfedd2014-03-11 09:53:09 +0100257 fflush(f);
258 if (ferror(f))
259 ret = -EBUSY;
Paul Cercueil3e898302014-02-17 16:17:11 +0100260 fclose(f);
Paul Cercueil6e7f79e2014-04-04 12:27:24 +0200261 return ret ? ret : -EIO;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100262}
263
Paul Cercueil167d3112014-02-18 12:23:53 +0100264static const char * get_filename(const struct iio_channel *chn,
265 const char *attr)
266{
Paul Cercueila604e9a2014-02-21 09:50:25 +0100267 struct iio_channel_pdata *pdata = chn->pdata;
Paul Cercueil167d3112014-02-18 12:23:53 +0100268 struct fn_map *maps = pdata->maps;
269 unsigned int i;
270 for (i = 0; i < pdata->nb_maps; i++)
Paul Cercueila604e9a2014-02-21 09:50:25 +0100271 if (!strcmp(attr, maps[i].attr))
Paul Cercueil167d3112014-02-18 12:23:53 +0100272 return maps[i].filename;
273 return attr;
274}
275
276static ssize_t local_read_chn_attr(const struct iio_channel *chn,
277 const char *attr, char *dst, size_t len)
278{
279 attr = get_filename(chn, attr);
Paul Cercueil50c762a2014-04-14 15:55:43 +0200280 return local_read_dev_attr(chn->dev, attr, dst, len, false);
Paul Cercueil167d3112014-02-18 12:23:53 +0100281}
282
283static ssize_t local_write_chn_attr(const struct iio_channel *chn,
284 const char *attr, const char *src)
285{
286 attr = get_filename(chn, attr);
Paul Cercueil50c762a2014-04-14 15:55:43 +0200287 return local_write_dev_attr(chn->dev, attr, src, false);
Paul Cercueil167d3112014-02-18 12:23:53 +0100288}
289
Paul Cercueilff778232014-03-24 14:23:08 +0100290static int channel_write_state(const struct iio_channel *chn)
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100291{
Paul Cercueilff778232014-03-24 14:23:08 +0100292 char *en = iio_channel_is_enabled(chn) ? "1" : "0";
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100293 ssize_t ret = local_write_chn_attr(chn, "en", en);
294 if (ret < 0)
295 return (int) ret;
296 else
297 return 0;
298}
299
Paul Cercueile6ebf492014-04-02 13:57:36 +0200300static int local_open(const struct iio_device *dev,
301 size_t samples_count, uint32_t *mask, size_t nb)
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100302{
303 unsigned int i;
304 int ret;
305 char buf[1024];
306 struct iio_device_pdata *pdata = dev->pdata;
307 if (pdata->f)
308 return -EBUSY;
309
Paul Cercueilff778232014-03-24 14:23:08 +0100310 if (nb != dev->words)
Paul Cercueile1311222014-03-12 15:46:16 +0100311 return -EINVAL;
312
Paul Cercueil50c762a2014-04-14 15:55:43 +0200313 ret = local_write_dev_attr(dev, "buffer/enable", "0", false);
Paul Cercueilf0aeae32014-04-02 13:52:26 +0200314 if (ret < 0)
Paul Cercueil48e39452014-03-14 09:37:53 +0100315 return ret;
316
Paul Cercueile6ebf492014-04-02 13:57:36 +0200317 if (samples_count) {
Paul Cercueil8c29e412014-04-07 09:46:45 +0200318 snprintf(buf, sizeof(buf),
319 "%lu", (unsigned long) samples_count);
Paul Cercueil50c762a2014-04-14 15:55:43 +0200320 ret = local_write_dev_attr(dev, "buffer/length", buf, false);
Paul Cercueile6ebf492014-04-02 13:57:36 +0200321 if (ret < 0)
322 return ret;
323 }
324
Paul Cercueil8c29e412014-04-07 09:46:45 +0200325 snprintf(buf, sizeof(buf), "/dev/%s", dev->id);
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100326 pdata->f = fopen(buf, "r+");
327 if (!pdata->f)
328 return -errno;
329
Paul Cercueilff778232014-03-24 14:23:08 +0100330 memcpy(dev->mask, mask, nb);
331
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100332 /* Enable channels */
333 for (i = 0; i < dev->nb_channels; i++) {
334 struct iio_channel *chn = dev->channels[i];
Paul Cercueile1311222014-03-12 15:46:16 +0100335 if (chn->index >= 0) {
Paul Cercueilff778232014-03-24 14:23:08 +0100336 ret = channel_write_state(chn);
Paul Cercueilf0aeae32014-04-02 13:52:26 +0200337 if (ret < 0)
Paul Cercueile1311222014-03-12 15:46:16 +0100338 goto err_close;
339 }
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100340 }
341
Paul Cercueile6ebf492014-04-02 13:57:36 +0200342 pdata->samples_count = samples_count;
343
344 /* If opened with samples_count == 0, we probably want DDS mode;
345 * then the buffer will only be enabled when closing the device. */
346 if (samples_count > 0)
Paul Cercueil50c762a2014-04-14 15:55:43 +0200347 ret = local_write_dev_attr(dev, "buffer/enable", "1", false);
Paul Cercueilf0aeae32014-04-02 13:52:26 +0200348 if (ret < 0)
Paul Cercueil45c575d2014-03-20 15:14:01 +0100349 goto err_close;
350
Paul Cercueil45c575d2014-03-20 15:14:01 +0100351 return 0;
Paul Cercueile1311222014-03-12 15:46:16 +0100352err_close:
353 fclose(pdata->f);
354 pdata->f = NULL;
355 return ret;
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100356}
357
358static int local_close(const struct iio_device *dev)
359{
360 struct iio_device_pdata *pdata = dev->pdata;
Paul Cercueilfc8ec4a2014-03-17 16:42:22 +0100361 int ret;
362
363 if (!pdata->f)
364 return -EBADF;
365
366 ret = fclose(pdata->f);
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100367 if (ret)
368 return ret;
369
370 pdata->f = NULL;
Paul Cercueile6ebf492014-04-02 13:57:36 +0200371 ret = local_write_dev_attr(dev, "buffer/enable",
Paul Cercueil50c762a2014-04-14 15:55:43 +0200372 pdata->samples_count ? "0" : "1", false);
Paul Cercueilf0aeae32014-04-02 13:52:26 +0200373 if (ret < 0)
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100374 return ret;
375 else
376 return 0;
377}
378
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100379static int local_get_trigger(const struct iio_device *dev,
380 const struct iio_device **trigger)
381{
382 char buf[1024];
383 unsigned int i;
384 ssize_t nb = local_read_dev_attr(dev, "trigger/current_trigger",
Paul Cercueil50c762a2014-04-14 15:55:43 +0200385 buf, sizeof(buf), false);
Paul Cercueild09322a2014-04-02 13:50:18 +0200386 if (nb < 0) {
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100387 *trigger = NULL;
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100388 return (int) nb;
Paul Cercueild09322a2014-04-02 13:50:18 +0200389 }
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100390
391 nb = dev->ctx->nb_devices;
Paul Cercueil4e2f6652014-04-04 12:41:45 +0200392 for (i = 0; i < (size_t) nb; i++) {
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100393 const struct iio_device *cur = dev->ctx->devices[i];
394 if (cur->name && !strcmp(cur->name, buf)) {
395 *trigger = cur;
396 return 0;
397 }
398 }
399 return -ENXIO;
400}
401
402static int local_set_trigger(const struct iio_device *dev,
403 const struct iio_device *trigger)
404{
405 ssize_t nb;
406 const char *value = trigger ? trigger->name : "";
Paul Cercueil50c762a2014-04-14 15:55:43 +0200407 nb = local_write_dev_attr(dev, "trigger/current_trigger", value, false);
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100408 if (nb < 0)
409 return (int) nb;
410 else
411 return 0;
412}
413
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100414static bool is_channel(const char *attr)
415{
416 unsigned int i;
417 char *ptr = NULL;
Paul Cercueilec6857d2014-03-11 17:23:49 +0100418 if (!strncmp(attr, "in_timestamp_", sizeof("in_timestamp_") - 1))
419 return true;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100420 if (!strncmp(attr, "in_", 3))
421 ptr = strchr(attr + 3, '_');
422 else if (!strncmp(attr, "out_", 4))
423 ptr = strchr(attr + 4, '_');
424 if (!ptr)
425 return false;
426 if (*(ptr - 1) >= '0' && *(ptr - 1) <= '9')
427 return true;
428 for (i = 0; i < ARRAY_SIZE(modifier_names); i++)
429 if (modifier_names[i] && !strncmp(ptr + 1, modifier_names[i],
430 strlen(modifier_names[i])))
431 return true;
432 return false;
433}
434
435static char * get_channel_id(const char *attr)
436{
437 char *res, *ptr;
438 unsigned int i;
439
440 attr = strchr(attr, '_') + 1;
441 ptr = strchr(attr, '_');
442 for (i = 0; i < ARRAY_SIZE(modifier_names); i++) {
443 if (modifier_names[i] && !strncmp(ptr + 1, modifier_names[i],
444 strlen(modifier_names[i]))) {
445 ptr = strchr(ptr + 1, '_');
446 break;
447 }
448 }
449
450 res = malloc(ptr - attr + 1);
451 if (!res)
452 return NULL;
453
454 memcpy(res, attr, ptr - attr);
455 res[ptr - attr] = 0;
456 return res;
457}
458
459static char * get_short_attr_name(const char *attr)
460{
461 char *ptr = strchr(attr, '_') + 1;
462 ptr = strchr(ptr, '_') + 1;
463 return strdup(ptr);
464}
465
466static int read_device_name(struct iio_device *dev)
467{
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100468 char buf[1024];
Paul Cercueil8c29e412014-04-07 09:46:45 +0200469 ssize_t ret = iio_device_attr_read(dev, "name", buf, sizeof(buf));
Paul Cercueil30430c72014-02-17 16:52:37 +0100470 if (ret < 0)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100471 return ret;
Paul Cercueil30430c72014-02-17 16:52:37 +0100472 else if (ret == 0)
473 return -EIO;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100474
Paul Cercueil30430c72014-02-17 16:52:37 +0100475 dev->name = strdup(buf);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100476 if (!dev->name)
477 return -ENOMEM;
Paul Cercueil30430c72014-02-17 16:52:37 +0100478 else
479 return 0;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100480}
481
482static int add_attr_to_device(struct iio_device *dev, const char *attr)
483{
Paul Cercueilbb618272014-02-20 11:35:52 +0100484 char **attrs, *name;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100485 unsigned int i;
486
487 for (i = 0; i < ARRAY_SIZE(device_attrs_blacklist); i++)
488 if (!strcmp(device_attrs_blacklist[i], attr))
489 return 0;
490
491 if (!strcmp(attr, "name"))
492 return read_device_name(dev);
493
494 name = strdup(attr);
495 if (!name)
496 return -ENOMEM;
497
498 attrs = realloc(dev->attrs, (1 + dev->nb_attrs) * sizeof(char *));
499 if (!attrs) {
500 free(name);
501 return -ENOMEM;
502 }
503
504 attrs[dev->nb_attrs++] = name;
505 dev->attrs = attrs;
506 DEBUG("Added attr \'%s\' to device \'%s\'\n", attr, dev->id);
507 return 0;
508}
509
Paul Cercueile3fbd952014-03-11 16:58:33 +0100510static int add_attr_to_channel(struct iio_channel *chn,
511 const char *attr, const char *path)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100512{
Paul Cercueila604e9a2014-02-21 09:50:25 +0100513 struct iio_channel_pdata *pdata = chn->pdata;
Paul Cercueil167d3112014-02-18 12:23:53 +0100514 struct fn_map *maps;
Paul Cercueilbb618272014-02-20 11:35:52 +0100515 char **attrs, *fn, *name = get_short_attr_name(attr);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100516 if (!name)
517 return -ENOMEM;
518
Paul Cercueile3fbd952014-03-11 16:58:33 +0100519 fn = strdup(path);
Paul Cercueil167d3112014-02-18 12:23:53 +0100520 if (!fn)
521 goto err_free_name;
522
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100523 attrs = realloc(chn->attrs, (1 + chn->nb_attrs) * sizeof(char *));
Paul Cercueil167d3112014-02-18 12:23:53 +0100524 if (!attrs)
525 goto err_free_fn;
526
527 maps = realloc(pdata->maps,
528 (1 + pdata->nb_maps) * sizeof(struct fn_map));
529 if (!maps)
530 goto err_update_maps;
531
Paul Cercueil167d3112014-02-18 12:23:53 +0100532 maps[pdata->nb_maps].attr = name;
533 maps[pdata->nb_maps++].filename = fn;
534 pdata->maps = maps;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100535
536 attrs[chn->nb_attrs++] = name;
537 chn->attrs = attrs;
538 DEBUG("Added attr \'%s\' to channel \'%s\'\n", name, chn->id);
539 return 0;
Paul Cercueil167d3112014-02-18 12:23:53 +0100540
541err_update_maps:
542 /* the first realloc succeeded so we must update chn->attrs
543 * even if an error occured later */
544 chn->attrs = attrs;
545err_free_fn:
546 free(fn);
547err_free_name:
548 free(name);
549 return -ENOMEM;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100550}
551
552static int add_channel_to_device(struct iio_device *dev,
553 struct iio_channel *chn)
554{
555 struct iio_channel **channels = realloc(dev->channels,
556 (dev->nb_channels + 1) * sizeof(struct iio_channel *));
557 if (!channels)
558 return -ENOMEM;
559
560 channels[dev->nb_channels++] = chn;
561 dev->channels = channels;
562 DEBUG("Added channel \'%s\' to device \'%s\'\n", chn->id, dev->id);
563 return 0;
564}
565
566static int add_device_to_context(struct iio_context *ctx,
567 struct iio_device *dev)
568{
569 struct iio_device **devices = realloc(ctx->devices,
570 (ctx->nb_devices + 1) * sizeof(struct iio_device *));
571 if (!devices)
572 return -ENOMEM;
573
574 devices[ctx->nb_devices++] = dev;
575 ctx->devices = devices;
576 DEBUG("Added device \'%s\' to context \'%s\'\n", dev->id, ctx->name);
577 return 0;
578}
579
580static bool is_global_attr(struct iio_channel *chn, const char *attr)
581{
582 unsigned int i, len;
583 char *ptr;
584
Paul Cercueil35a01312014-02-20 10:56:57 +0100585 if (!chn->is_output && !strncmp(attr, "in_", 3))
586 attr += 3;
587 else if (chn->is_output && !strncmp(attr, "out_", 4))
588 attr += 4;
589 else
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100590 return false;
591
592 ptr = strchr(attr, '_');
593 if (!ptr)
594 return false;
595
596 len = ptr - attr;
597
598 if (strncmp(chn->id, attr, len))
599 return false;
600
601 DEBUG("Found match: %s and %s\n", chn->id, attr);
602 if (chn->id[len] >= '0' && chn->id[len] <= '9')
603 return true;
604 else if (chn->id[len] != '_')
605 return false;
606
607 for (i = 0; i < ARRAY_SIZE(modifier_names); i++)
608 if (modifier_names[i] &&
609 !strncmp(chn->id + len + 1, modifier_names[i],
610 strlen(modifier_names[i])))
611 return true;
612
613 return false;
614}
615
616static int detect_and_move_global_attrs(struct iio_device *dev)
617{
618 unsigned int i;
Paul Cercueilbb618272014-02-20 11:35:52 +0100619 char **ptr = dev->attrs;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100620
621 for (i = 0; i < dev->nb_attrs; i++) {
622 unsigned int j;
623 bool global = false;
624 const char *attr = dev->attrs[i];
625
626 for (j = 0; j < dev->nb_channels; j++) {
627 struct iio_channel *chn = dev->channels[j];
628 if (is_global_attr(chn, attr)) {
629 int ret;
630 global = true;
Paul Cercueile3fbd952014-03-11 16:58:33 +0100631 ret = add_attr_to_channel(chn, attr, attr);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100632 if (ret)
633 return ret;
634 }
635 }
636
637 if (global) {
Paul Cercueilbb618272014-02-20 11:35:52 +0100638 free(dev->attrs[i]);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100639 dev->attrs[i] = NULL;
640 }
641 }
642
643 for (i = 0; i < dev->nb_attrs; i++) {
644 if (dev->attrs[i])
645 *ptr++ = dev->attrs[i];
646 }
647
648 dev->nb_attrs = ptr - dev->attrs;
649 return 0;
650}
651
652static struct iio_channel *create_channel(struct iio_device *dev,
Paul Cercueile3fbd952014-03-11 16:58:33 +0100653 char *id, const char *attr, const char *path)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100654{
655 struct iio_channel *chn = calloc(1, sizeof(*chn));
656 if (!chn)
657 return NULL;
658
Paul Cercueileaab6582014-02-21 09:35:59 +0100659 chn->pdata = calloc(1, sizeof(*chn->pdata));
660 if (!dev->pdata)
661 goto err_free_chn;
662
663 if (!strncmp(attr, "out_", 4))
Paul Cercueil35a01312014-02-20 10:56:57 +0100664 chn->is_output = true;
Paul Cercueileaab6582014-02-21 09:35:59 +0100665 else if (strncmp(attr, "in_", 3))
666 goto err_free_pdata;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100667
668 chn->dev = dev;
669 chn->id = id;
670 chn->modifier = IIO_NO_MOD;
671
Paul Cercueile3fbd952014-03-11 16:58:33 +0100672 if (!add_attr_to_channel(chn, attr, path))
Paul Cercueileaab6582014-02-21 09:35:59 +0100673 return chn;
674
675err_free_pdata:
676 free(chn->pdata);
677err_free_chn:
678 free(chn);
679 return NULL;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100680}
681
Paul Cercueile3fbd952014-03-11 16:58:33 +0100682static int add_attr_or_channel_helper(struct iio_device *dev,
683 const char *path, const char *prefix)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100684{
Paul Cercueil1be57832014-03-11 16:27:16 +0100685 int ret;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100686 unsigned int i;
Paul Cercueil1be57832014-03-11 16:27:16 +0100687 struct iio_channel *chn;
Paul Cercueile3fbd952014-03-11 16:58:33 +0100688 char buf[1024], *channel_id;
Paul Cercueil1be57832014-03-11 16:27:16 +0100689 const char *name = strrchr(path, '/') + 1;
Paul Cercueile3fbd952014-03-11 16:58:33 +0100690 if (!prefix) {
691 path = name;
692 } else {
693 snprintf(buf, sizeof(buf), "%s/%s", prefix, name);
694 path = buf;
695 }
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100696
Paul Cercueil1be57832014-03-11 16:27:16 +0100697 if (!is_channel(name))
698 return add_attr_to_device(dev, name);
699
700 channel_id = get_channel_id(name);
701 if (!channel_id)
702 return -ENOMEM;
703
704 for (i = 0; i < dev->nb_channels; i++) {
705 chn = dev->channels[i];
Paul Cercueil5248e792014-03-31 14:39:10 +0200706 if (!strcmp(chn->id, channel_id)
707 && chn->is_output == (name[0] == 'o')) {
Paul Cercueil1be57832014-03-11 16:27:16 +0100708 free(channel_id);
Paul Cercueile3fbd952014-03-11 16:58:33 +0100709 return add_attr_to_channel(chn, name, path);
Paul Cercueil1be57832014-03-11 16:27:16 +0100710 }
711 }
712
Paul Cercueile3fbd952014-03-11 16:58:33 +0100713 chn = create_channel(dev, channel_id, name, path);
Paul Cercueil1be57832014-03-11 16:27:16 +0100714 if (!chn) {
715 free(channel_id);
716 return -ENXIO;
717 }
718 ret = add_channel_to_device(dev, chn);
719 if (ret)
720 free_channel(chn);
721 return ret;
722}
723
Paul Cercueile3fbd952014-03-11 16:58:33 +0100724static int add_attr_or_channel(void *d, const char *path)
725{
726 return add_attr_or_channel_helper((struct iio_device *) d, path, NULL);
727}
728
729static int add_scan_element(void *d, const char *path)
730{
731 return add_attr_or_channel_helper((struct iio_device *) d,
732 path, "scan_elements");
733}
734
Paul Cercueil1be57832014-03-11 16:27:16 +0100735static int foreach_in_dir(void *d, const char *path, bool is_dir,
736 int (*callback)(void *, const char *))
737{
738 long name_max;
739 struct dirent *entry, *result;
740 DIR *dir = opendir(path);
741 if (!dir)
742 return -errno;
743
744 name_max = pathconf(path, _PC_NAME_MAX);
745 if (name_max == -1)
746 name_max = 255;
747 entry = malloc(offsetof(struct dirent, d_name) + name_max + 1);
748 if (!entry) {
749 closedir(dir);
750 return -ENOMEM;
751 }
752
753 while (true) {
754 struct stat st;
755 char buf[1024];
756 int ret = readdir_r(dir, entry, &result);
757 if (ret) {
758 strerror_r(ret, buf, sizeof(buf));
759 ERROR("Unable to open directory %s: %s\n", path, buf);
760 free(entry);
761 closedir(dir);
762 return ret;
763 }
764 if (!result)
765 break;
766
767 snprintf(buf, sizeof(buf), "%s/%s", path, entry->d_name);
768 if (stat(buf, &st) < 0) {
769 ret = -errno;
Paul Cercueilc21300c2014-03-13 09:54:14 +0100770 strerror_r(errno, buf, sizeof(buf));
Paul Cercueil1be57832014-03-11 16:27:16 +0100771 ERROR("Unable to stat file: %s\n", buf);
772 free(entry);
773 closedir(dir);
774 return ret;
775 }
776
777 if (is_dir && S_ISDIR(st.st_mode) && entry->d_name[0] != '.')
778 ret = callback(d, buf);
779 else if (!is_dir && S_ISREG(st.st_mode))
780 ret = callback(d, buf);
781 else
782 continue;
783
784 if (ret < 0) {
785 free(entry);
786 closedir(dir);
787 return ret;
788 }
789 }
790
791 free(entry);
792 closedir(dir);
793 return 0;
794}
795
Paul Cercueile3fbd952014-03-11 16:58:33 +0100796static int add_scan_elements(struct iio_device *dev, const char *devpath)
797{
798 struct stat st;
799 char buf[1024];
800 snprintf(buf, sizeof(buf), "%s/scan_elements", devpath);
801
802 if (!stat(buf, &st) && S_ISDIR(st.st_mode)) {
803 int ret = foreach_in_dir(dev, buf, false, add_scan_element);
804 if (ret < 0)
805 return ret;
806 }
807
808 return 0;
809}
810
Paul Cercueil1be57832014-03-11 16:27:16 +0100811static int create_device(void *d, const char *path)
812{
Paul Cercueilff778232014-03-24 14:23:08 +0100813 uint32_t *mask = NULL;
Paul Cercueil1be57832014-03-11 16:27:16 +0100814 unsigned int i;
815 int ret;
816 struct iio_context *ctx = d;
817 struct iio_device *dev = calloc(1, sizeof(*dev));
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100818 if (!dev)
Paul Cercueil1be57832014-03-11 16:27:16 +0100819 return -ENOMEM;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100820
Paul Cercueileaab6582014-02-21 09:35:59 +0100821 dev->pdata = calloc(1, sizeof(*dev->pdata));
822 if (!dev->pdata) {
823 free(dev);
Paul Cercueil1be57832014-03-11 16:27:16 +0100824 return -ENOMEM;
Paul Cercueileaab6582014-02-21 09:35:59 +0100825 }
826
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100827 dev->ctx = ctx;
Paul Cercueil1be57832014-03-11 16:27:16 +0100828 dev->id = strdup(strrchr(path, '/') + 1);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100829 if (!dev->id) {
Paul Cercueileaab6582014-02-21 09:35:59 +0100830 free(dev->pdata);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100831 free(dev);
Paul Cercueil1be57832014-03-11 16:27:16 +0100832 return -ENOMEM;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100833 }
834
Paul Cercueil1be57832014-03-11 16:27:16 +0100835 ret = foreach_in_dir(dev, path, false, add_attr_or_channel);
836 if (ret < 0) {
837 free_device(dev);
838 return ret;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100839 }
840
Paul Cercueil54913ac2014-03-31 14:48:19 +0200841 for (i = 0; i < dev->nb_channels; i++)
842 set_channel_name(dev->channels[i]);
843
844 ret = detect_and_move_global_attrs(dev);
Paul Cercueile3fbd952014-03-11 16:58:33 +0100845 if (ret < 0) {
846 free_device(dev);
847 return ret;
848 }
849
Paul Cercueil54913ac2014-03-31 14:48:19 +0200850 ret = add_scan_elements(dev, path);
Paul Cercueil1be57832014-03-11 16:27:16 +0100851 if (ret < 0) {
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100852 free_device(dev);
Paul Cercueil1be57832014-03-11 16:27:16 +0100853 return ret;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100854 }
855
Paul Cercueilff778232014-03-24 14:23:08 +0100856 dev->words = (dev->nb_channels + 31) / 32;
857 if (dev->words) {
858 mask = calloc(dev->words, sizeof(*mask));
Paul Cercueil45c575d2014-03-20 15:14:01 +0100859 if (!mask) {
860 free_device(dev);
861 return ret;
862 }
Paul Cercueil45c575d2014-03-20 15:14:01 +0100863 }
864
Paul Cercueilff778232014-03-24 14:23:08 +0100865 dev->mask = mask;
Paul Cercueil45c575d2014-03-20 15:14:01 +0100866
Paul Cercueil1be57832014-03-11 16:27:16 +0100867 ret = add_device_to_context(ctx, dev);
868 if (ret < 0)
869 free_device(dev);
870 return ret;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100871}
872
873static struct iio_backend_ops local_ops = {
Paul Cercueil44010ea2014-02-21 11:47:04 +0100874 .open = local_open,
875 .close = local_close,
876 .read = local_read,
877 .write = local_write,
Paul Cercueil167d3112014-02-18 12:23:53 +0100878 .read_device_attr = local_read_dev_attr,
879 .write_device_attr = local_write_dev_attr,
880 .read_channel_attr = local_read_chn_attr,
881 .write_channel_attr = local_write_chn_attr,
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100882 .get_trigger = local_get_trigger,
883 .set_trigger = local_set_trigger,
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100884 .shutdown = local_shutdown,
885};
886
887struct iio_context * iio_create_local_context(void)
888{
Paul Cercueil1be57832014-03-11 16:27:16 +0100889 int ret;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100890 struct iio_context *ctx = calloc(1, sizeof(*ctx));
891 if (!ctx) {
892 ERROR("Unable to allocate memory\n");
893 return NULL;
894 }
895
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100896 ctx->ops = &local_ops;
897 ctx->name = "local";
898
Paul Cercueil1be57832014-03-11 16:27:16 +0100899 ret = foreach_in_dir(ctx, "/sys/bus/iio/devices", true, create_device);
900 if (ret < 0) {
901 char buf[1024];
Paul Cercueil4c6729d2014-04-04 17:24:41 +0200902 strerror_r(-ret, buf, sizeof(buf));
Paul Cercueil1be57832014-03-11 16:27:16 +0100903 ERROR("Unable to create context: %s\n", buf);
904 iio_context_destroy(ctx);
Paul Cercueil4c6729d2014-04-04 17:24:41 +0200905 return NULL;
906 }
907
908 ret = iio_context_init(ctx);
909 if (ret < 0) {
910 char buf[1024];
911 strerror_r(-ret, buf, sizeof(buf));
912 ERROR("Unable to initialize context: %s\n", buf);
913 iio_context_destroy(ctx);
Paul Cercueil1be57832014-03-11 16:27:16 +0100914 ctx = NULL;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100915 }
Paul Cercueilae88fde2014-03-12 11:47:10 +0100916
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100917 return ctx;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100918}