blob: f1a2a7e230fb6b8781c37187741cdf93f9f42295 [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) {
125 name = malloc(strlen(chn->name) + len + 2);
126 if (!name)
127 return -ENOMEM;
128 sprintf(name, "%s_%.*s", chn->name, len, attr0);
129 DEBUG("Fixing name of channel %s from %s to %s\n",
130 chn->id, chn->name, name);
Paul Cercueilbb618272014-02-20 11:35:52 +0100131 free(chn->name);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100132 } else {
133 name = malloc(len + 2);
134 if (!name)
135 return -ENOMEM;
136 sprintf(name, "%.*s", len, attr0);
137 DEBUG("Setting name of channel %s to %s\n",
138 chn->id, name);
139 }
140 chn->name = name;
141
142 /* Shrink the attribute name */
143 for (i = 0; i < chn->nb_attrs; i++)
Paul Cercueilbb618272014-02-20 11:35:52 +0100144 strcut(chn->attrs[i], len + 1);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100145 }
146
147 if (chn->name) {
148 unsigned int i;
149 for (i = 0; i < ARRAY_SIZE(modifier_names); i++) {
150 unsigned int len;
151
152 if (!modifier_names[i])
153 continue;
154
155 len = strlen(modifier_names[i]);
156 if (!strncmp(chn->name, modifier_names[i], len)) {
157 if (chn->name[len]) {
158 /* Shrink the modifier from the extended name */
Paul Cercueilbb618272014-02-20 11:35:52 +0100159 strcut(chn->name, len + 1);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100160 } else {
Paul Cercueilbb618272014-02-20 11:35:52 +0100161 free(chn->name);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100162 chn->name = NULL;
163 }
164 chn->modifier = i;
165 DEBUG("Detected modifier for channel %s: %s\n",
166 chn->id, modifier_names[i]);
167 break;
168 }
169 }
170 }
171 return 0;
172}
173
Paul Cercueil45c575d2014-03-20 15:14:01 +0100174static ssize_t local_read(const struct iio_device *dev,
175 void *dst, size_t len, uint32_t *mask, size_t words)
Paul Cercueil44010ea2014-02-21 11:47:04 +0100176{
177 ssize_t ret;
Paul Cercueilff778232014-03-24 14:23:08 +0100178 FILE *f = dev->pdata->f;
Paul Cercueil44010ea2014-02-21 11:47:04 +0100179 if (!f)
180 return -EBADF;
Paul Cercueilff778232014-03-24 14:23:08 +0100181 if (words != dev->words)
Paul Cercueil45c575d2014-03-20 15:14:01 +0100182 return -EINVAL;
183
Paul Cercueilff778232014-03-24 14:23:08 +0100184 memcpy(mask, dev->mask, words);
Paul Cercueil44010ea2014-02-21 11:47:04 +0100185 ret = fread(dst, 1, len, f);
186 if (ret)
187 return ret;
188 else if (feof(f))
189 return 0;
190 else
191 return -EIO;
192}
193
194static ssize_t local_write(const struct iio_device *dev,
195 const void *src, size_t len)
196{
197 ssize_t ret;
198 FILE *f = dev->pdata->f;
199 if (!f)
200 return -EBADF;
201 ret = fwrite(src, 1, len, f);
202 if (ret)
203 return ret;
204 else if (feof(f))
205 return 0;
206 else
207 return -EIO;
208}
209
Paul Cercueil167d3112014-02-18 12:23:53 +0100210static ssize_t local_read_dev_attr(const struct iio_device *dev,
211 const char *attr, char *dst, size_t len)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100212{
Paul Cercueil3e898302014-02-17 16:17:11 +0100213 FILE *f;
214 char buf[1024];
215 ssize_t ret;
216
Paul Cercueil1be57832014-03-11 16:27:16 +0100217 snprintf(buf, sizeof(buf), "/sys/bus/iio/devices/%s/%s", dev->id, attr);
Paul Cercueil3e898302014-02-17 16:17:11 +0100218 f = fopen(buf, "r");
219 if (!f)
220 return -errno;
221
222 ret = fread(dst, 1, len, f);
223 if (ret > 0)
224 dst[ret - 1] = '\0';
Paul Cercueil96dfedd2014-03-11 09:53:09 +0100225 fflush(f);
226 if (ferror(f))
227 ret = -EBUSY;
Paul Cercueil3e898302014-02-17 16:17:11 +0100228 fclose(f);
Paul Cercueil6e7f79e2014-04-04 12:27:24 +0200229 return ret ? ret : -EIO;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100230}
231
Paul Cercueil167d3112014-02-18 12:23:53 +0100232static ssize_t local_write_dev_attr(const struct iio_device *dev,
233 const char *attr, const char *src)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100234{
Paul Cercueil3e898302014-02-17 16:17:11 +0100235 FILE *f;
236 char buf[1024];
237 ssize_t ret;
238 size_t len = strlen(src) + 1;
239
Paul Cercueil1be57832014-03-11 16:27:16 +0100240 snprintf(buf, sizeof(buf), "/sys/bus/iio/devices/%s/%s", dev->id, attr);
Paul Cercueilc35d1ac2014-03-10 12:33:32 +0100241 f = fopen(buf, "r+");
Paul Cercueil3e898302014-02-17 16:17:11 +0100242 if (!f)
243 return -errno;
244
245 ret = fwrite(src, 1, len, f);
Paul Cercueil96dfedd2014-03-11 09:53:09 +0100246 fflush(f);
247 if (ferror(f))
248 ret = -EBUSY;
Paul Cercueil3e898302014-02-17 16:17:11 +0100249 fclose(f);
Paul Cercueil6e7f79e2014-04-04 12:27:24 +0200250 return ret ? ret : -EIO;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100251}
252
Paul Cercueil167d3112014-02-18 12:23:53 +0100253static const char * get_filename(const struct iio_channel *chn,
254 const char *attr)
255{
Paul Cercueila604e9a2014-02-21 09:50:25 +0100256 struct iio_channel_pdata *pdata = chn->pdata;
Paul Cercueil167d3112014-02-18 12:23:53 +0100257 struct fn_map *maps = pdata->maps;
258 unsigned int i;
259 for (i = 0; i < pdata->nb_maps; i++)
Paul Cercueila604e9a2014-02-21 09:50:25 +0100260 if (!strcmp(attr, maps[i].attr))
Paul Cercueil167d3112014-02-18 12:23:53 +0100261 return maps[i].filename;
262 return attr;
263}
264
265static ssize_t local_read_chn_attr(const struct iio_channel *chn,
266 const char *attr, char *dst, size_t len)
267{
268 attr = get_filename(chn, attr);
269 return local_read_dev_attr(chn->dev, attr, dst, len);
270}
271
272static ssize_t local_write_chn_attr(const struct iio_channel *chn,
273 const char *attr, const char *src)
274{
275 attr = get_filename(chn, attr);
276 return local_write_dev_attr(chn->dev, attr, src);
277}
278
Paul Cercueilff778232014-03-24 14:23:08 +0100279static int channel_write_state(const struct iio_channel *chn)
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100280{
Paul Cercueilff778232014-03-24 14:23:08 +0100281 char *en = iio_channel_is_enabled(chn) ? "1" : "0";
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100282 ssize_t ret = local_write_chn_attr(chn, "en", en);
283 if (ret < 0)
284 return (int) ret;
285 else
286 return 0;
287}
288
Paul Cercueile6ebf492014-04-02 13:57:36 +0200289static int local_open(const struct iio_device *dev,
290 size_t samples_count, uint32_t *mask, size_t nb)
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100291{
292 unsigned int i;
293 int ret;
294 char buf[1024];
295 struct iio_device_pdata *pdata = dev->pdata;
296 if (pdata->f)
297 return -EBUSY;
298
Paul Cercueilff778232014-03-24 14:23:08 +0100299 if (nb != dev->words)
Paul Cercueile1311222014-03-12 15:46:16 +0100300 return -EINVAL;
301
Paul Cercueil48e39452014-03-14 09:37:53 +0100302 ret = local_write_dev_attr(dev, "buffer/enable", "0");
Paul Cercueilf0aeae32014-04-02 13:52:26 +0200303 if (ret < 0)
Paul Cercueil48e39452014-03-14 09:37:53 +0100304 return ret;
305
Paul Cercueile6ebf492014-04-02 13:57:36 +0200306 if (samples_count) {
307 sprintf(buf, "%lu", (unsigned long) samples_count);
308 ret = local_write_dev_attr(dev, "buffer/length", buf);
309 if (ret < 0)
310 return ret;
311 }
312
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100313 sprintf(buf, "/dev/%s", dev->id);
314 pdata->f = fopen(buf, "r+");
315 if (!pdata->f)
316 return -errno;
317
Paul Cercueilff778232014-03-24 14:23:08 +0100318 memcpy(dev->mask, mask, nb);
319
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100320 /* Enable channels */
321 for (i = 0; i < dev->nb_channels; i++) {
322 struct iio_channel *chn = dev->channels[i];
Paul Cercueile1311222014-03-12 15:46:16 +0100323 if (chn->index >= 0) {
Paul Cercueilff778232014-03-24 14:23:08 +0100324 ret = channel_write_state(chn);
Paul Cercueilf0aeae32014-04-02 13:52:26 +0200325 if (ret < 0)
Paul Cercueile1311222014-03-12 15:46:16 +0100326 goto err_close;
327 }
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100328 }
329
Paul Cercueile6ebf492014-04-02 13:57:36 +0200330 pdata->samples_count = samples_count;
331
332 /* If opened with samples_count == 0, we probably want DDS mode;
333 * then the buffer will only be enabled when closing the device. */
334 if (samples_count > 0)
335 ret = local_write_dev_attr(dev, "buffer/enable", "1");
Paul Cercueilf0aeae32014-04-02 13:52:26 +0200336 if (ret < 0)
Paul Cercueil45c575d2014-03-20 15:14:01 +0100337 goto err_close;
338
Paul Cercueil45c575d2014-03-20 15:14:01 +0100339 return 0;
Paul Cercueile1311222014-03-12 15:46:16 +0100340err_close:
341 fclose(pdata->f);
342 pdata->f = NULL;
343 return ret;
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100344}
345
346static int local_close(const struct iio_device *dev)
347{
348 struct iio_device_pdata *pdata = dev->pdata;
Paul Cercueilfc8ec4a2014-03-17 16:42:22 +0100349 int ret;
350
351 if (!pdata->f)
352 return -EBADF;
353
354 ret = fclose(pdata->f);
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100355 if (ret)
356 return ret;
357
358 pdata->f = NULL;
Paul Cercueile6ebf492014-04-02 13:57:36 +0200359 ret = local_write_dev_attr(dev, "buffer/enable",
360 pdata->samples_count ? "0" : "1");
Paul Cercueilf0aeae32014-04-02 13:52:26 +0200361 if (ret < 0)
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100362 return ret;
363 else
364 return 0;
365}
366
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100367static int local_get_trigger(const struct iio_device *dev,
368 const struct iio_device **trigger)
369{
370 char buf[1024];
371 unsigned int i;
372 ssize_t nb = local_read_dev_attr(dev, "trigger/current_trigger",
373 buf, sizeof(buf));
Paul Cercueild09322a2014-04-02 13:50:18 +0200374 if (nb < 0) {
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100375 *trigger = NULL;
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100376 return (int) nb;
Paul Cercueild09322a2014-04-02 13:50:18 +0200377 }
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100378
379 nb = dev->ctx->nb_devices;
380 for (i = 0; i < nb; i++) {
381 const struct iio_device *cur = dev->ctx->devices[i];
382 if (cur->name && !strcmp(cur->name, buf)) {
383 *trigger = cur;
384 return 0;
385 }
386 }
387 return -ENXIO;
388}
389
390static int local_set_trigger(const struct iio_device *dev,
391 const struct iio_device *trigger)
392{
393 ssize_t nb;
394 const char *value = trigger ? trigger->name : "";
395 nb = local_write_dev_attr(dev, "trigger/current_trigger", value);
396 if (nb < 0)
397 return (int) nb;
398 else
399 return 0;
400}
401
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100402static bool is_channel(const char *attr)
403{
404 unsigned int i;
405 char *ptr = NULL;
Paul Cercueilec6857d2014-03-11 17:23:49 +0100406 if (!strncmp(attr, "in_timestamp_", sizeof("in_timestamp_") - 1))
407 return true;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100408 if (!strncmp(attr, "in_", 3))
409 ptr = strchr(attr + 3, '_');
410 else if (!strncmp(attr, "out_", 4))
411 ptr = strchr(attr + 4, '_');
412 if (!ptr)
413 return false;
414 if (*(ptr - 1) >= '0' && *(ptr - 1) <= '9')
415 return true;
416 for (i = 0; i < ARRAY_SIZE(modifier_names); i++)
417 if (modifier_names[i] && !strncmp(ptr + 1, modifier_names[i],
418 strlen(modifier_names[i])))
419 return true;
420 return false;
421}
422
423static char * get_channel_id(const char *attr)
424{
425 char *res, *ptr;
426 unsigned int i;
427
428 attr = strchr(attr, '_') + 1;
429 ptr = strchr(attr, '_');
430 for (i = 0; i < ARRAY_SIZE(modifier_names); i++) {
431 if (modifier_names[i] && !strncmp(ptr + 1, modifier_names[i],
432 strlen(modifier_names[i]))) {
433 ptr = strchr(ptr + 1, '_');
434 break;
435 }
436 }
437
438 res = malloc(ptr - attr + 1);
439 if (!res)
440 return NULL;
441
442 memcpy(res, attr, ptr - attr);
443 res[ptr - attr] = 0;
444 return res;
445}
446
447static char * get_short_attr_name(const char *attr)
448{
449 char *ptr = strchr(attr, '_') + 1;
450 ptr = strchr(ptr, '_') + 1;
451 return strdup(ptr);
452}
453
454static int read_device_name(struct iio_device *dev)
455{
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100456 char buf[1024];
Paul Cercueil30430c72014-02-17 16:52:37 +0100457 ssize_t ret = iio_device_attr_read(dev, "name", buf, 1024);
458 if (ret < 0)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100459 return ret;
Paul Cercueil30430c72014-02-17 16:52:37 +0100460 else if (ret == 0)
461 return -EIO;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100462
Paul Cercueil30430c72014-02-17 16:52:37 +0100463 dev->name = strdup(buf);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100464 if (!dev->name)
465 return -ENOMEM;
Paul Cercueil30430c72014-02-17 16:52:37 +0100466 else
467 return 0;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100468}
469
470static int add_attr_to_device(struct iio_device *dev, const char *attr)
471{
Paul Cercueilbb618272014-02-20 11:35:52 +0100472 char **attrs, *name;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100473 unsigned int i;
474
475 for (i = 0; i < ARRAY_SIZE(device_attrs_blacklist); i++)
476 if (!strcmp(device_attrs_blacklist[i], attr))
477 return 0;
478
479 if (!strcmp(attr, "name"))
480 return read_device_name(dev);
481
482 name = strdup(attr);
483 if (!name)
484 return -ENOMEM;
485
486 attrs = realloc(dev->attrs, (1 + dev->nb_attrs) * sizeof(char *));
487 if (!attrs) {
488 free(name);
489 return -ENOMEM;
490 }
491
492 attrs[dev->nb_attrs++] = name;
493 dev->attrs = attrs;
494 DEBUG("Added attr \'%s\' to device \'%s\'\n", attr, dev->id);
495 return 0;
496}
497
Paul Cercueile3fbd952014-03-11 16:58:33 +0100498static int add_attr_to_channel(struct iio_channel *chn,
499 const char *attr, const char *path)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100500{
Paul Cercueila604e9a2014-02-21 09:50:25 +0100501 struct iio_channel_pdata *pdata = chn->pdata;
Paul Cercueil167d3112014-02-18 12:23:53 +0100502 struct fn_map *maps;
Paul Cercueilbb618272014-02-20 11:35:52 +0100503 char **attrs, *fn, *name = get_short_attr_name(attr);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100504 if (!name)
505 return -ENOMEM;
506
Paul Cercueile3fbd952014-03-11 16:58:33 +0100507 fn = strdup(path);
Paul Cercueil167d3112014-02-18 12:23:53 +0100508 if (!fn)
509 goto err_free_name;
510
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100511 attrs = realloc(chn->attrs, (1 + chn->nb_attrs) * sizeof(char *));
Paul Cercueil167d3112014-02-18 12:23:53 +0100512 if (!attrs)
513 goto err_free_fn;
514
515 maps = realloc(pdata->maps,
516 (1 + pdata->nb_maps) * sizeof(struct fn_map));
517 if (!maps)
518 goto err_update_maps;
519
Paul Cercueil167d3112014-02-18 12:23:53 +0100520 maps[pdata->nb_maps].attr = name;
521 maps[pdata->nb_maps++].filename = fn;
522 pdata->maps = maps;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100523
524 attrs[chn->nb_attrs++] = name;
525 chn->attrs = attrs;
526 DEBUG("Added attr \'%s\' to channel \'%s\'\n", name, chn->id);
527 return 0;
Paul Cercueil167d3112014-02-18 12:23:53 +0100528
529err_update_maps:
530 /* the first realloc succeeded so we must update chn->attrs
531 * even if an error occured later */
532 chn->attrs = attrs;
533err_free_fn:
534 free(fn);
535err_free_name:
536 free(name);
537 return -ENOMEM;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100538}
539
540static int add_channel_to_device(struct iio_device *dev,
541 struct iio_channel *chn)
542{
543 struct iio_channel **channels = realloc(dev->channels,
544 (dev->nb_channels + 1) * sizeof(struct iio_channel *));
545 if (!channels)
546 return -ENOMEM;
547
548 channels[dev->nb_channels++] = chn;
549 dev->channels = channels;
550 DEBUG("Added channel \'%s\' to device \'%s\'\n", chn->id, dev->id);
551 return 0;
552}
553
554static int add_device_to_context(struct iio_context *ctx,
555 struct iio_device *dev)
556{
557 struct iio_device **devices = realloc(ctx->devices,
558 (ctx->nb_devices + 1) * sizeof(struct iio_device *));
559 if (!devices)
560 return -ENOMEM;
561
562 devices[ctx->nb_devices++] = dev;
563 ctx->devices = devices;
564 DEBUG("Added device \'%s\' to context \'%s\'\n", dev->id, ctx->name);
565 return 0;
566}
567
568static bool is_global_attr(struct iio_channel *chn, const char *attr)
569{
570 unsigned int i, len;
571 char *ptr;
572
Paul Cercueil35a01312014-02-20 10:56:57 +0100573 if (!chn->is_output && !strncmp(attr, "in_", 3))
574 attr += 3;
575 else if (chn->is_output && !strncmp(attr, "out_", 4))
576 attr += 4;
577 else
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100578 return false;
579
580 ptr = strchr(attr, '_');
581 if (!ptr)
582 return false;
583
584 len = ptr - attr;
585
586 if (strncmp(chn->id, attr, len))
587 return false;
588
589 DEBUG("Found match: %s and %s\n", chn->id, attr);
590 if (chn->id[len] >= '0' && chn->id[len] <= '9')
591 return true;
592 else if (chn->id[len] != '_')
593 return false;
594
595 for (i = 0; i < ARRAY_SIZE(modifier_names); i++)
596 if (modifier_names[i] &&
597 !strncmp(chn->id + len + 1, modifier_names[i],
598 strlen(modifier_names[i])))
599 return true;
600
601 return false;
602}
603
604static int detect_and_move_global_attrs(struct iio_device *dev)
605{
606 unsigned int i;
Paul Cercueilbb618272014-02-20 11:35:52 +0100607 char **ptr = dev->attrs;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100608
609 for (i = 0; i < dev->nb_attrs; i++) {
610 unsigned int j;
611 bool global = false;
612 const char *attr = dev->attrs[i];
613
614 for (j = 0; j < dev->nb_channels; j++) {
615 struct iio_channel *chn = dev->channels[j];
616 if (is_global_attr(chn, attr)) {
617 int ret;
618 global = true;
Paul Cercueile3fbd952014-03-11 16:58:33 +0100619 ret = add_attr_to_channel(chn, attr, attr);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100620 if (ret)
621 return ret;
622 }
623 }
624
625 if (global) {
Paul Cercueilbb618272014-02-20 11:35:52 +0100626 free(dev->attrs[i]);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100627 dev->attrs[i] = NULL;
628 }
629 }
630
631 for (i = 0; i < dev->nb_attrs; i++) {
632 if (dev->attrs[i])
633 *ptr++ = dev->attrs[i];
634 }
635
636 dev->nb_attrs = ptr - dev->attrs;
637 return 0;
638}
639
640static struct iio_channel *create_channel(struct iio_device *dev,
Paul Cercueile3fbd952014-03-11 16:58:33 +0100641 char *id, const char *attr, const char *path)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100642{
643 struct iio_channel *chn = calloc(1, sizeof(*chn));
644 if (!chn)
645 return NULL;
646
Paul Cercueileaab6582014-02-21 09:35:59 +0100647 chn->pdata = calloc(1, sizeof(*chn->pdata));
648 if (!dev->pdata)
649 goto err_free_chn;
650
651 if (!strncmp(attr, "out_", 4))
Paul Cercueil35a01312014-02-20 10:56:57 +0100652 chn->is_output = true;
Paul Cercueileaab6582014-02-21 09:35:59 +0100653 else if (strncmp(attr, "in_", 3))
654 goto err_free_pdata;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100655
656 chn->dev = dev;
657 chn->id = id;
658 chn->modifier = IIO_NO_MOD;
659
Paul Cercueile3fbd952014-03-11 16:58:33 +0100660 if (!add_attr_to_channel(chn, attr, path))
Paul Cercueileaab6582014-02-21 09:35:59 +0100661 return chn;
662
663err_free_pdata:
664 free(chn->pdata);
665err_free_chn:
666 free(chn);
667 return NULL;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100668}
669
Paul Cercueile3fbd952014-03-11 16:58:33 +0100670static int add_attr_or_channel_helper(struct iio_device *dev,
671 const char *path, const char *prefix)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100672{
Paul Cercueil1be57832014-03-11 16:27:16 +0100673 int ret;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100674 unsigned int i;
Paul Cercueil1be57832014-03-11 16:27:16 +0100675 struct iio_channel *chn;
Paul Cercueile3fbd952014-03-11 16:58:33 +0100676 char buf[1024], *channel_id;
Paul Cercueil1be57832014-03-11 16:27:16 +0100677 const char *name = strrchr(path, '/') + 1;
Paul Cercueile3fbd952014-03-11 16:58:33 +0100678 if (!prefix) {
679 path = name;
680 } else {
681 snprintf(buf, sizeof(buf), "%s/%s", prefix, name);
682 path = buf;
683 }
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100684
Paul Cercueil1be57832014-03-11 16:27:16 +0100685 if (!is_channel(name))
686 return add_attr_to_device(dev, name);
687
688 channel_id = get_channel_id(name);
689 if (!channel_id)
690 return -ENOMEM;
691
692 for (i = 0; i < dev->nb_channels; i++) {
693 chn = dev->channels[i];
Paul Cercueil5248e792014-03-31 14:39:10 +0200694 if (!strcmp(chn->id, channel_id)
695 && chn->is_output == (name[0] == 'o')) {
Paul Cercueil1be57832014-03-11 16:27:16 +0100696 free(channel_id);
Paul Cercueile3fbd952014-03-11 16:58:33 +0100697 return add_attr_to_channel(chn, name, path);
Paul Cercueil1be57832014-03-11 16:27:16 +0100698 }
699 }
700
Paul Cercueile3fbd952014-03-11 16:58:33 +0100701 chn = create_channel(dev, channel_id, name, path);
Paul Cercueil1be57832014-03-11 16:27:16 +0100702 if (!chn) {
703 free(channel_id);
704 return -ENXIO;
705 }
706 ret = add_channel_to_device(dev, chn);
707 if (ret)
708 free_channel(chn);
709 return ret;
710}
711
Paul Cercueile3fbd952014-03-11 16:58:33 +0100712static int add_attr_or_channel(void *d, const char *path)
713{
714 return add_attr_or_channel_helper((struct iio_device *) d, path, NULL);
715}
716
717static int add_scan_element(void *d, const char *path)
718{
719 return add_attr_or_channel_helper((struct iio_device *) d,
720 path, "scan_elements");
721}
722
Paul Cercueil1be57832014-03-11 16:27:16 +0100723static int foreach_in_dir(void *d, const char *path, bool is_dir,
724 int (*callback)(void *, const char *))
725{
726 long name_max;
727 struct dirent *entry, *result;
728 DIR *dir = opendir(path);
729 if (!dir)
730 return -errno;
731
732 name_max = pathconf(path, _PC_NAME_MAX);
733 if (name_max == -1)
734 name_max = 255;
735 entry = malloc(offsetof(struct dirent, d_name) + name_max + 1);
736 if (!entry) {
737 closedir(dir);
738 return -ENOMEM;
739 }
740
741 while (true) {
742 struct stat st;
743 char buf[1024];
744 int ret = readdir_r(dir, entry, &result);
745 if (ret) {
746 strerror_r(ret, buf, sizeof(buf));
747 ERROR("Unable to open directory %s: %s\n", path, buf);
748 free(entry);
749 closedir(dir);
750 return ret;
751 }
752 if (!result)
753 break;
754
755 snprintf(buf, sizeof(buf), "%s/%s", path, entry->d_name);
756 if (stat(buf, &st) < 0) {
757 ret = -errno;
Paul Cercueilc21300c2014-03-13 09:54:14 +0100758 strerror_r(errno, buf, sizeof(buf));
Paul Cercueil1be57832014-03-11 16:27:16 +0100759 ERROR("Unable to stat file: %s\n", buf);
760 free(entry);
761 closedir(dir);
762 return ret;
763 }
764
765 if (is_dir && S_ISDIR(st.st_mode) && entry->d_name[0] != '.')
766 ret = callback(d, buf);
767 else if (!is_dir && S_ISREG(st.st_mode))
768 ret = callback(d, buf);
769 else
770 continue;
771
772 if (ret < 0) {
773 free(entry);
774 closedir(dir);
775 return ret;
776 }
777 }
778
779 free(entry);
780 closedir(dir);
781 return 0;
782}
783
Paul Cercueile3fbd952014-03-11 16:58:33 +0100784static int add_scan_elements(struct iio_device *dev, const char *devpath)
785{
786 struct stat st;
787 char buf[1024];
788 snprintf(buf, sizeof(buf), "%s/scan_elements", devpath);
789
790 if (!stat(buf, &st) && S_ISDIR(st.st_mode)) {
791 int ret = foreach_in_dir(dev, buf, false, add_scan_element);
792 if (ret < 0)
793 return ret;
794 }
795
796 return 0;
797}
798
Paul Cercueil1be57832014-03-11 16:27:16 +0100799static int create_device(void *d, const char *path)
800{
Paul Cercueilff778232014-03-24 14:23:08 +0100801 uint32_t *mask = NULL;
Paul Cercueil1be57832014-03-11 16:27:16 +0100802 unsigned int i;
803 int ret;
804 struct iio_context *ctx = d;
805 struct iio_device *dev = calloc(1, sizeof(*dev));
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100806 if (!dev)
Paul Cercueil1be57832014-03-11 16:27:16 +0100807 return -ENOMEM;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100808
Paul Cercueileaab6582014-02-21 09:35:59 +0100809 dev->pdata = calloc(1, sizeof(*dev->pdata));
810 if (!dev->pdata) {
811 free(dev);
Paul Cercueil1be57832014-03-11 16:27:16 +0100812 return -ENOMEM;
Paul Cercueileaab6582014-02-21 09:35:59 +0100813 }
814
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100815 dev->ctx = ctx;
Paul Cercueil1be57832014-03-11 16:27:16 +0100816 dev->id = strdup(strrchr(path, '/') + 1);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100817 if (!dev->id) {
Paul Cercueileaab6582014-02-21 09:35:59 +0100818 free(dev->pdata);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100819 free(dev);
Paul Cercueil1be57832014-03-11 16:27:16 +0100820 return -ENOMEM;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100821 }
822
Paul Cercueil1be57832014-03-11 16:27:16 +0100823 ret = foreach_in_dir(dev, path, false, add_attr_or_channel);
824 if (ret < 0) {
825 free_device(dev);
826 return ret;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100827 }
828
Paul Cercueil54913ac2014-03-31 14:48:19 +0200829 for (i = 0; i < dev->nb_channels; i++)
830 set_channel_name(dev->channels[i]);
831
832 ret = detect_and_move_global_attrs(dev);
Paul Cercueile3fbd952014-03-11 16:58:33 +0100833 if (ret < 0) {
834 free_device(dev);
835 return ret;
836 }
837
Paul Cercueil54913ac2014-03-31 14:48:19 +0200838 ret = add_scan_elements(dev, path);
Paul Cercueil1be57832014-03-11 16:27:16 +0100839 if (ret < 0) {
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100840 free_device(dev);
Paul Cercueil1be57832014-03-11 16:27:16 +0100841 return ret;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100842 }
843
Paul Cercueilff778232014-03-24 14:23:08 +0100844 dev->words = (dev->nb_channels + 31) / 32;
845 if (dev->words) {
846 mask = calloc(dev->words, sizeof(*mask));
Paul Cercueil45c575d2014-03-20 15:14:01 +0100847 if (!mask) {
848 free_device(dev);
849 return ret;
850 }
Paul Cercueil45c575d2014-03-20 15:14:01 +0100851 }
852
Paul Cercueilff778232014-03-24 14:23:08 +0100853 dev->mask = mask;
Paul Cercueil45c575d2014-03-20 15:14:01 +0100854
Paul Cercueil1be57832014-03-11 16:27:16 +0100855 ret = add_device_to_context(ctx, dev);
856 if (ret < 0)
857 free_device(dev);
858 return ret;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100859}
860
861static struct iio_backend_ops local_ops = {
Paul Cercueil44010ea2014-02-21 11:47:04 +0100862 .open = local_open,
863 .close = local_close,
864 .read = local_read,
865 .write = local_write,
Paul Cercueil167d3112014-02-18 12:23:53 +0100866 .read_device_attr = local_read_dev_attr,
867 .write_device_attr = local_write_dev_attr,
868 .read_channel_attr = local_read_chn_attr,
869 .write_channel_attr = local_write_chn_attr,
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100870 .get_trigger = local_get_trigger,
871 .set_trigger = local_set_trigger,
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100872 .shutdown = local_shutdown,
873};
874
875struct iio_context * iio_create_local_context(void)
876{
Paul Cercueil1be57832014-03-11 16:27:16 +0100877 int ret;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100878 struct iio_context *ctx = calloc(1, sizeof(*ctx));
879 if (!ctx) {
880 ERROR("Unable to allocate memory\n");
881 return NULL;
882 }
883
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100884 ctx->ops = &local_ops;
885 ctx->name = "local";
886
Paul Cercueil1be57832014-03-11 16:27:16 +0100887 ret = foreach_in_dir(ctx, "/sys/bus/iio/devices", true, create_device);
888 if (ret < 0) {
889 char buf[1024];
Paul Cercueilc21300c2014-03-13 09:54:14 +0100890 strerror_r(errno, buf, sizeof(buf));
Paul Cercueil1be57832014-03-11 16:27:16 +0100891 ERROR("Unable to create context: %s\n", buf);
892 iio_context_destroy(ctx);
893 ctx = NULL;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100894 }
Paul Cercueilae88fde2014-03-12 11:47:10 +0100895
Paul Cercueilcd6ce842014-03-14 13:21:06 +0100896 iio_context_init_channels(ctx);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100897 return ctx;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100898}