blob: 916b186df923f04fcfc75a526bb751c4b8e728a6 [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 Cercueil92f46df2014-04-28 13:17:53 +020028#include <sys/ioctl.h>
29#include <sys/mman.h>
Paul Cercueil1be57832014-03-11 16:27:16 +010030#include <sys/types.h>
31#include <sys/stat.h>
32#include <unistd.h>
Paul Cercueil0b2ce712014-02-17 15:04:18 +010033
34#define ARRAY_SIZE(x) (sizeof(x) ? sizeof(x) / sizeof((x)[0]) : 0)
35
Paul Cercueil92f46df2014-04-28 13:17:53 +020036#define NB_BLOCKS 4
37
38#define BLOCK_ALLOC_IOCTL _IOWR('i', 0xa0, struct block_alloc_req)
39#define BLOCK_FREE_IOCTL _IO('i', 0xa1)
40#define BLOCK_QUERY_IOCTL _IOWR('i', 0xa2, struct block)
41#define BLOCK_ENQUEUE_IOCTL _IOWR('i', 0xa3, struct block)
42#define BLOCK_DEQUEUE_IOCTL _IOWR('i', 0xa4, struct block)
43
44struct block_alloc_req {
45 uint32_t type,
46 size,
47 count,
48 id;
49};
50
51struct block {
52 uint32_t id,
53 size,
54 bytes_used,
55 type,
56 flags,
57 offset;
58 uint64_t timestamp;
59};
60
Paul Cercueileaab6582014-02-21 09:35:59 +010061struct iio_device_pdata {
Paul Cercueil44010ea2014-02-21 11:47:04 +010062 FILE *f;
Paul Cercueile6ebf492014-04-02 13:57:36 +020063 unsigned int samples_count;
Paul Cercueil92f46df2014-04-28 13:17:53 +020064
65 struct block blocks[NB_BLOCKS];
66 void *addrs[NB_BLOCKS];
67 int last_dequeued;
68 bool is_high_speed;
Paul Cercueileaab6582014-02-21 09:35:59 +010069};
70
Paul Cercueil0b2ce712014-02-17 15:04:18 +010071static const char * const device_attrs_blacklist[] = {
72 "dev",
73 "uevent",
74};
75
76static const char * const modifier_names[] = {
77 [IIO_MOD_X] = "x",
78 [IIO_MOD_Y] = "y",
79 [IIO_MOD_Z] = "z",
80 [IIO_MOD_ROOT_SUM_SQUARED_X_Y] = "sqrt(x^2+y^2)",
81 [IIO_MOD_SUM_SQUARED_X_Y_Z] = "x^2+y^2+z^2",
82 [IIO_MOD_LIGHT_BOTH] = "both",
83 [IIO_MOD_LIGHT_IR] = "ir",
84 [IIO_MOD_LIGHT_CLEAR] = "clear",
85 [IIO_MOD_LIGHT_RED] = "red",
86 [IIO_MOD_LIGHT_GREEN] = "green",
87 [IIO_MOD_LIGHT_BLUE] = "blue",
88};
89
Paul Cercueil0b2ce712014-02-17 15:04:18 +010090static void local_shutdown(struct iio_context *ctx)
91{
Paul Cercueil42d12352014-05-05 16:11:58 +020092 /* Free the backend data stored in every device structure */
Paul Cercueil167d3112014-02-18 12:23:53 +010093 unsigned int i;
Paul Cercueil42d12352014-05-05 16:11:58 +020094 for (i = 0; i < ctx->nb_devices; i++)
95 free(ctx->devices[i]->pdata);
Paul Cercueil0b2ce712014-02-17 15:04:18 +010096}
97
98/** Shrinks the first nb characters of a string
99 * e.g. strcut("foobar", 4) replaces the content with "ar". */
100static void strcut(char *str, int nb)
101{
102 char *ptr = str + nb;
103 while (*ptr)
104 *str++ = *ptr++;
105 *str = 0;
106}
107
108static int set_channel_name(struct iio_channel *chn)
109{
110 if (chn->nb_attrs < 2)
111 return 0;
112
113 while (true) {
114 bool can_fix = true;
115 unsigned int i, len;
116 char *name;
Paul Cercueilb34e0222014-05-05 15:32:38 +0200117 const char *attr0 = chn->attrs[0].name;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100118 const char *ptr = strchr(attr0, '_');
119 if (!ptr)
120 break;
121
122 len = ptr - attr0;
123 for (i = 1; can_fix && i < chn->nb_attrs; i++)
Paul Cercueilb34e0222014-05-05 15:32:38 +0200124 can_fix = !strncmp(attr0, chn->attrs[i].name, len);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100125
126 if (!can_fix)
127 break;
128
129 if (chn->name) {
Paul Cercueil8c29e412014-04-07 09:46:45 +0200130 size_t nlen = strlen(chn->name) + len + 2;
131 name = malloc(nlen);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100132 if (!name)
133 return -ENOMEM;
Paul Cercueil8c29e412014-04-07 09:46:45 +0200134 snprintf(name, nlen, "%s_%.*s", chn->name, len, attr0);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100135 DEBUG("Fixing name of channel %s from %s to %s\n",
136 chn->id, chn->name, name);
Paul Cercueilbb618272014-02-20 11:35:52 +0100137 free(chn->name);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100138 } else {
139 name = malloc(len + 2);
140 if (!name)
141 return -ENOMEM;
Paul Cercueil8c29e412014-04-07 09:46:45 +0200142 snprintf(name, len + 2, "%.*s", len, attr0);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100143 DEBUG("Setting name of channel %s to %s\n",
144 chn->id, name);
145 }
146 chn->name = name;
147
148 /* Shrink the attribute name */
149 for (i = 0; i < chn->nb_attrs; i++)
Paul Cercueilb34e0222014-05-05 15:32:38 +0200150 strcut(chn->attrs[i].name, len + 1);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100151 }
152
153 if (chn->name) {
154 unsigned int i;
155 for (i = 0; i < ARRAY_SIZE(modifier_names); i++) {
156 unsigned int len;
157
158 if (!modifier_names[i])
159 continue;
160
161 len = strlen(modifier_names[i]);
162 if (!strncmp(chn->name, modifier_names[i], len)) {
163 if (chn->name[len]) {
164 /* Shrink the modifier from the extended name */
Paul Cercueilbb618272014-02-20 11:35:52 +0100165 strcut(chn->name, len + 1);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100166 } else {
Paul Cercueilbb618272014-02-20 11:35:52 +0100167 free(chn->name);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100168 chn->name = NULL;
169 }
170 chn->modifier = i;
171 DEBUG("Detected modifier for channel %s: %s\n",
172 chn->id, modifier_names[i]);
173 break;
174 }
175 }
176 }
177 return 0;
178}
179
Paul Cercueil45c575d2014-03-20 15:14:01 +0100180static ssize_t local_read(const struct iio_device *dev,
181 void *dst, size_t len, uint32_t *mask, size_t words)
Paul Cercueil44010ea2014-02-21 11:47:04 +0100182{
183 ssize_t ret;
Paul Cercueilff778232014-03-24 14:23:08 +0100184 FILE *f = dev->pdata->f;
Paul Cercueil44010ea2014-02-21 11:47:04 +0100185 if (!f)
186 return -EBADF;
Paul Cercueilff778232014-03-24 14:23:08 +0100187 if (words != dev->words)
Paul Cercueil45c575d2014-03-20 15:14:01 +0100188 return -EINVAL;
189
Paul Cercueilff778232014-03-24 14:23:08 +0100190 memcpy(mask, dev->mask, words);
Paul Cercueil44010ea2014-02-21 11:47:04 +0100191 ret = fread(dst, 1, len, f);
192 if (ret)
193 return ret;
194 else if (feof(f))
195 return 0;
196 else
197 return -EIO;
198}
199
200static ssize_t local_write(const struct iio_device *dev,
201 const void *src, size_t len)
202{
203 ssize_t ret;
204 FILE *f = dev->pdata->f;
205 if (!f)
206 return -EBADF;
207 ret = fwrite(src, 1, len, f);
208 if (ret)
209 return ret;
210 else if (feof(f))
211 return 0;
212 else
213 return -EIO;
214}
215
Paul Cercueil92f46df2014-04-28 13:17:53 +0200216static ssize_t local_get_buffer(const struct iio_device *dev,
217 void **addr_ptr, uint32_t *mask, size_t words)
218{
219 struct block block;
220 struct iio_device_pdata *pdata = dev->pdata;
221 FILE *f = pdata->f;
222 ssize_t ret;
223
224 if (!pdata->is_high_speed)
225 return -ENOSYS;
226 if (!f)
227 return -EBADF;
228 if (words != dev->words || !addr_ptr)
229 return -EINVAL;
230
231 memcpy(mask, dev->mask, words);
232
233 if (pdata->last_dequeued >= 0) {
234 ret = (ssize_t) ioctl(fileno(f), BLOCK_ENQUEUE_IOCTL,
235 &pdata->blocks[pdata->last_dequeued]);
236 if (ret) {
237 ret = (ssize_t) -errno;
238 ERROR("Unable to enqueue block: %s\n", strerror(errno));
239 return ret;
240 }
241 }
242
243 ret = (ssize_t) ioctl(fileno(f), BLOCK_DEQUEUE_IOCTL, &block);
244 if (ret) {
245 ret = (ssize_t) -errno;
246 ERROR("Unable to dequeue block: %s\n", strerror(errno));
247 return ret;
248 }
249
250 pdata->last_dequeued = block.id;
251 *addr_ptr = pdata->addrs[block.id];
252 return (ssize_t) block.bytes_used;
253}
254
Paul Cercueil167d3112014-02-18 12:23:53 +0100255static ssize_t local_read_dev_attr(const struct iio_device *dev,
Paul Cercueil50c762a2014-04-14 15:55:43 +0200256 const char *attr, char *dst, size_t len, bool is_debug)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100257{
Paul Cercueil3e898302014-02-17 16:17:11 +0100258 FILE *f;
259 char buf[1024];
260 ssize_t ret;
261
Paul Cercueil50c762a2014-04-14 15:55:43 +0200262 if (is_debug)
263 snprintf(buf, sizeof(buf), "/sys/kernel/debug/iio/%s/%s",
264 dev->id, attr);
265 else
266 snprintf(buf, sizeof(buf), "/sys/bus/iio/devices/%s/%s",
267 dev->id, attr);
Paul Cercueil3e898302014-02-17 16:17:11 +0100268 f = fopen(buf, "r");
269 if (!f)
270 return -errno;
271
272 ret = fread(dst, 1, len, f);
273 if (ret > 0)
274 dst[ret - 1] = '\0';
Paul Cercueil96dfedd2014-03-11 09:53:09 +0100275 fflush(f);
276 if (ferror(f))
277 ret = -EBUSY;
Paul Cercueil3e898302014-02-17 16:17:11 +0100278 fclose(f);
Paul Cercueil6e7f79e2014-04-04 12:27:24 +0200279 return ret ? ret : -EIO;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100280}
281
Paul Cercueil167d3112014-02-18 12:23:53 +0100282static ssize_t local_write_dev_attr(const struct iio_device *dev,
Paul Cercueilcecda352014-05-06 18:14:29 +0200283 const char *attr, const char *src, size_t len, bool is_debug)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100284{
Paul Cercueil3e898302014-02-17 16:17:11 +0100285 FILE *f;
286 char buf[1024];
287 ssize_t ret;
Paul Cercueil3e898302014-02-17 16:17:11 +0100288
Paul Cercueil50c762a2014-04-14 15:55:43 +0200289 if (is_debug)
290 snprintf(buf, sizeof(buf), "/sys/kernel/debug/iio/%s/%s",
291 dev->id, attr);
292 else
293 snprintf(buf, sizeof(buf), "/sys/bus/iio/devices/%s/%s",
294 dev->id, attr);
Paul Cercueilc35d1ac2014-03-10 12:33:32 +0100295 f = fopen(buf, "r+");
Paul Cercueil3e898302014-02-17 16:17:11 +0100296 if (!f)
297 return -errno;
298
299 ret = fwrite(src, 1, len, f);
Paul Cercueil96dfedd2014-03-11 09:53:09 +0100300 fflush(f);
301 if (ferror(f))
302 ret = -EBUSY;
Paul Cercueil3e898302014-02-17 16:17:11 +0100303 fclose(f);
Paul Cercueil6e7f79e2014-04-04 12:27:24 +0200304 return ret ? ret : -EIO;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100305}
306
Paul Cercueil167d3112014-02-18 12:23:53 +0100307static const char * get_filename(const struct iio_channel *chn,
308 const char *attr)
309{
Paul Cercueil167d3112014-02-18 12:23:53 +0100310 unsigned int i;
Paul Cercueil42d12352014-05-05 16:11:58 +0200311 for (i = 0; i < chn->nb_attrs; i++)
312 if (!strcmp(attr, chn->attrs[i].name))
313 return chn->attrs[i].filename;
Paul Cercueil167d3112014-02-18 12:23:53 +0100314 return attr;
315}
316
317static ssize_t local_read_chn_attr(const struct iio_channel *chn,
318 const char *attr, char *dst, size_t len)
319{
320 attr = get_filename(chn, attr);
Paul Cercueil50c762a2014-04-14 15:55:43 +0200321 return local_read_dev_attr(chn->dev, attr, dst, len, false);
Paul Cercueil167d3112014-02-18 12:23:53 +0100322}
323
324static ssize_t local_write_chn_attr(const struct iio_channel *chn,
Paul Cercueilcecda352014-05-06 18:14:29 +0200325 const char *attr, const char *src, size_t len)
Paul Cercueil167d3112014-02-18 12:23:53 +0100326{
327 attr = get_filename(chn, attr);
Paul Cercueilcecda352014-05-06 18:14:29 +0200328 return local_write_dev_attr(chn->dev, attr, src, len, false);
Paul Cercueil167d3112014-02-18 12:23:53 +0100329}
330
Paul Cercueilff778232014-03-24 14:23:08 +0100331static int channel_write_state(const struct iio_channel *chn)
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100332{
Paul Cercueilff778232014-03-24 14:23:08 +0100333 char *en = iio_channel_is_enabled(chn) ? "1" : "0";
Paul Cercueilcecda352014-05-06 18:14:29 +0200334 ssize_t ret = local_write_chn_attr(chn, "en", en, 2);
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100335 if (ret < 0)
336 return (int) ret;
337 else
338 return 0;
339}
340
Paul Cercueil92f46df2014-04-28 13:17:53 +0200341static int enable_high_speed(const struct iio_device *dev)
342{
343 struct block_alloc_req req;
344 struct iio_device_pdata *pdata = dev->pdata;
345 unsigned int i;
346 int ret, fd = fileno(pdata->f);
347
348 req.type = 0;
349 req.size = pdata->samples_count *
350 iio_device_get_sample_size_mask(dev, dev->mask, dev->words);
351 req.count = NB_BLOCKS;
352
353 ret = ioctl(fd, BLOCK_ALLOC_IOCTL, &req);
354 if (ret < 0)
355 return -errno;
356
357 /* mmap all the blocks */
358 for (i = 0; i < NB_BLOCKS; i++) {
359 pdata->blocks[i].id = i;
360 ret = ioctl(fd, BLOCK_QUERY_IOCTL, &pdata->blocks[i]);
361 if (ret) {
362 ret = -errno;
363 goto err_munmap;
364 }
365
366 ret = ioctl(fd, BLOCK_ENQUEUE_IOCTL, &pdata->blocks[i]);
367 if (ret) {
368 ret = -errno;
369 goto err_munmap;
370 }
371
372 pdata->addrs[i] = mmap(0, pdata->blocks[i].size, PROT_READ,
373 MAP_SHARED, fd, pdata->blocks[i].offset);
374 if (pdata->addrs[i] == MAP_FAILED) {
375 ret = -errno;
376 goto err_munmap;
377 }
378 }
379
380 return 0;
381
382err_munmap:
383 for (; i > 0; i--)
384 munmap(pdata->addrs[i - 1], pdata->blocks[i - 1].size);
385 ioctl(fd, BLOCK_FREE_IOCTL, 0);
386 return ret;
387}
388
Paul Cercueile6ebf492014-04-02 13:57:36 +0200389static int local_open(const struct iio_device *dev,
390 size_t samples_count, uint32_t *mask, size_t nb)
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100391{
392 unsigned int i;
393 int ret;
394 char buf[1024];
395 struct iio_device_pdata *pdata = dev->pdata;
396 if (pdata->f)
397 return -EBUSY;
398
Paul Cercueilff778232014-03-24 14:23:08 +0100399 if (nb != dev->words)
Paul Cercueile1311222014-03-12 15:46:16 +0100400 return -EINVAL;
401
Paul Cercueilcecda352014-05-06 18:14:29 +0200402 ret = local_write_dev_attr(dev, "buffer/enable", "0", 2, false);
Paul Cercueilf0aeae32014-04-02 13:52:26 +0200403 if (ret < 0)
Paul Cercueil48e39452014-03-14 09:37:53 +0100404 return ret;
405
Paul Cercueile6ebf492014-04-02 13:57:36 +0200406 if (samples_count) {
Paul Cercueil8c29e412014-04-07 09:46:45 +0200407 snprintf(buf, sizeof(buf),
408 "%lu", (unsigned long) samples_count);
Paul Cercueilcecda352014-05-06 18:14:29 +0200409 ret = local_write_dev_attr(dev, "buffer/length",
410 buf, strlen(buf) + 1, false);
Paul Cercueile6ebf492014-04-02 13:57:36 +0200411 if (ret < 0)
412 return ret;
413 }
414
Paul Cercueil8c29e412014-04-07 09:46:45 +0200415 snprintf(buf, sizeof(buf), "/dev/%s", dev->id);
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100416 pdata->f = fopen(buf, "r+");
417 if (!pdata->f)
418 return -errno;
419
Paul Cercueilff778232014-03-24 14:23:08 +0100420 memcpy(dev->mask, mask, nb);
421
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100422 /* Enable channels */
423 for (i = 0; i < dev->nb_channels; i++) {
424 struct iio_channel *chn = dev->channels[i];
Paul Cercueile1311222014-03-12 15:46:16 +0100425 if (chn->index >= 0) {
Paul Cercueilff778232014-03-24 14:23:08 +0100426 ret = channel_write_state(chn);
Paul Cercueilf0aeae32014-04-02 13:52:26 +0200427 if (ret < 0)
Paul Cercueile1311222014-03-12 15:46:16 +0100428 goto err_close;
429 }
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100430 }
431
Paul Cercueile6ebf492014-04-02 13:57:36 +0200432 /* If opened with samples_count == 0, we probably want DDS mode;
433 * then the buffer will only be enabled when closing the device. */
434 if (samples_count > 0)
Paul Cercueilcecda352014-05-06 18:14:29 +0200435 ret = local_write_dev_attr(dev, "buffer/enable", "1", 2, false);
Paul Cercueilf0aeae32014-04-02 13:52:26 +0200436 if (ret < 0)
Paul Cercueil45c575d2014-03-20 15:14:01 +0100437 goto err_close;
438
Paul Cercueil6411bce2014-05-02 14:20:51 +0200439 pdata->samples_count = samples_count;
440 pdata->is_high_speed = !!samples_count && !enable_high_speed(dev);
Paul Cercueil45c575d2014-03-20 15:14:01 +0100441 return 0;
Paul Cercueile1311222014-03-12 15:46:16 +0100442err_close:
443 fclose(pdata->f);
444 pdata->f = NULL;
445 return ret;
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100446}
447
448static int local_close(const struct iio_device *dev)
449{
450 struct iio_device_pdata *pdata = dev->pdata;
Paul Cercueilfc8ec4a2014-03-17 16:42:22 +0100451 int ret;
452
453 if (!pdata->f)
454 return -EBADF;
455
Paul Cercueil92f46df2014-04-28 13:17:53 +0200456 if (pdata->is_high_speed) {
457 unsigned int i;
458 for (i = 0; i < NB_BLOCKS; i++)
459 munmap(pdata->addrs[i], pdata->blocks[i].size);
460 ioctl(fileno(pdata->f), BLOCK_FREE_IOCTL, 0);
461 }
462
Paul Cercueilfc8ec4a2014-03-17 16:42:22 +0100463 ret = fclose(pdata->f);
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100464 if (ret)
465 return ret;
466
467 pdata->f = NULL;
Paul Cercueile6ebf492014-04-02 13:57:36 +0200468 ret = local_write_dev_attr(dev, "buffer/enable",
Paul Cercueilcecda352014-05-06 18:14:29 +0200469 pdata->samples_count ? "0" : "1", 2, false);
Paul Cercueilf0aeae32014-04-02 13:52:26 +0200470 if (ret < 0)
Paul Cercueil8a7975e2014-03-12 12:53:43 +0100471 return ret;
472 else
473 return 0;
474}
475
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100476static int local_get_trigger(const struct iio_device *dev,
477 const struct iio_device **trigger)
478{
479 char buf[1024];
480 unsigned int i;
481 ssize_t nb = local_read_dev_attr(dev, "trigger/current_trigger",
Paul Cercueil50c762a2014-04-14 15:55:43 +0200482 buf, sizeof(buf), false);
Paul Cercueild09322a2014-04-02 13:50:18 +0200483 if (nb < 0) {
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100484 *trigger = NULL;
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100485 return (int) nb;
Paul Cercueild09322a2014-04-02 13:50:18 +0200486 }
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100487
488 nb = dev->ctx->nb_devices;
Paul Cercueil4e2f6652014-04-04 12:41:45 +0200489 for (i = 0; i < (size_t) nb; i++) {
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100490 const struct iio_device *cur = dev->ctx->devices[i];
491 if (cur->name && !strcmp(cur->name, buf)) {
492 *trigger = cur;
493 return 0;
494 }
495 }
496 return -ENXIO;
497}
498
499static int local_set_trigger(const struct iio_device *dev,
500 const struct iio_device *trigger)
501{
502 ssize_t nb;
503 const char *value = trigger ? trigger->name : "";
Paul Cercueilcecda352014-05-06 18:14:29 +0200504 nb = local_write_dev_attr(dev, "trigger/current_trigger",
505 value, strlen(value) + 1, false);
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100506 if (nb < 0)
507 return (int) nb;
508 else
509 return 0;
510}
511
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100512static bool is_channel(const char *attr)
513{
514 unsigned int i;
515 char *ptr = NULL;
Paul Cercueilec6857d2014-03-11 17:23:49 +0100516 if (!strncmp(attr, "in_timestamp_", sizeof("in_timestamp_") - 1))
517 return true;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100518 if (!strncmp(attr, "in_", 3))
519 ptr = strchr(attr + 3, '_');
520 else if (!strncmp(attr, "out_", 4))
521 ptr = strchr(attr + 4, '_');
522 if (!ptr)
523 return false;
524 if (*(ptr - 1) >= '0' && *(ptr - 1) <= '9')
525 return true;
526 for (i = 0; i < ARRAY_SIZE(modifier_names); i++)
527 if (modifier_names[i] && !strncmp(ptr + 1, modifier_names[i],
528 strlen(modifier_names[i])))
529 return true;
530 return false;
531}
532
533static char * get_channel_id(const char *attr)
534{
535 char *res, *ptr;
536 unsigned int i;
537
538 attr = strchr(attr, '_') + 1;
539 ptr = strchr(attr, '_');
540 for (i = 0; i < ARRAY_SIZE(modifier_names); i++) {
541 if (modifier_names[i] && !strncmp(ptr + 1, modifier_names[i],
542 strlen(modifier_names[i]))) {
543 ptr = strchr(ptr + 1, '_');
544 break;
545 }
546 }
547
548 res = malloc(ptr - attr + 1);
549 if (!res)
550 return NULL;
551
552 memcpy(res, attr, ptr - attr);
553 res[ptr - attr] = 0;
554 return res;
555}
556
557static char * get_short_attr_name(const char *attr)
558{
559 char *ptr = strchr(attr, '_') + 1;
560 ptr = strchr(ptr, '_') + 1;
561 return strdup(ptr);
562}
563
564static int read_device_name(struct iio_device *dev)
565{
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100566 char buf[1024];
Paul Cercueil8c29e412014-04-07 09:46:45 +0200567 ssize_t ret = iio_device_attr_read(dev, "name", buf, sizeof(buf));
Paul Cercueil30430c72014-02-17 16:52:37 +0100568 if (ret < 0)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100569 return ret;
Paul Cercueil30430c72014-02-17 16:52:37 +0100570 else if (ret == 0)
571 return -EIO;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100572
Paul Cercueil30430c72014-02-17 16:52:37 +0100573 dev->name = strdup(buf);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100574 if (!dev->name)
575 return -ENOMEM;
Paul Cercueil30430c72014-02-17 16:52:37 +0100576 else
577 return 0;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100578}
579
580static int add_attr_to_device(struct iio_device *dev, const char *attr)
581{
Paul Cercueilbb618272014-02-20 11:35:52 +0100582 char **attrs, *name;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100583 unsigned int i;
584
585 for (i = 0; i < ARRAY_SIZE(device_attrs_blacklist); i++)
586 if (!strcmp(device_attrs_blacklist[i], attr))
587 return 0;
588
589 if (!strcmp(attr, "name"))
590 return read_device_name(dev);
591
592 name = strdup(attr);
593 if (!name)
594 return -ENOMEM;
595
596 attrs = realloc(dev->attrs, (1 + dev->nb_attrs) * sizeof(char *));
597 if (!attrs) {
598 free(name);
599 return -ENOMEM;
600 }
601
602 attrs[dev->nb_attrs++] = name;
603 dev->attrs = attrs;
604 DEBUG("Added attr \'%s\' to device \'%s\'\n", attr, dev->id);
605 return 0;
606}
607
Paul Cercueile3fbd952014-03-11 16:58:33 +0100608static int add_attr_to_channel(struct iio_channel *chn,
609 const char *attr, const char *path)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100610{
Paul Cercueilb34e0222014-05-05 15:32:38 +0200611 struct iio_channel_attr *attrs;
612 char *fn, *name = get_short_attr_name(attr);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100613 if (!name)
614 return -ENOMEM;
615
Paul Cercueile3fbd952014-03-11 16:58:33 +0100616 fn = strdup(path);
Paul Cercueil167d3112014-02-18 12:23:53 +0100617 if (!fn)
618 goto err_free_name;
619
Paul Cercueilb34e0222014-05-05 15:32:38 +0200620 attrs = realloc(chn->attrs, (1 + chn->nb_attrs) *
621 sizeof(struct iio_channel_attr));
Paul Cercueil167d3112014-02-18 12:23:53 +0100622 if (!attrs)
623 goto err_free_fn;
624
Paul Cercueil42d12352014-05-05 16:11:58 +0200625 attrs[chn->nb_attrs].filename = fn;
Paul Cercueilb34e0222014-05-05 15:32:38 +0200626 attrs[chn->nb_attrs++].name = name;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100627 chn->attrs = attrs;
628 DEBUG("Added attr \'%s\' to channel \'%s\'\n", name, chn->id);
629 return 0;
Paul Cercueil167d3112014-02-18 12:23:53 +0100630
Paul Cercueil167d3112014-02-18 12:23:53 +0100631err_free_fn:
632 free(fn);
633err_free_name:
634 free(name);
635 return -ENOMEM;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100636}
637
638static int add_channel_to_device(struct iio_device *dev,
639 struct iio_channel *chn)
640{
641 struct iio_channel **channels = realloc(dev->channels,
642 (dev->nb_channels + 1) * sizeof(struct iio_channel *));
643 if (!channels)
644 return -ENOMEM;
645
646 channels[dev->nb_channels++] = chn;
647 dev->channels = channels;
648 DEBUG("Added channel \'%s\' to device \'%s\'\n", chn->id, dev->id);
649 return 0;
650}
651
652static int add_device_to_context(struct iio_context *ctx,
653 struct iio_device *dev)
654{
655 struct iio_device **devices = realloc(ctx->devices,
656 (ctx->nb_devices + 1) * sizeof(struct iio_device *));
657 if (!devices)
658 return -ENOMEM;
659
660 devices[ctx->nb_devices++] = dev;
661 ctx->devices = devices;
662 DEBUG("Added device \'%s\' to context \'%s\'\n", dev->id, ctx->name);
663 return 0;
664}
665
666static bool is_global_attr(struct iio_channel *chn, const char *attr)
667{
668 unsigned int i, len;
669 char *ptr;
670
Paul Cercueil35a01312014-02-20 10:56:57 +0100671 if (!chn->is_output && !strncmp(attr, "in_", 3))
672 attr += 3;
673 else if (chn->is_output && !strncmp(attr, "out_", 4))
674 attr += 4;
675 else
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100676 return false;
677
678 ptr = strchr(attr, '_');
679 if (!ptr)
680 return false;
681
682 len = ptr - attr;
683
684 if (strncmp(chn->id, attr, len))
685 return false;
686
687 DEBUG("Found match: %s and %s\n", chn->id, attr);
688 if (chn->id[len] >= '0' && chn->id[len] <= '9')
689 return true;
690 else if (chn->id[len] != '_')
691 return false;
692
693 for (i = 0; i < ARRAY_SIZE(modifier_names); i++)
694 if (modifier_names[i] &&
695 !strncmp(chn->id + len + 1, modifier_names[i],
696 strlen(modifier_names[i])))
697 return true;
698
699 return false;
700}
701
702static int detect_and_move_global_attrs(struct iio_device *dev)
703{
704 unsigned int i;
Paul Cercueilbb618272014-02-20 11:35:52 +0100705 char **ptr = dev->attrs;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100706
707 for (i = 0; i < dev->nb_attrs; i++) {
708 unsigned int j;
709 bool global = false;
710 const char *attr = dev->attrs[i];
711
712 for (j = 0; j < dev->nb_channels; j++) {
713 struct iio_channel *chn = dev->channels[j];
714 if (is_global_attr(chn, attr)) {
715 int ret;
716 global = true;
Paul Cercueile3fbd952014-03-11 16:58:33 +0100717 ret = add_attr_to_channel(chn, attr, attr);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100718 if (ret)
719 return ret;
720 }
721 }
722
723 if (global) {
Paul Cercueilbb618272014-02-20 11:35:52 +0100724 free(dev->attrs[i]);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100725 dev->attrs[i] = NULL;
726 }
727 }
728
729 for (i = 0; i < dev->nb_attrs; i++) {
730 if (dev->attrs[i])
731 *ptr++ = dev->attrs[i];
732 }
733
734 dev->nb_attrs = ptr - dev->attrs;
735 return 0;
736}
737
738static struct iio_channel *create_channel(struct iio_device *dev,
Paul Cercueile3fbd952014-03-11 16:58:33 +0100739 char *id, const char *attr, const char *path)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100740{
741 struct iio_channel *chn = calloc(1, sizeof(*chn));
742 if (!chn)
743 return NULL;
744
Paul Cercueileaab6582014-02-21 09:35:59 +0100745 if (!strncmp(attr, "out_", 4))
Paul Cercueil35a01312014-02-20 10:56:57 +0100746 chn->is_output = true;
Paul Cercueileaab6582014-02-21 09:35:59 +0100747 else if (strncmp(attr, "in_", 3))
Paul Cercueil42d12352014-05-05 16:11:58 +0200748 goto err_free_chn;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100749
750 chn->dev = dev;
751 chn->id = id;
752 chn->modifier = IIO_NO_MOD;
753
Paul Cercueile3fbd952014-03-11 16:58:33 +0100754 if (!add_attr_to_channel(chn, attr, path))
Paul Cercueileaab6582014-02-21 09:35:59 +0100755 return chn;
756
Paul Cercueileaab6582014-02-21 09:35:59 +0100757err_free_chn:
758 free(chn);
759 return NULL;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100760}
761
Paul Cercueile3fbd952014-03-11 16:58:33 +0100762static int add_attr_or_channel_helper(struct iio_device *dev,
Paul Cercueila91358e2014-04-24 16:40:19 +0200763 const char *path, bool dir_is_scan_elements)
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100764{
Paul Cercueil1be57832014-03-11 16:27:16 +0100765 int ret;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100766 unsigned int i;
Paul Cercueil1be57832014-03-11 16:27:16 +0100767 struct iio_channel *chn;
Paul Cercueile3fbd952014-03-11 16:58:33 +0100768 char buf[1024], *channel_id;
Paul Cercueil1be57832014-03-11 16:27:16 +0100769 const char *name = strrchr(path, '/') + 1;
Paul Cercueila91358e2014-04-24 16:40:19 +0200770
771 if (dir_is_scan_elements) {
772 snprintf(buf, sizeof(buf), "scan_elements/%s", name);
Paul Cercueile3fbd952014-03-11 16:58:33 +0100773 path = buf;
Paul Cercueila91358e2014-04-24 16:40:19 +0200774 } else {
775 path = name;
Paul Cercueile3fbd952014-03-11 16:58:33 +0100776 }
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100777
Paul Cercueil1be57832014-03-11 16:27:16 +0100778 if (!is_channel(name))
779 return add_attr_to_device(dev, name);
780
781 channel_id = get_channel_id(name);
782 if (!channel_id)
783 return -ENOMEM;
784
785 for (i = 0; i < dev->nb_channels; i++) {
786 chn = dev->channels[i];
Paul Cercueil5248e792014-03-31 14:39:10 +0200787 if (!strcmp(chn->id, channel_id)
788 && chn->is_output == (name[0] == 'o')) {
Paul Cercueil1be57832014-03-11 16:27:16 +0100789 free(channel_id);
Paul Cercueila91358e2014-04-24 16:40:19 +0200790 ret = add_attr_to_channel(chn, name, path);
791 chn->is_scan_element = dir_is_scan_elements && !ret;
792 return ret;
Paul Cercueil1be57832014-03-11 16:27:16 +0100793 }
794 }
795
Paul Cercueile3fbd952014-03-11 16:58:33 +0100796 chn = create_channel(dev, channel_id, name, path);
Paul Cercueil1be57832014-03-11 16:27:16 +0100797 if (!chn) {
798 free(channel_id);
799 return -ENXIO;
800 }
801 ret = add_channel_to_device(dev, chn);
802 if (ret)
803 free_channel(chn);
Paul Cercueila91358e2014-04-24 16:40:19 +0200804 else
805 chn->is_scan_element = dir_is_scan_elements;
Paul Cercueil1be57832014-03-11 16:27:16 +0100806 return ret;
807}
808
Paul Cercueile3fbd952014-03-11 16:58:33 +0100809static int add_attr_or_channel(void *d, const char *path)
810{
Paul Cercueila91358e2014-04-24 16:40:19 +0200811 return add_attr_or_channel_helper((struct iio_device *) d, path, false);
Paul Cercueile3fbd952014-03-11 16:58:33 +0100812}
813
814static int add_scan_element(void *d, const char *path)
815{
Paul Cercueila91358e2014-04-24 16:40:19 +0200816 return add_attr_or_channel_helper((struct iio_device *) d, path, true);
Paul Cercueile3fbd952014-03-11 16:58:33 +0100817}
818
Paul Cercueil1be57832014-03-11 16:27:16 +0100819static int foreach_in_dir(void *d, const char *path, bool is_dir,
820 int (*callback)(void *, const char *))
821{
822 long name_max;
823 struct dirent *entry, *result;
824 DIR *dir = opendir(path);
825 if (!dir)
826 return -errno;
827
828 name_max = pathconf(path, _PC_NAME_MAX);
829 if (name_max == -1)
830 name_max = 255;
831 entry = malloc(offsetof(struct dirent, d_name) + name_max + 1);
832 if (!entry) {
833 closedir(dir);
834 return -ENOMEM;
835 }
836
837 while (true) {
838 struct stat st;
839 char buf[1024];
840 int ret = readdir_r(dir, entry, &result);
841 if (ret) {
842 strerror_r(ret, buf, sizeof(buf));
843 ERROR("Unable to open directory %s: %s\n", path, buf);
844 free(entry);
845 closedir(dir);
846 return ret;
847 }
848 if (!result)
849 break;
850
851 snprintf(buf, sizeof(buf), "%s/%s", path, entry->d_name);
852 if (stat(buf, &st) < 0) {
853 ret = -errno;
Paul Cercueilc21300c2014-03-13 09:54:14 +0100854 strerror_r(errno, buf, sizeof(buf));
Paul Cercueil1be57832014-03-11 16:27:16 +0100855 ERROR("Unable to stat file: %s\n", buf);
856 free(entry);
857 closedir(dir);
858 return ret;
859 }
860
861 if (is_dir && S_ISDIR(st.st_mode) && entry->d_name[0] != '.')
862 ret = callback(d, buf);
863 else if (!is_dir && S_ISREG(st.st_mode))
864 ret = callback(d, buf);
865 else
866 continue;
867
868 if (ret < 0) {
869 free(entry);
870 closedir(dir);
871 return ret;
872 }
873 }
874
875 free(entry);
876 closedir(dir);
877 return 0;
878}
879
Paul Cercueile3fbd952014-03-11 16:58:33 +0100880static int add_scan_elements(struct iio_device *dev, const char *devpath)
881{
882 struct stat st;
883 char buf[1024];
884 snprintf(buf, sizeof(buf), "%s/scan_elements", devpath);
885
886 if (!stat(buf, &st) && S_ISDIR(st.st_mode)) {
887 int ret = foreach_in_dir(dev, buf, false, add_scan_element);
888 if (ret < 0)
889 return ret;
890 }
891
892 return 0;
893}
894
Paul Cercueil1be57832014-03-11 16:27:16 +0100895static int create_device(void *d, const char *path)
896{
Paul Cercueilff778232014-03-24 14:23:08 +0100897 uint32_t *mask = NULL;
Paul Cercueil1be57832014-03-11 16:27:16 +0100898 unsigned int i;
899 int ret;
900 struct iio_context *ctx = d;
901 struct iio_device *dev = calloc(1, sizeof(*dev));
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100902 if (!dev)
Paul Cercueil1be57832014-03-11 16:27:16 +0100903 return -ENOMEM;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100904
Paul Cercueileaab6582014-02-21 09:35:59 +0100905 dev->pdata = calloc(1, sizeof(*dev->pdata));
906 if (!dev->pdata) {
907 free(dev);
Paul Cercueil1be57832014-03-11 16:27:16 +0100908 return -ENOMEM;
Paul Cercueileaab6582014-02-21 09:35:59 +0100909 }
910
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100911 dev->ctx = ctx;
Paul Cercueil1be57832014-03-11 16:27:16 +0100912 dev->id = strdup(strrchr(path, '/') + 1);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100913 if (!dev->id) {
Paul Cercueileaab6582014-02-21 09:35:59 +0100914 free(dev->pdata);
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100915 free(dev);
Paul Cercueil1be57832014-03-11 16:27:16 +0100916 return -ENOMEM;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100917 }
918
Paul Cercueil1be57832014-03-11 16:27:16 +0100919 ret = foreach_in_dir(dev, path, false, add_attr_or_channel);
920 if (ret < 0) {
921 free_device(dev);
922 return ret;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100923 }
924
Paul Cercueil54913ac2014-03-31 14:48:19 +0200925 for (i = 0; i < dev->nb_channels; i++)
926 set_channel_name(dev->channels[i]);
927
928 ret = detect_and_move_global_attrs(dev);
Paul Cercueile3fbd952014-03-11 16:58:33 +0100929 if (ret < 0) {
930 free_device(dev);
931 return ret;
932 }
933
Paul Cercueil54913ac2014-03-31 14:48:19 +0200934 ret = add_scan_elements(dev, path);
Paul Cercueil1be57832014-03-11 16:27:16 +0100935 if (ret < 0) {
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100936 free_device(dev);
Paul Cercueil1be57832014-03-11 16:27:16 +0100937 return ret;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100938 }
939
Paul Cercueilff778232014-03-24 14:23:08 +0100940 dev->words = (dev->nb_channels + 31) / 32;
941 if (dev->words) {
942 mask = calloc(dev->words, sizeof(*mask));
Paul Cercueil45c575d2014-03-20 15:14:01 +0100943 if (!mask) {
944 free_device(dev);
945 return ret;
946 }
Paul Cercueil45c575d2014-03-20 15:14:01 +0100947 }
948
Paul Cercueilff778232014-03-24 14:23:08 +0100949 dev->mask = mask;
Paul Cercueil45c575d2014-03-20 15:14:01 +0100950
Paul Cercueil1be57832014-03-11 16:27:16 +0100951 ret = add_device_to_context(ctx, dev);
952 if (ret < 0)
953 free_device(dev);
954 return ret;
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100955}
956
Paul Cercueilddaa26a2014-04-14 17:32:18 +0200957static int add_debug_attr(void *d, const char *path)
958{
959 struct iio_device *dev = d;
960 const char *attr = strrchr(path, '/') + 1;
961 char **attrs, *name = strdup(attr);
962 if (!name)
963 return -ENOMEM;
964
965 attrs = realloc(dev->debug_attrs,
966 (1 + dev->nb_debug_attrs) * sizeof(char *));
967 if (!attrs) {
968 free(name);
969 return -ENOMEM;
970 }
971
972 attrs[dev->nb_debug_attrs++] = name;
973 dev->debug_attrs = attrs;
974 DEBUG("Added debug attr \'%s\' to device \'%s\'\n", name, dev->id);
975 return 0;
976}
977
978static int add_debug(void *d, const char *path)
979{
980 struct iio_context *ctx = d;
981 const char *name = strrchr(path, '/') + 1;
982 struct iio_device *dev = iio_context_find_device(ctx, name);
983 if (!dev)
984 return -ENODEV;
985 else
986 return foreach_in_dir(dev, path, false, add_debug_attr);
987}
988
Paul Cercueil0b2ce712014-02-17 15:04:18 +0100989static struct iio_backend_ops local_ops = {
Paul Cercueil44010ea2014-02-21 11:47:04 +0100990 .open = local_open,
991 .close = local_close,
992 .read = local_read,
993 .write = local_write,
Paul Cercueil92f46df2014-04-28 13:17:53 +0200994 .get_buffer = local_get_buffer,
Paul Cercueil167d3112014-02-18 12:23:53 +0100995 .read_device_attr = local_read_dev_attr,
996 .write_device_attr = local_write_dev_attr,
997 .read_channel_attr = local_read_chn_attr,
998 .write_channel_attr = local_write_chn_attr,
Paul Cercueil096e9aa2014-03-10 12:48:51 +0100999 .get_trigger = local_get_trigger,
1000 .set_trigger = local_set_trigger,
Paul Cercueil0b2ce712014-02-17 15:04:18 +01001001 .shutdown = local_shutdown,
1002};
1003
1004struct iio_context * iio_create_local_context(void)
1005{
Paul Cercueil1be57832014-03-11 16:27:16 +01001006 int ret;
Paul Cercueil0b2ce712014-02-17 15:04:18 +01001007 struct iio_context *ctx = calloc(1, sizeof(*ctx));
1008 if (!ctx) {
1009 ERROR("Unable to allocate memory\n");
1010 return NULL;
1011 }
1012
Paul Cercueil0b2ce712014-02-17 15:04:18 +01001013 ctx->ops = &local_ops;
1014 ctx->name = "local";
1015
Paul Cercueil1be57832014-03-11 16:27:16 +01001016 ret = foreach_in_dir(ctx, "/sys/bus/iio/devices", true, create_device);
1017 if (ret < 0) {
1018 char buf[1024];
Paul Cercueil4c6729d2014-04-04 17:24:41 +02001019 strerror_r(-ret, buf, sizeof(buf));
Paul Cercueil1be57832014-03-11 16:27:16 +01001020 ERROR("Unable to create context: %s\n", buf);
1021 iio_context_destroy(ctx);
Paul Cercueil4c6729d2014-04-04 17:24:41 +02001022 return NULL;
1023 }
1024
Paul Cercueilddaa26a2014-04-14 17:32:18 +02001025 foreach_in_dir(ctx, "/sys/kernel/debug/iio", true, add_debug);
1026
Paul Cercueil4c6729d2014-04-04 17:24:41 +02001027 ret = iio_context_init(ctx);
1028 if (ret < 0) {
1029 char buf[1024];
1030 strerror_r(-ret, buf, sizeof(buf));
1031 ERROR("Unable to initialize context: %s\n", buf);
1032 iio_context_destroy(ctx);
Paul Cercueil1be57832014-03-11 16:27:16 +01001033 ctx = NULL;
Paul Cercueil0b2ce712014-02-17 15:04:18 +01001034 }
Paul Cercueilae88fde2014-03-12 11:47:10 +01001035
Paul Cercueil0b2ce712014-02-17 15:04:18 +01001036 return ctx;
Paul Cercueil0b2ce712014-02-17 15:04:18 +01001037}